diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index a1ed38a6a66506f77cef757781990df5a2fb9784..3096f600ecc0e7063df19e31fa4a49d41a9223f9 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -93,8 +93,8 @@ </plurals> <string name="settings_title">Settings</string> <string name="bluetooth_setting_title">BLUETOOTH</string> - <string name="bluetooth_setting">Turn on Bluetooth</string> - <string name="bluetooth_setting_enabled">While signed in</string> + <string name="bluetooth_setting">Connect via Bluetooth</string> + <string name="bluetooth_setting_enabled">Whenever contacts are nearby</string> <string name="bluetooth_setting_disabled">Only when adding contacts</string> <string name="notification_settings_title">NOTIFICATIONS</string> <string name="notify_private_messages_setting">Show alerts for private messages</string> diff --git a/briar-android/src/org/briarproject/android/PasswordActivity.java b/briar-android/src/org/briarproject/android/PasswordActivity.java index 26ac3dee0938710792bb9389d3081c3cabc0c4c0..727985f1b1a989aed8eaa1fa00857b962334dca2 100644 --- a/briar-android/src/org/briarproject/android/PasswordActivity.java +++ b/briar-android/src/org/briarproject/android/PasswordActivity.java @@ -133,10 +133,7 @@ public class PasswordActivity extends RoboActivity { continueButton.setVisibility(GONE); progress.setVisibility(VISIBLE); // Decrypt the database key in a background thread - int length = e.length(); - final char[] password = new char[length]; - e.getChars(0, length, password, 0); - e.delete(0, length); + final String password = e.toString(); cryptoExecutor.execute(new Runnable() { public void run() { byte[] key = crypto.decryptWithPassword(encrypted, password); diff --git a/briar-android/src/org/briarproject/android/SetupActivity.java b/briar-android/src/org/briarproject/android/SetupActivity.java index a71442eeb0cbe4fc841329863542656bd0222625..109ce0cee8077441187b78a7a22da2f9934fe8dd 100644 --- a/briar-android/src/org/briarproject/android/SetupActivity.java +++ b/briar-android/src/org/briarproject/android/SetupActivity.java @@ -19,7 +19,6 @@ import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP; import static org.briarproject.api.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.api.crypto.PasswordStrengthEstimator.WEAK; -import java.util.Arrays; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -43,7 +42,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.os.Bundle; -import android.text.Editable; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; @@ -187,18 +185,16 @@ OnEditorActionListener { else strengthMeter.setVisibility(INVISIBLE); String nickname = nicknameEntry.getText().toString(); int nicknameLength = StringUtils.toUtf8(nickname).length; - char[] firstPassword = getChars(passwordEntry.getText()); - char[] secondPassword = getChars(passwordConfirmation.getText()); - boolean passwordsMatch = Arrays.equals(firstPassword, secondPassword); + String firstPassword = passwordEntry.getText().toString(); + String secondPassword = passwordConfirmation.getText().toString(); + boolean passwordsMatch = firstPassword.equals(secondPassword); float strength = strengthEstimator.estimateStrength(firstPassword); - for(int i = 0; i < firstPassword.length; i++) firstPassword[i] = 0; - for(int i = 0; i < secondPassword.length; i++) secondPassword[i] = 0; strengthMeter.setStrength(strength); if(nicknameLength > MAX_AUTHOR_NAME_LENGTH) { feedback.setText(R.string.name_too_long); - } else if(firstPassword.length == 0) { + } else if(firstPassword.length() == 0) { feedback.setText(""); - } else if(secondPassword.length == 0 || passwordsMatch) { + } else if(secondPassword.length() == 0 || passwordsMatch) { if(strength < PasswordStrengthEstimator.WEAK) feedback.setText(R.string.password_too_weak); else feedback.setText(""); @@ -212,13 +208,6 @@ OnEditorActionListener { && passwordsMatch && strength >= WEAK); } - private char[] getChars(Editable e) { - int length = e.length(); - char[] c = new char[length]; - e.getChars(0, length, c, 0); - return c; - } - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { // Hide the soft keyboard Object o = getSystemService(INPUT_METHOD_SERVICE); @@ -231,18 +220,14 @@ OnEditorActionListener { feedback.setVisibility(GONE); continueButton.setVisibility(GONE); progress.setVisibility(VISIBLE); - // Copy the passwords and erase the originals final String nickname = nicknameEntry.getText().toString(); - final char[] password = getChars(passwordEntry.getText()); - delete(passwordEntry.getText()); - delete(passwordConfirmation.getText()); + final String password = passwordEntry.getText().toString(); // Store the DB key and create the identity in a background thread cryptoExecutor.execute(new Runnable() { public void run() { - byte[] key = crypto.generateSecretKey().getEncoded(); + byte[] key = crypto.generateSecretKey().getBytes(); databaseConfig.setEncryptionKey(key); byte[] encrypted = encryptDatabaseKey(key, password); - for(int i = 0; i < password.length; i++) password[i] = 0; storeEncryptedDatabaseKey(encrypted); LocalAuthor localAuthor = createLocalAuthor(nickname); showDashboard(referenceManager.putReference(localAuthor, @@ -251,10 +236,6 @@ OnEditorActionListener { }); } - private void delete(Editable e) { - e.delete(0, e.length()); - } - private void storeEncryptedDatabaseKey(final byte[] encrypted) { long now = System.currentTimeMillis(); SharedPreferences prefs = getSharedPreferences("db", MODE_PRIVATE); @@ -266,7 +247,7 @@ OnEditorActionListener { LOG.info("Key storage took " + duration + " ms"); } - private byte[] encryptDatabaseKey(byte[] key, char[] password) { + private byte[] encryptDatabaseKey(byte[] key, String password) { long now = System.currentTimeMillis(); byte[] encrypted = crypto.encryptWithPassword(key, password); long duration = System.currentTimeMillis() - now; diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java index 1b65e45c8717b24e39f9f41baffe93a2614cd6d0..a63bbc7ebbc189a19c7d9a47094709caf5e78b40 100644 --- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java +++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java @@ -69,8 +69,7 @@ class DroidtoothPlugin implements DuplexPlugin { private final SecureRandom secureRandom; private final Clock clock; private final DuplexPluginCallback callback; - private final int maxFrameLength; - private final long maxLatency, pollingInterval; + private final int maxLatency, pollingInterval; private volatile boolean running = false; private volatile boolean wasDisabled = false; @@ -82,15 +81,14 @@ class DroidtoothPlugin implements DuplexPlugin { DroidtoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor, Context appContext, SecureRandom secureRandom, Clock clock, - DuplexPluginCallback callback, int maxFrameLength, long maxLatency, - long pollingInterval) { + DuplexPluginCallback callback, int maxLatency, + int pollingInterval) { this.ioExecutor = ioExecutor; this.androidExecutor = androidExecutor; this.appContext = appContext; this.secureRandom = secureRandom; this.clock = clock; this.callback = callback; - this.maxFrameLength = maxFrameLength; this.maxLatency = maxLatency; this.pollingInterval = pollingInterval; } @@ -99,12 +97,13 @@ class DroidtoothPlugin implements DuplexPlugin { return ID; } - public int getMaxFrameLength() { - return maxFrameLength; + public int getMaxLatency() { + return maxLatency; } - public long getMaxLatency() { - return maxLatency; + public int getMaxIdleTime() { + // Bluetooth detects dead connections so we don't need keepalives + return Integer.MAX_VALUE; } public boolean start() throws IOException { @@ -240,7 +239,7 @@ class DroidtoothPlugin implements DuplexPlugin { return true; } - public long getPollingInterval() { + public int getPollingInterval() { return pollingInterval; } @@ -361,6 +360,7 @@ class DroidtoothPlugin implements DuplexPlugin { private class BluetoothStateReceiver extends BroadcastReceiver { + @Override public void onReceive(Context ctx, Intent intent) { int state = intent.getIntExtra(EXTRA_STATE, 0); if(state == STATE_ON) { diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java index b7bb2028546aaa3dc508cffdbde4da0ab9bb3835..2b04481c4b6e1f2d0641a734011bf90634d00201 100644 --- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPluginFactory.java @@ -15,9 +15,8 @@ import android.content.Context; public class DroidtoothPluginFactory implements DuplexPluginFactory { - private static final int MAX_FRAME_LENGTH = 1024; - private static final long MAX_LATENCY = 60 * 1000; // 1 minute - private static final long POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes + private static final int MAX_LATENCY = 30 * 1000; // 30 seconds + private static final int POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes private final Executor ioExecutor; private final AndroidExecutor androidExecutor; @@ -41,7 +40,6 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory { public DuplexPlugin createPlugin(DuplexPluginCallback callback) { return new DroidtoothPlugin(ioExecutor, androidExecutor, appContext, - secureRandom, clock, callback, MAX_FRAME_LENGTH, MAX_LATENCY, - POLLING_INTERVAL); + secureRandom, clock, callback, MAX_LATENCY, POLLING_INTERVAL); } } diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java index d349650735cbd6540378c97777b9e28e013eb8b2..e9b645e1c81d8c4efa31f34ac3467c00518c7968 100644 --- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java +++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java @@ -39,10 +39,6 @@ class DroidtoothTransportConnection implements DuplexTransportConnection { private class Reader implements TransportConnectionReader { - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); - } - public long getMaxLatency() { return plugin.getMaxLatency(); } @@ -60,12 +56,12 @@ class DroidtoothTransportConnection implements DuplexTransportConnection { private class Writer implements TransportConnectionWriter { - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); + public int getMaxLatency() { + return plugin.getMaxLatency(); } - public long getMaxLatency() { - return plugin.getMaxLatency(); + public int getMaxIdleTime() { + return plugin.getMaxIdleTime(); } public long getCapacity() { diff --git a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java index eaec50c41c0873622e5ae4da61fac973c90132e3..a7689bbda817040374156027b5616220025d2f22 100644 --- a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java @@ -26,10 +26,9 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { private volatile BroadcastReceiver networkStateReceiver = null; AndroidLanTcpPlugin(Executor ioExecutor, Context appContext, - DuplexPluginCallback callback, int maxFrameLength, long maxLatency, - long pollingInterval) { - super(ioExecutor, callback, maxFrameLength, maxLatency, - pollingInterval); + DuplexPluginCallback callback, int maxLatency, + int maxIdleTime, int pollingInterval) { + super(ioExecutor, callback, maxLatency, maxIdleTime, pollingInterval); this.appContext = appContext; } diff --git a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java index a51326e55bb441263c69f2191935408d5b1b621f..debad159c47e8d84eb96c11182186b67f2526dbd 100644 --- a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java @@ -11,9 +11,9 @@ import android.content.Context; public class AndroidLanTcpPluginFactory implements DuplexPluginFactory { - private static final int MAX_FRAME_LENGTH = 1024; - private static final long MAX_LATENCY = 60 * 1000; // 1 minute - private static final long POLLING_INTERVAL = 60 * 1000; // 1 minute + private static final int MAX_LATENCY = 30 * 1000; // 30 seconds + private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds + private static final int POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes private final Executor ioExecutor; private final Context appContext; @@ -29,6 +29,6 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory { public DuplexPlugin createPlugin(DuplexPluginCallback callback) { return new AndroidLanTcpPlugin(ioExecutor, appContext, callback, - MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL); + MAX_LATENCY, MAX_IDLE_TIME, POLLING_INTERVAL); } } diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index 905040b5c696f2c20e15f758a084092655785788..0d89832611d713258abc8848756cbe70d2d88285 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -75,8 +75,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { private final Context appContext; private final LocationUtils locationUtils; private final DuplexPluginCallback callback; - private final int maxFrameLength; - private final long maxLatency, pollingInterval; + private final int maxLatency, maxIdleTime, pollingInterval, socketTimeout; private final File torDirectory, torFile, geoIpFile, configFile, doneFile; private final File cookieFile, hostnameFile; private final AtomicBoolean circuitBuilt; @@ -90,14 +89,17 @@ class TorPlugin implements DuplexPlugin, EventHandler { TorPlugin(Executor ioExecutor, Context appContext, LocationUtils locationUtils, DuplexPluginCallback callback, - int maxFrameLength, long maxLatency, long pollingInterval) { + int maxLatency, int maxIdleTime, int pollingInterval) { this.ioExecutor = ioExecutor; this.appContext = appContext; this.locationUtils = locationUtils; this.callback = callback; - this.maxFrameLength = maxFrameLength; this.maxLatency = maxLatency; + this.maxIdleTime = maxIdleTime; this.pollingInterval = pollingInterval; + if(maxIdleTime > Integer.MAX_VALUE / 2) + socketTimeout = Integer.MAX_VALUE; + else socketTimeout = maxIdleTime * 2; torDirectory = appContext.getDir("tor", MODE_PRIVATE); torFile = new File(torDirectory, "tor"); geoIpFile = new File(torDirectory, "geoip"); @@ -112,12 +114,12 @@ class TorPlugin implements DuplexPlugin, EventHandler { return ID; } - public int getMaxFrameLength() { - return maxFrameLength; + public int getMaxLatency() { + return maxLatency; } - public long getMaxLatency() { - return maxLatency; + public int getMaxIdleTime() { + return maxIdleTime; } public boolean start() throws IOException { @@ -446,6 +448,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { Socket s; try { s = ss.accept(); + s.setSoTimeout(socketTimeout); } catch(IOException e) { // This is expected when the socket is closed if(LOG.isLoggable(INFO)) LOG.info(e.toString()); @@ -494,7 +497,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { return true; } - public long getPollingInterval() { + public int getPollingInterval() { return pollingInterval; } @@ -529,6 +532,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", SOCKS_PORT); proxy.resolveAddrLocally(false); Socket s = new SocksSocket(proxy, onion, 80); + s.setSoTimeout(socketTimeout); if(LOG.isLoggable(INFO)) LOG.info("Connected to " + onion); return new TorTransportConnection(this, s); } catch(IOException e) { diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java index b7c08b225a72c1d0dea4c73a09e9f9118216a68b..4861ba6b797188982c9433a4922f8fea107127f5 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java @@ -17,9 +17,9 @@ public class TorPluginFactory implements DuplexPluginFactory { private static final Logger LOG = Logger.getLogger(TorPluginFactory.class.getName()); - private static final int MAX_FRAME_LENGTH = 1024; - private static final long MAX_LATENCY = 60 * 1000; // 1 minute - private static final long POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes + private static final int MAX_LATENCY = 30 * 1000; // 30 seconds + private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds + private static final int POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes private final Executor ioExecutor; private final Context appContext; @@ -43,6 +43,6 @@ public class TorPluginFactory implements DuplexPluginFactory { return null; } return new TorPlugin(ioExecutor,appContext, locationUtils, callback, - MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL); + MAX_LATENCY, MAX_IDLE_TIME, POLLING_INTERVAL); } } diff --git a/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java b/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java index ab0969eae633fd8270cc35bcb33bc8e361884ebf..4640eb1f6be81d3ae0275ed2274141aec2c385b2 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java @@ -38,10 +38,6 @@ class TorTransportConnection implements DuplexTransportConnection { private class Reader implements TransportConnectionReader { - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); - } - public long getMaxLatency() { return plugin.getMaxLatency(); } @@ -59,12 +55,12 @@ class TorTransportConnection implements DuplexTransportConnection { private class Writer implements TransportConnectionWriter { - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); + public int getMaxLatency() { + return plugin.getMaxLatency(); } - public long getMaxLatency() { - return plugin.getMaxLatency(); + public int getMaxIdleTime() { + return plugin.getMaxIdleTime(); } public long getCapacity() { diff --git a/briar-api/src/org/briarproject/api/crypto/AuthenticatedCipher.java b/briar-api/src/org/briarproject/api/crypto/AuthenticatedCipher.java index 1a6d955229509fc9888ff2e11f94eaf5500b8bc3..6f60ed0526c2fe714cb80c7418ee0d3db95b2b69 100644 --- a/briar-api/src/org/briarproject/api/crypto/AuthenticatedCipher.java +++ b/briar-api/src/org/briarproject/api/crypto/AuthenticatedCipher.java @@ -2,7 +2,7 @@ package org.briarproject.api.crypto; import java.security.GeneralSecurityException; -/** An authenticated cipher that support additional authenticated data. */ +/** An authenticated cipher that supports additional authenticated data. */ public interface AuthenticatedCipher { /** @@ -13,10 +13,10 @@ public interface AuthenticatedCipher { throws GeneralSecurityException; /** Encrypts or decrypts data in a single-part operation. */ - int doFinal(byte[] input, int inputOff, int len, byte[] output, + int process(byte[] input, int inputOff, int len, byte[] output, int outputOff) throws GeneralSecurityException; - /** Returns the length of the message authenticated code (MAC) in bytes. */ + /** Returns the length of the message authentication code (MAC) in bytes. */ int getMacLength(); /** Returns the block size of the cipher in bytes. */ diff --git a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java index ba9c86224b04d83b6ae67d1da2ed50b7f9f90b83..0f448daef8b84cfcc5a458404dba73e651a76168 100644 --- a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java +++ b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java @@ -65,7 +65,7 @@ public interface CryptoComponent { /** * Derives a tag key from the given temporary secret. - * @param alice indicates whether the key is for connections initiated by + * @param alice indicates whether the key is for streams initiated by * Alice or Bob. */ SecretKey deriveTagKey(byte[] secret, boolean alice); @@ -89,7 +89,7 @@ public interface CryptoComponent { * given password. The ciphertext will be decryptable using the same * password after the app restarts. */ - byte[] encryptWithPassword(byte[] plaintext, char[] password); + byte[] encryptWithPassword(byte[] plaintext, String password); /** * Decrypts and authenticates the given ciphertext that has been read from @@ -97,5 +97,5 @@ public interface CryptoComponent { * given password. Returns null if the ciphertext cannot be decrypted and * authenticated (for example, if the password is wrong). */ - byte[] decryptWithPassword(byte[] ciphertext, char[] password); + byte[] decryptWithPassword(byte[] ciphertext, String password); } diff --git a/briar-api/src/org/briarproject/api/crypto/KeyManager.java b/briar-api/src/org/briarproject/api/crypto/KeyManager.java index 59e4d7af3083c5d034de0ef0040806a5162944a3..02a43e9706656cd06dace79eb64730f1e2561ed6 100644 --- a/briar-api/src/org/briarproject/api/crypto/KeyManager.java +++ b/briar-api/src/org/briarproject/api/crypto/KeyManager.java @@ -16,9 +16,6 @@ public interface KeyManager extends Service { */ StreamContext getStreamContext(ContactId c, TransportId t); - /** - * Called whenever an endpoint has been added. The initial secret is erased - * before returning. - */ - void endpointAdded(Endpoint ep, long maxLatency, byte[] initialSecret); + /** Called whenever an endpoint has been added. */ + void endpointAdded(Endpoint ep, int maxLatency, byte[] initialSecret); } diff --git a/briar-api/src/org/briarproject/api/crypto/PasswordStrengthEstimator.java b/briar-api/src/org/briarproject/api/crypto/PasswordStrengthEstimator.java index 9ffa2dc244da42bfe0d4a675721510a640b53754..93d27a6b32aaa142a86cfd4c53f054900cf7fc4d 100644 --- a/briar-api/src/org/briarproject/api/crypto/PasswordStrengthEstimator.java +++ b/briar-api/src/org/briarproject/api/crypto/PasswordStrengthEstimator.java @@ -12,5 +12,5 @@ public interface PasswordStrengthEstimator { * Returns an estimate between 0 (weakest) and 1 (strongest), inclusive, * of the strength of the given password. */ - float estimateStrength(char[] password); + float estimateStrength(String password); } diff --git a/briar-api/src/org/briarproject/api/crypto/SecretKey.java b/briar-api/src/org/briarproject/api/crypto/SecretKey.java index 63d1f7661705087cc95ed9050072bba010bd0370..62b75a9452bfbc736179d0abe83050574228d8a8 100644 --- a/briar-api/src/org/briarproject/api/crypto/SecretKey.java +++ b/briar-api/src/org/briarproject/api/crypto/SecretKey.java @@ -1,21 +1,15 @@ package org.briarproject.api.crypto; /** A secret key used for encryption and/or authentication. */ -public interface SecretKey { +public class SecretKey { - /** Returns the encoded representation of this key. */ - byte[] getEncoded(); + private final byte[] key; - /** - * Returns a copy of this key - erasing this key will erase the copy and - * vice versa. - */ - SecretKey copy(); + public SecretKey(byte[] key) { + this.key = key; + } - /** - * Erases this key from memory. Any copies derived from this key via the - * {@link #copy()} method, and any keys from which this key was derived via - * the {@link #copy()} method, are also erased. - */ - void erase(); + public byte[] getBytes() { + return key; + } } diff --git a/briar-api/src/org/briarproject/api/crypto/StreamDecrypter.java b/briar-api/src/org/briarproject/api/crypto/StreamDecrypter.java new file mode 100644 index 0000000000000000000000000000000000000000..9f217c13861ba4cc6525c7c69ddcc4abcfae3d0f --- /dev/null +++ b/briar-api/src/org/briarproject/api/crypto/StreamDecrypter.java @@ -0,0 +1,14 @@ +package org.briarproject.api.crypto; + +import java.io.IOException; + +public interface StreamDecrypter { + + /** + * Reads a frame, decrypts its payload into the given buffer and returns + * the payload length, or -1 if no more frames can be read from the stream. + * @throws java.io.IOException if an error occurs while reading the frame, + * or if authenticated decryption fails. + */ + int readFrame(byte[] payload) throws IOException; +} diff --git a/briar-api/src/org/briarproject/api/crypto/StreamDecrypterFactory.java b/briar-api/src/org/briarproject/api/crypto/StreamDecrypterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..8b758ec11feef6c53fe7ef5cbb8ad65ca4a6f6aa --- /dev/null +++ b/briar-api/src/org/briarproject/api/crypto/StreamDecrypterFactory.java @@ -0,0 +1,17 @@ +package org.briarproject.api.crypto; + +import java.io.InputStream; + +import org.briarproject.api.transport.StreamContext; + +public interface StreamDecrypterFactory { + + /** Creates a {@link StreamDecrypter} for decrypting a transport stream. */ + StreamDecrypter createStreamDecrypter(InputStream in, StreamContext ctx); + + /** + * Creates a {@link StreamDecrypter} for decrypting an invitation stream. + */ + StreamDecrypter createInvitationStreamDecrypter(InputStream in, + byte[] secret, boolean alice); +} diff --git a/briar-api/src/org/briarproject/api/crypto/StreamEncrypter.java b/briar-api/src/org/briarproject/api/crypto/StreamEncrypter.java new file mode 100644 index 0000000000000000000000000000000000000000..a332743bb1924fbca5dbb90b22ec6bbbd2b8eeee --- /dev/null +++ b/briar-api/src/org/briarproject/api/crypto/StreamEncrypter.java @@ -0,0 +1,13 @@ +package org.briarproject.api.crypto; + +import java.io.IOException; + +public interface StreamEncrypter { + + /** Encrypts the given frame and writes it to the stream. */ + void writeFrame(byte[] payload, int payloadLength, int paddingLength, + boolean finalFrame) throws IOException; + + /** Flushes the stream. */ + void flush() throws IOException; +} diff --git a/briar-api/src/org/briarproject/api/crypto/StreamEncrypterFactory.java b/briar-api/src/org/briarproject/api/crypto/StreamEncrypterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..86df7bbe2439f1f0ea9c6417cd60c8b1911614f3 --- /dev/null +++ b/briar-api/src/org/briarproject/api/crypto/StreamEncrypterFactory.java @@ -0,0 +1,17 @@ +package org.briarproject.api.crypto; + +import java.io.OutputStream; + +import org.briarproject.api.transport.StreamContext; + +public interface StreamEncrypterFactory { + + /** Creates a {@link StreamEncrypter} for encrypting a transport stream. */ + StreamEncrypter createStreamEncrypter(OutputStream out, StreamContext ctx); + + /** + * Creates a {@link StreamEncrypter} for encrypting an invitation stream. + */ + StreamEncrypter createInvitationStreamEncrypter(OutputStream out, + byte[] secret, boolean alice); +} diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java index e7b55dd6d2bf857e89825fcdc12d7bdff18521e1..5159ac0220bc19863092d67a1fe6b0427c15143c 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java @@ -73,7 +73,7 @@ public interface DatabaseComponent { * Stores a transport and returns true if the transport was not previously * in the database. */ - boolean addTransport(TransportId t, long maxLatency) throws DbException; + boolean addTransport(TransportId t, int maxLatency) throws DbException; /** * Returns an acknowledgement for the given contact, or null if there are @@ -88,14 +88,14 @@ public interface DatabaseComponent { * sendable messages that fit in the given length. */ Collection<byte[]> generateBatch(ContactId c, int maxLength, - long maxLatency) throws DbException; + int maxLatency) throws DbException; /** * Returns an offer for the given contact for transmission over a * transport with the given maximum latency, or null if there are no * messages to offer. */ - Offer generateOffer(ContactId c, int maxMessages, long maxLatency) + Offer generateOffer(ContactId c, int maxMessages, int maxLatency) throws DbException; /** @@ -112,7 +112,7 @@ public interface DatabaseComponent { * sendable messages that fit in the given length. */ Collection<byte[]> generateRequestedBatch(ContactId c, int maxLength, - long maxLatency) throws DbException; + int maxLatency) throws DbException; /** * Returns a retention ack for the given contact, or null if no retention @@ -125,7 +125,7 @@ public interface DatabaseComponent { * over a transport with the given latency. Returns null if no update is * due. */ - RetentionUpdate generateRetentionUpdate(ContactId c, long maxLatency) + RetentionUpdate generateRetentionUpdate(ContactId c, int maxLatency) throws DbException; /** @@ -139,7 +139,7 @@ public interface DatabaseComponent { * over a transport with the given latency. Returns null if no update is * due. */ - SubscriptionUpdate generateSubscriptionUpdate(ContactId c, long maxLatency) + SubscriptionUpdate generateSubscriptionUpdate(ContactId c, int maxLatency) throws DbException; /** @@ -155,7 +155,7 @@ public interface DatabaseComponent { * updates are due. */ Collection<TransportUpdate> generateTransportUpdates(ContactId c, - long maxLatency) throws DbException; + int maxLatency) throws DbException; /** * Returns the status of all groups to which the user subscribes or can @@ -227,8 +227,8 @@ public interface DatabaseComponent { /** Returns all contacts who subscribe to the given group. */ Collection<Contact> getSubscribers(GroupId g) throws DbException; - /** Returns the maximum latencies of all local transports. */ - Map<TransportId, Long> getTransportLatencies() throws DbException; + /** Returns the maximum latencies of all supported transports. */ + Map<TransportId, Integer> getTransportLatencies() throws DbException; /** Returns the number of unread messages in each subscribed group. */ Map<GroupId, Integer> getUnreadMessageCounts() throws DbException; diff --git a/briar-api/src/org/briarproject/api/event/TransportAddedEvent.java b/briar-api/src/org/briarproject/api/event/TransportAddedEvent.java index 7a1f599a6e59d452f782d61144678e5454c0a1ab..b775ce6c9e5dd88c112f6821fe157b4a0e68d853 100644 --- a/briar-api/src/org/briarproject/api/event/TransportAddedEvent.java +++ b/briar-api/src/org/briarproject/api/event/TransportAddedEvent.java @@ -6,9 +6,9 @@ import org.briarproject.api.TransportId; public class TransportAddedEvent extends Event { private final TransportId transportId; - private final long maxLatency; + private final int maxLatency; - public TransportAddedEvent(TransportId transportId, long maxLatency) { + public TransportAddedEvent(TransportId transportId, int maxLatency) { this.transportId = transportId; this.maxLatency = maxLatency; } @@ -17,7 +17,7 @@ public class TransportAddedEvent extends Event { return transportId; } - public long getMaxLatency() { + public int getMaxLatency() { return maxLatency; } } diff --git a/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java b/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java index c5ceca842c7afbc01945b5080afc7f5ed01ab63f..8764f3e255bb0ed10e8a676b9887dfd63172b22d 100644 --- a/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java +++ b/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java @@ -11,6 +11,9 @@ public interface MessagingSessionFactory { MessagingSession createIncomingSession(ContactId c, TransportId t, InputStream in); - MessagingSession createOutgoingSession(ContactId c, TransportId t, - long maxLatency, boolean duplex, OutputStream out); + MessagingSession createSimplexOutgoingSession(ContactId c, TransportId t, + int maxLatency, OutputStream out); + + MessagingSession createDuplexOutgoingSession(ContactId c, TransportId t, + int maxLatency, int maxIdleTime, OutputStream out); } diff --git a/briar-api/src/org/briarproject/api/messaging/PacketWriter.java b/briar-api/src/org/briarproject/api/messaging/PacketWriter.java index bc7b4e1de6d4086956003b46872a508e7a548800..1300e84c5b7730eb3f2088bf308e6dfcb561b0cd 100644 --- a/briar-api/src/org/briarproject/api/messaging/PacketWriter.java +++ b/briar-api/src/org/briarproject/api/messaging/PacketWriter.java @@ -29,4 +29,6 @@ public interface PacketWriter { void writeTransportAck(TransportAck a) throws IOException; void writeTransportUpdate(TransportUpdate u) throws IOException; + + void flush() throws IOException; } diff --git a/briar-api/src/org/briarproject/api/plugins/Plugin.java b/briar-api/src/org/briarproject/api/plugins/Plugin.java index 3389a73577d9da1ce789a25d3387d2ae87fe9b16..cc0338a4a3b9d1d9386db6885368e6f34adbef6c 100644 --- a/briar-api/src/org/briarproject/api/plugins/Plugin.java +++ b/briar-api/src/org/briarproject/api/plugins/Plugin.java @@ -11,11 +11,11 @@ public interface Plugin { /** Returns the plugin's transport identifier. */ TransportId getId(); - /** Returns the transport's maximum frame length in bytes. */ - int getMaxFrameLength(); - /** Returns the transport's maximum latency in milliseconds. */ - long getMaxLatency(); + int getMaxLatency(); + + /** Returns the transport's maximum idle time in milliseconds. */ + int getMaxIdleTime(); /** Starts the plugin and returns true if it started successfully. */ boolean start() throws IOException; @@ -36,7 +36,7 @@ public interface Plugin { * Returns the desired interval in milliseconds between calls to the * plugin's {@link #poll(Collection)} method. */ - long getPollingInterval(); + int getPollingInterval(); /** * Attempts to establish connections to contacts, passing any created diff --git a/briar-api/src/org/briarproject/api/plugins/TransportConnectionReader.java b/briar-api/src/org/briarproject/api/plugins/TransportConnectionReader.java index c4c99dfa37c3e17c94a43a438922fe5a52d173c2..e071c4432b0b8d5eeccd0f014294b25281ed262a 100644 --- a/briar-api/src/org/briarproject/api/plugins/TransportConnectionReader.java +++ b/briar-api/src/org/briarproject/api/plugins/TransportConnectionReader.java @@ -9,9 +9,6 @@ import java.io.InputStream; */ public interface TransportConnectionReader { - /** Returns the maximum frame length of the transport in bytes. */ - int getMaxFrameLength(); - /** Returns the maximum latency of the transport in milliseconds. */ long getMaxLatency(); diff --git a/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java b/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java index 554e5de8a110e3d24c249cf896287bdd1e5336d4..d75522f1bb2c48f6541909864008e4bbf876ddb9 100644 --- a/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java +++ b/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java @@ -9,11 +9,11 @@ import java.io.OutputStream; */ public interface TransportConnectionWriter { - /** Returns the maximum frame length of the transport in bytes. */ - int getMaxFrameLength(); - /** Returns the maximum latency of the transport in milliseconds. */ - long getMaxLatency(); + int getMaxLatency(); + + /** Returns the maximum idle time of the transport in milliseconds. */ + int getMaxIdleTime(); /** Returns the capacity of the transport connection in bytes. */ long getCapacity(); diff --git a/briar-api/src/org/briarproject/api/transport/StreamReader.java b/briar-api/src/org/briarproject/api/transport/StreamReader.java deleted file mode 100644 index 2fef7b4df0a913f119c7436090d85046f8fbb164..0000000000000000000000000000000000000000 --- a/briar-api/src/org/briarproject/api/transport/StreamReader.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.briarproject.api.transport; - -import java.io.InputStream; - -/** Decrypts and authenticates data received over an underlying transport. */ -public interface StreamReader { - - /** - * Returns an input stream from which the decrypted, authenticated data can - * be read. - */ - InputStream getInputStream(); -} diff --git a/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java b/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java index 14459ec60a0d7e1c2e1f3e286fdaea0dd8554ea9..5cf4fd6394ac7f9004d5ad1d48f5d1cca25f7fe2 100644 --- a/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java +++ b/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java @@ -4,11 +4,16 @@ import java.io.InputStream; public interface StreamReaderFactory { - /** Creates a {@link StreamReader} for a transport connection. */ - StreamReader createStreamReader(InputStream in, int maxFrameLength, - StreamContext ctx); + /** + * Creates an {@link java.io.InputStream InputStream} for reading from a + * transport stream. + */ + InputStream createStreamReader(InputStream in, StreamContext ctx); - /** Creates a {@link StreamReader} for an invitation connection. */ - StreamReader createInvitationStreamReader(InputStream in, - int maxFrameLength, byte[] secret, boolean alice); + /** + * Creates an {@link java.io.InputStream InputStream} for reading from an + * invitation stream. + */ + InputStream createInvitationStreamReader(InputStream in, + byte[] secret, boolean alice); } diff --git a/briar-api/src/org/briarproject/api/transport/StreamWriter.java b/briar-api/src/org/briarproject/api/transport/StreamWriter.java deleted file mode 100644 index 2684742dbdf5889fcb382c8cadbaade65b2eb7f0..0000000000000000000000000000000000000000 --- a/briar-api/src/org/briarproject/api/transport/StreamWriter.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.briarproject.api.transport; - -import java.io.OutputStream; - -/** Encrypts and authenticates data to be sent over an underlying transport. */ -public interface StreamWriter { - - /** - * Returns an output stream to which unencrypted, unauthenticated data can - * be written. - */ - OutputStream getOutputStream(); -} diff --git a/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java b/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java index f038a75221d52f0798ab9176ed353868d281d624..38e373ccc244a1439d332176ee5380a89343aa9f 100644 --- a/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java +++ b/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java @@ -4,11 +4,16 @@ import java.io.OutputStream; public interface StreamWriterFactory { - /** Creates a {@link StreamWriter} for a transport connection. */ - StreamWriter createStreamWriter(OutputStream out, int maxFrameLength, - StreamContext ctx); + /** + * Creates an {@link java.io.OutputStream OutputStream} for writing to a + * transport stream + */ + OutputStream createStreamWriter(OutputStream out, StreamContext ctx); - /** Creates a {@link StreamWriter} for an invitation connection. */ - StreamWriter createInvitationStreamWriter(OutputStream out, - int maxFrameLength, byte[] secret, boolean alice); + /** + * Creates an {@link java.io.OutputStream OutputStream} for writing to an + * invitation stream. + */ + OutputStream createInvitationStreamWriter(OutputStream out, + byte[] secret, boolean alice); } diff --git a/briar-api/src/org/briarproject/api/transport/TransportConstants.java b/briar-api/src/org/briarproject/api/transport/TransportConstants.java index 666d74e964b00202b89b907c1011caa5e98616d3..8f05eb23ab9c9ecf44608671176c67ae1b861657 100644 --- a/briar-api/src/org/briarproject/api/transport/TransportConstants.java +++ b/briar-api/src/org/briarproject/api/transport/TransportConstants.java @@ -1,24 +1,25 @@ package org.briarproject.api.transport; + public interface TransportConstants { /** The length of the pseudo-random tag in bytes. */ int TAG_LENGTH = 16; /** The maximum length of a frame in bytes, including the header and MAC. */ - int MAX_FRAME_LENGTH = 32768; // 2^15, 32 KiB - - /** The length of the initalisation vector (IV) in bytes. */ - int IV_LENGTH = 12; + int MAX_FRAME_LENGTH = 1024; - /** The length of the additional authenticated data (AAD) in bytes. */ - int AAD_LENGTH = 6; + /** The length of the message authentication code (MAC) in bytes. */ + int MAC_LENGTH = 16; /** The length of the frame header in bytes. */ - int HEADER_LENGTH = 2; + int HEADER_LENGTH = 4 + MAC_LENGTH; - /** The length of the message authentication code (MAC) in bytes. */ - int MAC_LENGTH = 16; + /** The maximum total length of the frame payload and padding in bytes. */ + int MAX_PAYLOAD_LENGTH = MAX_FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; + + /** The length of the initalisation vector (IV) in bytes. */ + int IV_LENGTH = 12; /** * The minimum stream length in bytes that all transport plugins must diff --git a/briar-core/src/org/briarproject/crypto/AuthenticatedCipherImpl.java b/briar-core/src/org/briarproject/crypto/AuthenticatedCipherImpl.java index 93807087d263c1d5f3ad43b100ea304a907b0441..d001b60c5441d4f90fbeb074874485c8a4f984e0 100644 --- a/briar-core/src/org/briarproject/crypto/AuthenticatedCipherImpl.java +++ b/briar-core/src/org/briarproject/crypto/AuthenticatedCipherImpl.java @@ -20,7 +20,7 @@ class AuthenticatedCipherImpl implements AuthenticatedCipher { this.macLength = macLength; } - public int doFinal(byte[] input, int inputOff, int len, byte[] output, + public int process(byte[] input, int inputOff, int len, byte[] output, int outputOff) throws GeneralSecurityException { int processed = 0; if(len != 0) { @@ -38,7 +38,7 @@ class AuthenticatedCipherImpl implements AuthenticatedCipher { public void init(boolean encrypt, SecretKey key, byte[] iv, byte[] aad) throws GeneralSecurityException { - KeyParameter k = new KeyParameter(key.getEncoded()); + KeyParameter k = new KeyParameter(key.getBytes()); AEADParameters params = new AEADParameters(k, macLength * 8, iv, aad); try { cipher.init(encrypt, params); diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java index 4edd26a4fe9835f83f54054660f0442ed66e0a4e..5612c407769787fd131893587e45798395047aa7 100644 --- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java +++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java @@ -7,8 +7,6 @@ import static org.briarproject.crypto.EllipticCurveConstants.P; import static org.briarproject.crypto.EllipticCurveConstants.PARAMETERS; import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.security.GeneralSecurityException; import java.security.SecureRandom; import java.util.ArrayList; @@ -31,6 +29,7 @@ import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.crypto.Signature; import org.briarproject.api.system.SeedProvider; import org.briarproject.util.ByteUtils; +import org.briarproject.util.StringUtils; import org.spongycastle.crypto.AsymmetricCipherKeyPair; import org.spongycastle.crypto.BlockCipher; import org.spongycastle.crypto.CipherParameters; @@ -44,11 +43,11 @@ import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator; import org.spongycastle.crypto.macs.HMac; import org.spongycastle.crypto.modes.AEADBlockCipher; import org.spongycastle.crypto.modes.GCMBlockCipher; +import org.spongycastle.crypto.modes.gcm.BasicGCMMultiplier; import org.spongycastle.crypto.params.ECKeyGenerationParameters; import org.spongycastle.crypto.params.ECPrivateKeyParameters; import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.crypto.params.KeyParameter; -import org.spongycastle.util.Strings; class CryptoComponentImpl implements CryptoComponent { @@ -114,7 +113,7 @@ class CryptoComponentImpl implements CryptoComponent { public SecretKey generateSecretKey() { byte[] b = new byte[CIPHER_KEY_BYTES]; secureRandom.nextBytes(b); - return new SecretKeyImpl(b); + return new SecretKey(b); } public MessageDigest getMessageDigest() { @@ -188,8 +187,6 @@ class CryptoComponentImpl implements CryptoComponent { int[] codes = new int[2]; codes[0] = ByteUtils.readUint(alice, CODE_BITS); codes[1] = ByteUtils.readUint(bob, CODE_BITS); - ByteUtils.erase(alice); - ByteUtils.erase(bob); return codes; } @@ -223,9 +220,7 @@ class CryptoComponentImpl implements CryptoComponent { byte[] raw = deriveSharedSecret(ourPriv, theirPub); // Derive the cooked secret from the raw secret using the // concatenation KDF - byte[] cooked = concatenationKdf(raw, MASTER, aliceInfo, bobInfo); - ByteUtils.erase(raw); - return cooked; + return concatenationKdf(raw, MASTER, aliceInfo, bobInfo); } // Package access for testing @@ -296,12 +291,16 @@ class CryptoComponentImpl implements CryptoComponent { } private SecretKey deriveKey(byte[] secret, byte[] label, long context) { - byte[] key = counterModeKdf(secret, label, context); - return new SecretKeyImpl(key); + return new SecretKey(counterModeKdf(secret, label, context)); } public AuthenticatedCipher getFrameCipher() { - AEADBlockCipher a = new GCMBlockCipher(new AESLightEngine()); + return getAuthenticatedCipher(); + } + + private AuthenticatedCipher getAuthenticatedCipher() { + AEADBlockCipher a = new GCMBlockCipher(new AESLightEngine(), + new BasicGCMMultiplier()); return new AuthenticatedCipherImpl(a, MAC_BYTES); } @@ -313,21 +312,19 @@ class CryptoComponentImpl implements CryptoComponent { ByteUtils.writeUint32(streamNumber, tag, 0); BlockCipher cipher = new AESLightEngine(); assert cipher.getBlockSize() == TAG_LENGTH; - KeyParameter k = new KeyParameter(tagKey.getEncoded()); + KeyParameter k = new KeyParameter(tagKey.getBytes()); cipher.init(true, k); cipher.processBlock(tag, 0, tag, 0); - ByteUtils.erase(k.getKey()); } - public byte[] encryptWithPassword(byte[] input, char[] password) { + public byte[] encryptWithPassword(byte[] input, String password) { // Generate a random salt byte[] salt = new byte[PBKDF_SALT_BYTES]; secureRandom.nextBytes(salt); // Calibrate the KDF int iterations = chooseIterationCount(PBKDF_TARGET_MILLIS); // Derive the key from the password - byte[] keyBytes = pbkdf2(password, salt, iterations); - SecretKey key = new SecretKeyImpl(keyBytes); + SecretKey key = new SecretKey(pbkdf2(password, salt, iterations)); // Generate a random IV byte[] iv = new byte[STORAGE_IV_BYTES]; secureRandom.nextBytes(iv); @@ -338,22 +335,18 @@ class CryptoComponentImpl implements CryptoComponent { ByteUtils.writeUint32(iterations, output, salt.length); System.arraycopy(iv, 0, output, salt.length + 4, iv.length); // Initialise the cipher and encrypt the plaintext + AuthenticatedCipher cipher = getAuthenticatedCipher(); try { - AEADBlockCipher a = new GCMBlockCipher(new AESLightEngine()); - AuthenticatedCipher cipher = new AuthenticatedCipherImpl(a, - MAC_BYTES); cipher.init(true, key, iv, null); int outputOff = salt.length + 4 + iv.length; - cipher.doFinal(input, 0, input.length, output, outputOff); + cipher.process(input, 0, input.length, output, outputOff); return output; } catch(GeneralSecurityException e) { throw new RuntimeException(e); - } finally { - key.erase(); } } - public byte[] decryptWithPassword(byte[] input, char[] password) { + public byte[] decryptWithPassword(byte[] input, String password) { // The input contains the salt, iterations, IV, ciphertext and MAC if(input.length < PBKDF_SALT_BYTES + 4 + STORAGE_IV_BYTES + MAC_BYTES) return null; // Invalid @@ -365,16 +358,12 @@ class CryptoComponentImpl implements CryptoComponent { byte[] iv = new byte[STORAGE_IV_BYTES]; System.arraycopy(input, salt.length + 4, iv, 0, iv.length); // Derive the key from the password - byte[] keyBytes = pbkdf2(password, salt, (int) iterations); - SecretKey key = new SecretKeyImpl(keyBytes); + SecretKey key = new SecretKey(pbkdf2(password, salt, (int) iterations)); // Initialise the cipher - AuthenticatedCipher cipher; + AuthenticatedCipher cipher = getAuthenticatedCipher(); try { - AEADBlockCipher a = new GCMBlockCipher(new AESLightEngine()); - cipher = new AuthenticatedCipherImpl(a, MAC_BYTES); cipher.init(false, key, iv, null); } catch(GeneralSecurityException e) { - key.erase(); throw new RuntimeException(e); } // Try to decrypt the ciphertext (may be invalid) @@ -382,12 +371,10 @@ class CryptoComponentImpl implements CryptoComponent { int inputOff = salt.length + 4 + iv.length; int inputLen = input.length - inputOff; byte[] output = new byte[inputLen - MAC_BYTES]; - cipher.doFinal(input, inputOff, inputLen, output, 0); + cipher.process(input, inputOff, inputLen, output, 0); return output; } catch(GeneralSecurityException e) { - return null; // Invalid - } finally { - key.erase(); + return null; // Invalid ciphertext } } @@ -417,7 +404,6 @@ class CryptoComponentImpl implements CryptoComponent { // The secret is the first CIPHER_KEY_BYTES bytes of the hash byte[] output = new byte[CIPHER_KEY_BYTES]; System.arraycopy(hash, 0, output, 0, output.length); - ByteUtils.erase(hash); return output; } @@ -447,20 +433,17 @@ class CryptoComponentImpl implements CryptoComponent { prf.update((byte) CIPHER_KEY_BYTES); // Output length prf.doFinal(mac, 0); System.arraycopy(mac, 0, output, 0, output.length); - ByteUtils.erase(mac); - ByteUtils.erase(k.getKey()); return output; } // Password-based key derivation function - see PKCS#5 v2.1, section 5.2 - private byte[] pbkdf2(char[] password, byte[] salt, int iterations) { - byte[] utf8 = toUtf8ByteArray(password); + private byte[] pbkdf2(String password, byte[] salt, int iterations) { + byte[] utf8 = StringUtils.toUtf8(password); Digest digest = new SHA384Digest(); PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest); gen.init(utf8, salt, iterations); int keyLengthInBits = CIPHER_KEY_BYTES * 8; CipherParameters p = gen.generateDerivedParameters(keyLengthInBits); - ByteUtils.erase(utf8); return ((KeyParameter) p).getKey(); } @@ -512,18 +495,4 @@ class CryptoComponentImpl implements CryptoComponent { if(size % 2 == 1) return list.get(size / 2); return list.get(size / 2 - 1) + list.get(size / 2) / 2; } - - private byte[] toUtf8ByteArray(char[] c) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - Strings.toUTF8ByteArray(c, out); - byte[] utf8 = out.toByteArray(); - // Erase the output stream's buffer - out.reset(); - out.write(new byte[utf8.length]); - return utf8; - } catch(IOException e) { - throw new RuntimeException(e); - } - } } diff --git a/briar-core/src/org/briarproject/crypto/CryptoModule.java b/briar-core/src/org/briarproject/crypto/CryptoModule.java index d7dd2fdb49a6798281ea8132c20dc46efe4a63e1..d4ac20c6495d50fd7c21f3c8a339a0579cb8ec9b 100644 --- a/briar-core/src/org/briarproject/crypto/CryptoModule.java +++ b/briar-core/src/org/briarproject/crypto/CryptoModule.java @@ -14,6 +14,8 @@ import javax.inject.Singleton; import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoExecutor; import org.briarproject.api.crypto.PasswordStrengthEstimator; +import org.briarproject.api.crypto.StreamDecrypterFactory; +import org.briarproject.api.crypto.StreamEncrypterFactory; import org.briarproject.api.lifecycle.LifecycleManager; import com.google.inject.AbstractModule; @@ -44,6 +46,8 @@ public class CryptoModule extends AbstractModule { CryptoComponentImpl.class).in(Singleton.class); bind(PasswordStrengthEstimator.class).to( PasswordStrengthEstimatorImpl.class); + bind(StreamDecrypterFactory.class).to(StreamDecrypterFactoryImpl.class); + bind(StreamEncrypterFactory.class).to(StreamEncrypterFactoryImpl.class); } @Provides @Singleton @CryptoExecutor diff --git a/briar-core/src/org/briarproject/transport/FrameEncoder.java b/briar-core/src/org/briarproject/crypto/FrameEncoder.java similarity index 54% rename from briar-core/src/org/briarproject/transport/FrameEncoder.java rename to briar-core/src/org/briarproject/crypto/FrameEncoder.java index 684b8754248391d6d8c01dca3b89b12ca55cf252..2cc6ffd58adca65a4cf6ba632ad86f63bb7df58f 100644 --- a/briar-core/src/org/briarproject/transport/FrameEncoder.java +++ b/briar-core/src/org/briarproject/crypto/FrameEncoder.java @@ -1,44 +1,33 @@ -package org.briarproject.transport; +package org.briarproject.crypto; -import static org.briarproject.api.transport.TransportConstants.AAD_LENGTH; import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; import static org.briarproject.api.transport.TransportConstants.IV_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED; import org.briarproject.util.ByteUtils; class FrameEncoder { - static void encodeIv(byte[] iv, long frameNumber) { + static void encodeIv(byte[] iv, long frameNumber, boolean header) { if(iv.length < IV_LENGTH) throw new IllegalArgumentException(); if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalArgumentException(); ByteUtils.writeUint32(frameNumber, iv, 0); - for(int i = 4; i < IV_LENGTH; i++) iv[i] = 0; - } - - static void encodeAad(byte[] aad, long frameNumber, int plaintextLength) { - if(aad.length < AAD_LENGTH) throw new IllegalArgumentException(); - if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED) - throw new IllegalArgumentException(); - if(plaintextLength < HEADER_LENGTH) - throw new IllegalArgumentException(); - if(plaintextLength > MAX_FRAME_LENGTH - MAC_LENGTH) - throw new IllegalArgumentException(); - ByteUtils.writeUint32(frameNumber, aad, 0); - ByteUtils.writeUint16(plaintextLength, aad, 4); + if(header) iv[4] = 1; + else iv[4] = 0; + for(int i = 5; i < IV_LENGTH; i++) iv[i] = 0; } static void encodeHeader(byte[] header, boolean finalFrame, - int payloadLength) { + int payloadLength, int paddingLength) { if(header.length < HEADER_LENGTH) throw new IllegalArgumentException(); - if(payloadLength < 0) - throw new IllegalArgumentException(); - if(payloadLength > MAX_FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH) + if(payloadLength < 0) throw new IllegalArgumentException(); + if(paddingLength < 0) throw new IllegalArgumentException(); + if(payloadLength + paddingLength > MAX_PAYLOAD_LENGTH) throw new IllegalArgumentException(); ByteUtils.writeUint16(payloadLength, header, 0); + ByteUtils.writeUint16(paddingLength, header, 2); if(finalFrame) header[0] |= 0x80; } @@ -51,4 +40,9 @@ class FrameEncoder { if(header.length < HEADER_LENGTH) throw new IllegalArgumentException(); return ByteUtils.readUint16(header, 0) & 0x7FFF; } + + static int getPaddingLength(byte[] header) { + if(header.length < HEADER_LENGTH) throw new IllegalArgumentException(); + return ByteUtils.readUint16(header, 2); + } } diff --git a/briar-core/src/org/briarproject/crypto/PasswordStrengthEstimatorImpl.java b/briar-core/src/org/briarproject/crypto/PasswordStrengthEstimatorImpl.java index aeec2ddc3bcc3127224796c101edd96c46efcc10..1fd99f52bfe2c8b79660a3be5df2e038c2daf6ad 100644 --- a/briar-core/src/org/briarproject/crypto/PasswordStrengthEstimatorImpl.java +++ b/briar-core/src/org/briarproject/crypto/PasswordStrengthEstimatorImpl.java @@ -13,9 +13,10 @@ class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator { private static final double STRONG = Math.log(Math.pow(LOWER + UPPER + DIGIT + OTHER, 10)); - public float estimateStrength(char[] password) { + public float estimateStrength(String password) { HashSet<Character> unique = new HashSet<Character>(); - for(char c : password) unique.add(c); + int length = password.length(); + for(int i = 0; i < length; i++) unique.add(password.charAt(i)); boolean lower = false, upper = false, digit = false, other = false; for(char c : unique) { if(Character.isLowerCase(c)) lower = true; diff --git a/briar-core/src/org/briarproject/crypto/SecretKeyImpl.java b/briar-core/src/org/briarproject/crypto/SecretKeyImpl.java deleted file mode 100644 index f37685cd17860279f57d6c2bf7af27c35ba203c6..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/crypto/SecretKeyImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.briarproject.crypto; - -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import org.briarproject.api.crypto.SecretKey; -import org.briarproject.util.ByteUtils; - -class SecretKeyImpl implements SecretKey { - - private final byte[] key; - - private boolean erased = false; - - private final Lock synchLock = new ReentrantLock(); - - SecretKeyImpl(byte[] key) { - this.key = key; - } - - public byte[] getEncoded() { - synchLock.lock(); - try{ - if(erased) throw new IllegalStateException(); - return key; - } - finally{ - synchLock.unlock(); - } - - } - - public SecretKey copy() { - return new SecretKeyImpl(key.clone()); - } - - public void erase() { - synchLock.lock(); - try{ - if(erased) throw new IllegalStateException(); - ByteUtils.erase(key); - erased = true; - } - finally{ - synchLock.unlock(); - } - } -} diff --git a/briar-core/src/org/briarproject/crypto/StreamDecrypterFactoryImpl.java b/briar-core/src/org/briarproject/crypto/StreamDecrypterFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..bed0503c244ace043a359662c0a3155225920a99 --- /dev/null +++ b/briar-core/src/org/briarproject/crypto/StreamDecrypterFactoryImpl.java @@ -0,0 +1,40 @@ +package org.briarproject.crypto; + +import java.io.InputStream; + +import javax.inject.Inject; + +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamDecrypter; +import org.briarproject.api.crypto.StreamDecrypterFactory; +import org.briarproject.api.transport.StreamContext; + +class StreamDecrypterFactoryImpl implements StreamDecrypterFactory { + + private final CryptoComponent crypto; + + @Inject + StreamDecrypterFactoryImpl(CryptoComponent crypto) { + this.crypto = crypto; + } + + public StreamDecrypter createStreamDecrypter(InputStream in, + StreamContext ctx) { + // Derive the frame key + byte[] secret = ctx.getSecret(); + long streamNumber = ctx.getStreamNumber(); + boolean alice = !ctx.getAlice(); + SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); + // Create the decrypter + return new StreamDecrypterImpl(in, crypto.getFrameCipher(), frameKey); + } + + public StreamDecrypter createInvitationStreamDecrypter(InputStream in, + byte[] secret, boolean alice) { + // Derive the frame key + SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice); + // Create the decrypter + return new StreamDecrypterImpl(in, crypto.getFrameCipher(), frameKey); + } +} diff --git a/briar-core/src/org/briarproject/crypto/StreamDecrypterImpl.java b/briar-core/src/org/briarproject/crypto/StreamDecrypterImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..b27b0cf9e44c33b0933f775696690b0aab1e58b6 --- /dev/null +++ b/briar-core/src/org/briarproject/crypto/StreamDecrypterImpl.java @@ -0,0 +1,97 @@ +package org.briarproject.crypto; + +import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.IV_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; + +import org.briarproject.api.FormatException; +import org.briarproject.api.crypto.AuthenticatedCipher; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamDecrypter; + +class StreamDecrypterImpl implements StreamDecrypter { + + private final InputStream in; + private final AuthenticatedCipher frameCipher; + private final SecretKey frameKey; + private final byte[] iv, aad, header, ciphertext; + + private long frameNumber; + private boolean finalFrame; + + StreamDecrypterImpl(InputStream in, AuthenticatedCipher frameCipher, + SecretKey frameKey) { + this.in = in; + this.frameCipher = frameCipher; + this.frameKey = frameKey; + iv = new byte[IV_LENGTH]; + aad = new byte[IV_LENGTH]; + header = new byte[HEADER_LENGTH]; + ciphertext = new byte[MAX_FRAME_LENGTH]; + frameNumber = 0; + finalFrame = false; + } + + public int readFrame(byte[] payload) throws IOException { + if(payload.length < MAX_PAYLOAD_LENGTH) + throw new IllegalArgumentException(); + if(finalFrame) return -1; + // Read the header + int offset = 0; + while(offset < HEADER_LENGTH) { + int read = in.read(ciphertext, offset, HEADER_LENGTH - offset); + if(read == -1) throw new EOFException(); + offset += read; + } + // Decrypt and authenticate the header + FrameEncoder.encodeIv(iv, frameNumber, true); + FrameEncoder.encodeIv(aad, frameNumber, true); + try { + frameCipher.init(false, frameKey, iv, aad); + int decrypted = frameCipher.process(ciphertext, 0, HEADER_LENGTH, + header, 0); + if(decrypted != HEADER_LENGTH - MAC_LENGTH) + throw new RuntimeException(); + } catch(GeneralSecurityException e) { + throw new FormatException(); + } + // Decode and validate the header + finalFrame = FrameEncoder.isFinalFrame(header); + int payloadLength = FrameEncoder.getPayloadLength(header); + int paddingLength = FrameEncoder.getPaddingLength(header); + if(payloadLength + paddingLength > MAX_PAYLOAD_LENGTH) + throw new FormatException(); + // Read the payload and padding + int frameLength = HEADER_LENGTH + payloadLength + paddingLength + + MAC_LENGTH; + while(offset < frameLength) { + int read = in.read(ciphertext, offset, frameLength - offset); + if(read == -1) throw new EOFException(); + offset += read; + } + // Decrypt and authenticate the payload and padding + FrameEncoder.encodeIv(iv, frameNumber, false); + FrameEncoder.encodeIv(aad, frameNumber, false); + try { + frameCipher.init(false, frameKey, iv, aad); + int decrypted = frameCipher.process(ciphertext, HEADER_LENGTH, + payloadLength + paddingLength + MAC_LENGTH, payload, 0); + if(decrypted != payloadLength + paddingLength) + throw new RuntimeException(); + } catch(GeneralSecurityException e) { + throw new FormatException(); + } + // If there's any padding it must be all zeroes + for(int i = 0; i < paddingLength; i++) + if(payload[payloadLength + i] != 0) throw new FormatException(); + frameNumber++; + return payloadLength; + } +} \ No newline at end of file diff --git a/briar-core/src/org/briarproject/crypto/StreamEncrypterFactoryImpl.java b/briar-core/src/org/briarproject/crypto/StreamEncrypterFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..ec8286e06100b00598b726925e6a4eb0eb096cc7 --- /dev/null +++ b/briar-core/src/org/briarproject/crypto/StreamEncrypterFactoryImpl.java @@ -0,0 +1,48 @@ +package org.briarproject.crypto; + +import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; + +import java.io.OutputStream; + +import javax.inject.Inject; + +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamEncrypter; +import org.briarproject.api.crypto.StreamEncrypterFactory; +import org.briarproject.api.transport.StreamContext; + +class StreamEncrypterFactoryImpl implements StreamEncrypterFactory { + + private final CryptoComponent crypto; + + @Inject + StreamEncrypterFactoryImpl(CryptoComponent crypto) { + this.crypto = crypto; + } + + public StreamEncrypter createStreamEncrypter(OutputStream out, + StreamContext ctx) { + byte[] secret = ctx.getSecret(); + long streamNumber = ctx.getStreamNumber(); + boolean alice = ctx.getAlice(); + // Encode the tag + byte[] tag = new byte[TAG_LENGTH]; + SecretKey tagKey = crypto.deriveTagKey(secret, alice); + crypto.encodeTag(tag, tagKey, streamNumber); + // Derive the frame key + SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); + // Create the encrypter + return new StreamEncrypterImpl(out, crypto.getFrameCipher(), frameKey, + tag); + } + + public StreamEncrypter createInvitationStreamEncrypter(OutputStream out, + byte[] secret, boolean alice) { + // Derive the frame key + SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice); + // Create the encrypter + return new StreamEncrypterImpl(out, crypto.getFrameCipher(), frameKey, + null); + } +} diff --git a/briar-core/src/org/briarproject/crypto/StreamEncrypterImpl.java b/briar-core/src/org/briarproject/crypto/StreamEncrypterImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..475c62f59fe8cec9994f8115ca8369b9c2465cbf --- /dev/null +++ b/briar-core/src/org/briarproject/crypto/StreamEncrypterImpl.java @@ -0,0 +1,97 @@ +package org.briarproject.crypto; + +import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.IV_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; +import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.GeneralSecurityException; + +import org.briarproject.api.crypto.AuthenticatedCipher; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamEncrypter; + +class StreamEncrypterImpl implements StreamEncrypter { + + private final OutputStream out; + private final AuthenticatedCipher frameCipher; + private final SecretKey frameKey; + private final byte[] tag, iv, aad, plaintext, ciphertext; + + private long frameNumber; + private boolean writeTag; + + StreamEncrypterImpl(OutputStream out, AuthenticatedCipher frameCipher, + SecretKey frameKey, byte[] tag) { + this.out = out; + this.frameCipher = frameCipher; + this.frameKey = frameKey; + this.tag = tag; + iv = new byte[IV_LENGTH]; + aad = new byte[IV_LENGTH]; + plaintext = new byte[HEADER_LENGTH + MAX_PAYLOAD_LENGTH]; + ciphertext = new byte[MAX_FRAME_LENGTH]; + frameNumber = 0; + writeTag = (tag != null); + } + + public void writeFrame(byte[] payload, int payloadLength, + int paddingLength, boolean finalFrame) throws IOException { + if(payloadLength + paddingLength > MAX_PAYLOAD_LENGTH) + throw new IllegalArgumentException(); + // Don't allow the frame counter to wrap + if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IOException(); + // Write the tag if required + if(writeTag) { + out.write(tag, 0, tag.length); + writeTag = false; + } + // Encode the header + FrameEncoder.encodeHeader(plaintext, finalFrame, payloadLength, + paddingLength); + // Encrypt and authenticate the header + FrameEncoder.encodeIv(iv, frameNumber, true); + FrameEncoder.encodeIv(aad, frameNumber, true); + try { + frameCipher.init(true, frameKey, iv, aad); + int encrypted = frameCipher.process(plaintext, 0, + HEADER_LENGTH - MAC_LENGTH, ciphertext, 0); + if(encrypted != HEADER_LENGTH) throw new RuntimeException(); + } catch(GeneralSecurityException badCipher) { + throw new RuntimeException(badCipher); + } + // Combine the payload and padding + System.arraycopy(payload, 0, plaintext, HEADER_LENGTH, payloadLength); + for(int i = 0; i < paddingLength; i++) + plaintext[HEADER_LENGTH + payloadLength + i] = 0; + // Encrypt and authenticate the payload and padding + FrameEncoder.encodeIv(iv, frameNumber, false); + FrameEncoder.encodeIv(aad, frameNumber, false); + try { + frameCipher.init(true, frameKey, iv, aad); + int encrypted = frameCipher.process(plaintext, HEADER_LENGTH, + payloadLength + paddingLength, ciphertext, HEADER_LENGTH); + if(encrypted != payloadLength + paddingLength + MAC_LENGTH) + throw new RuntimeException(); + } catch(GeneralSecurityException badCipher) { + throw new RuntimeException(badCipher); + } + // Write the frame + out.write(ciphertext, 0, HEADER_LENGTH + payloadLength + paddingLength + + MAC_LENGTH); + frameNumber++; + } + + public void flush() throws IOException { + // Write the tag if required + if(writeTag) { + out.write(tag, 0, tag.length); + writeTag = false; + } + out.flush(); + } +} \ No newline at end of file diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java index 5bce51798e1ce545ec8e51b5656a85006cf7239c..ba3c4ffafc2031fc19e8051a50db502013b7ebf3 100644 --- a/briar-core/src/org/briarproject/db/Database.java +++ b/briar-core/src/org/briarproject/db/Database.java @@ -152,7 +152,7 @@ interface Database<T> { * <p> * Locking: write. */ - boolean addTransport(T txn, TransportId t, long maxLatency) + boolean addTransport(T txn, TransportId t, int maxLatency) throws DbException; /** @@ -460,7 +460,7 @@ interface Database<T> { * <p> * Locking: write. */ - RetentionUpdate getRetentionUpdate(T txn, ContactId c, long maxLatency) + RetentionUpdate getRetentionUpdate(T txn, ContactId c, int maxLatency) throws DbException; /** @@ -499,7 +499,7 @@ interface Database<T> { * Locking: write. */ SubscriptionUpdate getSubscriptionUpdate(T txn, ContactId c, - long maxLatency) throws DbException; + int maxLatency) throws DbException; /** * Returns a collection of transport acks for the given contact, or null if @@ -511,11 +511,11 @@ interface Database<T> { throws DbException; /** - * Returns the maximum latencies of all local transports. + * Returns the maximum latencies of all supported transports. * <p> * Locking: read. */ - Map<TransportId, Long> getTransportLatencies(T txn) throws DbException; + Map<TransportId, Integer> getTransportLatencies(T txn) throws DbException; /** * Returns a collection of transport updates for the given contact and @@ -525,7 +525,7 @@ interface Database<T> { * Locking: write. */ Collection<TransportUpdate> getTransportUpdates(T txn, ContactId c, - long maxLatency) throws DbException; + int maxLatency) throws DbException; /** * Returns the number of unread messages in each subscribed group. @@ -798,6 +798,6 @@ interface Database<T> { * <p> * Locking: write. */ - void updateExpiryTime(T txn, ContactId c, MessageId m, long maxLatency) + void updateExpiryTime(T txn, ContactId c, MessageId m, int maxLatency) throws DbException; } diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index c22643f0be075806ecc43d52659faddb86a11bf5..1c8bc564d7cc6cbdedfef4a15bbcae1605d07d91 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -314,7 +314,7 @@ DatabaseCleaner.Callback { } } - public boolean addTransport(TransportId t, long maxLatency) + public boolean addTransport(TransportId t, int maxLatency) throws DbException { boolean added; lock.writeLock().lock(); @@ -357,7 +357,7 @@ DatabaseCleaner.Callback { } public Collection<byte[]> generateBatch(ContactId c, int maxLength, - long maxLatency) throws DbException { + int maxLatency) throws DbException { Collection<MessageId> ids; List<byte[]> messages = new ArrayList<byte[]>(); lock.writeLock().lock(); @@ -384,7 +384,7 @@ DatabaseCleaner.Callback { return Collections.unmodifiableList(messages); } - public Offer generateOffer(ContactId c, int maxMessages, long maxLatency) + public Offer generateOffer(ContactId c, int maxMessages, int maxLatency) throws DbException { Collection<MessageId> ids; lock.writeLock().lock(); @@ -432,7 +432,7 @@ DatabaseCleaner.Callback { } public Collection<byte[]> generateRequestedBatch(ContactId c, int maxLength, - long maxLatency) throws DbException { + int maxLatency) throws DbException { Collection<MessageId> ids; List<byte[]> messages = new ArrayList<byte[]>(); lock.writeLock().lock(); @@ -478,7 +478,7 @@ DatabaseCleaner.Callback { } } - public RetentionUpdate generateRetentionUpdate(ContactId c, long maxLatency) + public RetentionUpdate generateRetentionUpdate(ContactId c, int maxLatency) throws DbException { lock.writeLock().lock(); try { @@ -519,7 +519,7 @@ DatabaseCleaner.Callback { } public SubscriptionUpdate generateSubscriptionUpdate(ContactId c, - long maxLatency) throws DbException { + int maxLatency) throws DbException { lock.writeLock().lock(); try { T txn = db.startTransaction(); @@ -560,7 +560,7 @@ DatabaseCleaner.Callback { } public Collection<TransportUpdate> generateTransportUpdates(ContactId c, - long maxLatency) throws DbException { + int maxLatency) throws DbException { lock.writeLock().lock(); try { T txn = db.startTransaction(); @@ -932,12 +932,13 @@ DatabaseCleaner.Callback { } } - public Map<TransportId, Long> getTransportLatencies() throws DbException { + public Map<TransportId, Integer> getTransportLatencies() + throws DbException { lock.readLock().lock(); try { T txn = db.startTransaction(); try { - Map<TransportId, Long> latencies = + Map<TransportId, Integer> latencies = db.getTransportLatencies(txn); db.commitTransaction(txn); return latencies; diff --git a/briar-core/src/org/briarproject/db/ExponentialBackoff.java b/briar-core/src/org/briarproject/db/ExponentialBackoff.java index 15444d11c15b52103e0222ca8d3fe20e4f0ea007..5248e8e50c888005bf0b275ad256462512b09199 100644 --- a/briar-core/src/org/briarproject/db/ExponentialBackoff.java +++ b/briar-core/src/org/briarproject/db/ExponentialBackoff.java @@ -11,13 +11,12 @@ class ExponentialBackoff { * transmissions increases exponentially. If the expiry time would * be greater than Long.MAX_VALUE, Long.MAX_VALUE is returned. */ - static long calculateExpiry(long now, long maxLatency, int txCount) { + static long calculateExpiry(long now, int maxLatency, int txCount) { if(now < 0) throw new IllegalArgumentException(); if(maxLatency <= 0) throw new IllegalArgumentException(); if(txCount < 0) throw new IllegalArgumentException(); // The maximum round-trip time is twice the maximum latency - long roundTrip = maxLatency * 2; - if(roundTrip < 0) return Long.MAX_VALUE; + long roundTrip = maxLatency * 2L; // The interval between transmissions is roundTrip * 2 ^ txCount for(int i = 0; i < txCount; i++) { roundTrip <<= 1; diff --git a/briar-core/src/org/briarproject/db/H2Database.java b/briar-core/src/org/briarproject/db/H2Database.java index d7f064ddb26b7f1c18a372504e1c640728b128e1..fc91e20dca23aab698dfa8ad5435d34f7898e28d 100644 --- a/briar-core/src/org/briarproject/db/H2Database.java +++ b/briar-core/src/org/briarproject/db/H2Database.java @@ -81,34 +81,18 @@ class H2Database extends JdbcDatabase { } } + @Override protected Connection createConnection() throws SQLException { byte[] key = config.getEncryptionKey(); if(key == null) throw new IllegalStateException(); - char[] password = encodePassword(key); Properties props = new Properties(); props.setProperty("user", "user"); - props.put("password", password); - try { - return DriverManager.getConnection(url, props); - } finally { - for(int i = 0; i < password.length; i++) password[i] = 0; - } - } - - private char[] encodePassword(byte[] key) { - // The database password is the hex-encoded key - char[] hex = StringUtils.toHexChars(key); - // Separate the database password from the user password with a space - char[] user = "password".toCharArray(); - char[] combined = new char[hex.length + 1 + user.length]; - System.arraycopy(hex, 0, combined, 0, hex.length); - combined[hex.length] = ' '; - System.arraycopy(user, 0, combined, hex.length + 1, user.length); - // Erase the hex-encoded key - for(int i = 0; i < hex.length; i++) hex[i] = 0; - return combined; + // Separate the file password from the user password with a space + props.put("password", StringUtils.toHexString(key) + " password"); + return DriverManager.getConnection(url, props); } + @Override protected void flushBuffersToDisk(Statement s) throws SQLException { // FIXME: Remove this after implementing BTPv2? s.execute("CHECKPOINT SYNC"); diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java index 57b596f7efdde420370cd9a60471b19534ad02a9..189add0cac1e8af5dc159eb969f2b8a977b43469 100644 --- a/briar-core/src/org/briarproject/db/JdbcDatabase.java +++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java @@ -65,8 +65,8 @@ import org.briarproject.api.transport.TemporarySecret; */ abstract class JdbcDatabase implements Database<Connection> { - private static final int SCHEMA_VERSION = 6; - private static final int MIN_SCHEMA_VERSION = 5; + private static final int SCHEMA_VERSION = 7; + private static final int MIN_SCHEMA_VERSION = 7; private static final String CREATE_SETTINGS = "CREATE TABLE settings" @@ -216,7 +216,7 @@ abstract class JdbcDatabase implements Database<Connection> { private static final String CREATE_TRANSPORTS = "CREATE TABLE transports" + " (transportId VARCHAR NOT NULL," - + " maxLatency BIGINT NOT NULL," + + " maxLatency INT NOT NULL," + " PRIMARY KEY (transportId))"; private static final String CREATE_TRANSPORT_CONFIGS = @@ -897,7 +897,7 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public boolean addTransport(Connection txn, TransportId t, long maxLatency) + public boolean addTransport(Connection txn, TransportId t, int maxLatency) throws DbException { PreparedStatement ps = null; ResultSet rs = null; @@ -2055,7 +2055,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public RetentionUpdate getRetentionUpdate(Connection txn, ContactId c, - long maxLatency) throws DbException { + int maxLatency) throws DbException { long now = clock.currentTimeMillis(); PreparedStatement ps = null; ResultSet rs = null; @@ -2233,7 +2233,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public SubscriptionUpdate getSubscriptionUpdate(Connection txn, ContactId c, - long maxLatency) throws DbException { + int maxLatency) throws DbException { long now = clock.currentTimeMillis(); PreparedStatement ps = null; ResultSet rs = null; @@ -2327,7 +2327,7 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public Map<TransportId, Long> getTransportLatencies(Connection txn) + public Map<TransportId, Integer> getTransportLatencies(Connection txn) throws DbException { PreparedStatement ps = null; ResultSet rs = null; @@ -2335,10 +2335,11 @@ abstract class JdbcDatabase implements Database<Connection> { String sql = "SELECT transportId, maxLatency FROM transports"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); - Map<TransportId, Long> latencies = new HashMap<TransportId, Long>(); + Map<TransportId, Integer> latencies = + new HashMap<TransportId, Integer>(); while(rs.next()){ TransportId id = new TransportId(rs.getString(1)); - latencies.put(id, rs.getLong(2)); + latencies.put(id, rs.getInt(2)); } rs.close(); ps.close(); @@ -2351,7 +2352,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public Collection<TransportUpdate> getTransportUpdates(Connection txn, - ContactId c, long maxLatency) throws DbException { + ContactId c, int maxLatency) throws DbException { long now = clock.currentTimeMillis(); PreparedStatement ps = null; ResultSet rs = null; @@ -3332,7 +3333,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public void updateExpiryTime(Connection txn, ContactId c, MessageId m, - long maxLatency) throws DbException { + int maxLatency) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { diff --git a/briar-core/src/org/briarproject/invitation/AliceConnector.java b/briar-core/src/org/briarproject/invitation/AliceConnector.java index 9c7fd31f3aa0d0adf90501dbb6b2170954998d20..f641af40a65124ff9df9ae4584910aa95fd1f52e 100644 --- a/briar-core/src/org/briarproject/invitation/AliceConnector.java +++ b/briar-core/src/org/briarproject/invitation/AliceConnector.java @@ -29,9 +29,7 @@ import org.briarproject.api.serial.ReaderFactory; import org.briarproject.api.serial.Writer; import org.briarproject.api.serial.WriterFactory; import org.briarproject.api.system.Clock; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; /** A connection thread for the peer being Alice in the invitation protocol. */ @@ -51,9 +49,9 @@ class AliceConnector extends Connector { Map<TransportId, TransportProperties> localProps, PseudoRandom random) { super(crypto, db, readerFactory, writerFactory, streamReaderFactory, - streamWriterFactory, authorFactory, groupFactory, - keyManager, connectionManager, clock, reuseConnection, group, - plugin, localAuthor, localProps, random); + streamWriterFactory, authorFactory, groupFactory, keyManager, + connectionManager, clock, reuseConnection, group, plugin, + localAuthor, localProps, random); } @Override @@ -130,15 +128,16 @@ class AliceConnector extends Connector { // Confirmation succeeded - upgrade to a secure connection if(LOG.isLoggable(INFO)) LOG.info(pluginName + " confirmation succeeded"); - int maxFrameLength = conn.getReader().getMaxFrameLength(); - StreamReader streamReader = + // Create the readers + InputStream streamReader = streamReaderFactory.createInvitationStreamReader(in, - maxFrameLength, secret, false); // Bob's stream - r = readerFactory.createReader(streamReader.getInputStream()); - StreamWriter streamWriter = + secret, false); // Bob's stream + r = readerFactory.createReader(streamReader); + // Create the writers + OutputStream streamWriter = streamWriterFactory.createInvitationStreamWriter(out, - maxFrameLength, secret, true); // Alice's stream - w = writerFactory.createWriter(streamWriter.getOutputStream()); + secret, true); // Alice's stream + w = writerFactory.createWriter(streamWriter); // Derive the invitation nonces byte[][] nonces = crypto.deriveInvitationNonces(secret); byte[] aliceNonce = nonces[0], bobNonce = nonces[1]; diff --git a/briar-core/src/org/briarproject/invitation/BobConnector.java b/briar-core/src/org/briarproject/invitation/BobConnector.java index 205b3446ddbbb54d0dcb0080531318cc0d679ab1..dae8691920e1acf62cfc81c4af54feae020ba704 100644 --- a/briar-core/src/org/briarproject/invitation/BobConnector.java +++ b/briar-core/src/org/briarproject/invitation/BobConnector.java @@ -29,9 +29,7 @@ import org.briarproject.api.serial.ReaderFactory; import org.briarproject.api.serial.Writer; import org.briarproject.api.serial.WriterFactory; import org.briarproject.api.system.Clock; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; /** A connection thread for the peer being Bob in the invitation protocol. */ @@ -51,9 +49,9 @@ class BobConnector extends Connector { Map<TransportId, TransportProperties> localProps, PseudoRandom random) { super(crypto, db, readerFactory, writerFactory, streamReaderFactory, - streamWriterFactory, authorFactory, groupFactory, - keyManager, connectionManager, clock, reuseConnection, group, - plugin, localAuthor, localProps, random); + streamWriterFactory, authorFactory, groupFactory, keyManager, + connectionManager, clock, reuseConnection, group, plugin, + localAuthor, localProps, random); } @Override @@ -130,15 +128,16 @@ class BobConnector extends Connector { // Confirmation succeeded - upgrade to a secure connection if(LOG.isLoggable(INFO)) LOG.info(pluginName + " confirmation succeeded"); - int maxFrameLength = conn.getReader().getMaxFrameLength(); - StreamReader streamReader = + // Create the readers + InputStream streamReader = streamReaderFactory.createInvitationStreamReader(in, - maxFrameLength, secret, true); // Alice's stream - r = readerFactory.createReader(streamReader.getInputStream()); - StreamWriter streamWriter = + secret, true); // Alice's stream + r = readerFactory.createReader(streamReader); + // Create the writers + OutputStream streamWriter = streamWriterFactory.createInvitationStreamWriter(out, - maxFrameLength, secret, false); // Bob's stream - w = writerFactory.createWriter(streamWriter.getOutputStream()); + secret, false); // Bob's stream + w = writerFactory.createWriter(streamWriter); // Derive the nonces byte[][] nonces = crypto.deriveInvitationNonces(secret); byte[] aliceNonce = nonces[0], bobNonce = nonces[1]; diff --git a/briar-core/src/org/briarproject/invitation/Connector.java b/briar-core/src/org/briarproject/invitation/Connector.java index 9c0144620286c09c6bc48f28de6faf93b322434f..1fa00401217ad5717afd9f7f086adc47bb916d6f 100644 --- a/briar-core/src/org/briarproject/invitation/Connector.java +++ b/briar-core/src/org/briarproject/invitation/Connector.java @@ -285,7 +285,7 @@ abstract class Connector extends Thread { db.setRemoteProperties(contactId, remoteProps); // Create an endpoint for each transport shared with the contact List<TransportId> ids = new ArrayList<TransportId>(); - Map<TransportId, Long> latencies = db.getTransportLatencies(); + Map<TransportId, Integer> latencies = db.getTransportLatencies(); for(TransportId id : localProps.keySet()) { if(latencies.containsKey(id) && remoteProps.containsKey(id)) ids.add(id); @@ -296,7 +296,7 @@ abstract class Connector extends Thread { for(int i = 0; i < size; i++) { TransportId id = ids.get(i); Endpoint ep = new Endpoint(contactId, id, epoch, alice); - long maxLatency = latencies.get(id); + int maxLatency = latencies.get(id); try { db.addEndpoint(ep); } catch(NoSuchTransportException e) { diff --git a/briar-core/src/org/briarproject/messaging/DuplexOutgoingSession.java b/briar-core/src/org/briarproject/messaging/DuplexOutgoingSession.java index a656c94587b84f0296cbde6487e52c04cae0ba70..23177097f912900e73d18b20c320528d6aa5bda6 100644 --- a/briar-core/src/org/briarproject/messaging/DuplexOutgoingSession.java +++ b/briar-core/src/org/briarproject/messaging/DuplexOutgoingSession.java @@ -1,11 +1,11 @@ package org.briarproject.messaging; +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.api.messaging.MessagingConstants.MAX_PACKET_LENGTH; import java.io.IOException; -import java.io.OutputStream; import java.util.Collection; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; @@ -36,7 +36,6 @@ import org.briarproject.api.messaging.Ack; import org.briarproject.api.messaging.MessagingSession; import org.briarproject.api.messaging.Offer; import org.briarproject.api.messaging.PacketWriter; -import org.briarproject.api.messaging.PacketWriterFactory; import org.briarproject.api.messaging.Request; import org.briarproject.api.messaging.RetentionAck; import org.briarproject.api.messaging.RetentionUpdate; @@ -44,16 +43,18 @@ import org.briarproject.api.messaging.SubscriptionAck; import org.briarproject.api.messaging.SubscriptionUpdate; import org.briarproject.api.messaging.TransportAck; import org.briarproject.api.messaging.TransportUpdate; +import org.briarproject.api.system.Clock; /** * An outgoing {@link org.briarproject.api.messaging.MessagingSession * MessagingSession} suitable for duplex transports. The session offers * messages before sending them, keeps its output stream open when there are no - * more packets to send, and reacts to events that make packets available to - * send. + * packets to send, and reacts to events that make packets available to send. */ class DuplexOutgoingSession implements MessagingSession, EventListener { + // Check for retransmittable packets once every 60 seconds + private static final int RETX_QUERY_INTERVAL = 60 * 1000; private static final Logger LOG = Logger.getLogger(DuplexOutgoingSession.class.getName()); @@ -65,27 +66,32 @@ class DuplexOutgoingSession implements MessagingSession, EventListener { private final DatabaseComponent db; private final Executor dbExecutor; private final EventBus eventBus; + private final Clock clock; private final ContactId contactId; private final TransportId transportId; - private final long maxLatency; - private final OutputStream out; + private final int maxLatency, maxIdleTime; private final PacketWriter packetWriter; private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks; + // The following must only be accessed on the writer thread + private long nextKeepalive = 0, nextRetxQuery = 0; + private boolean dataToFlush = true; + private volatile boolean interrupted = false; DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, - EventBus eventBus, PacketWriterFactory packetWriterFactory, - ContactId contactId, TransportId transportId, long maxLatency, - OutputStream out) { + EventBus eventBus, Clock clock, ContactId contactId, + TransportId transportId, int maxLatency, int maxIdleTime, + PacketWriter packetWriter) { this.db = db; this.dbExecutor = dbExecutor; this.eventBus = eventBus; + this.clock = clock; this.contactId = contactId; this.transportId = transportId; this.maxLatency = maxLatency; - this.out = out; - packetWriter = packetWriterFactory.createPacketWriter(out); + this.maxIdleTime = maxIdleTime; + this.packetWriter = packetWriter; writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>(); } @@ -103,16 +109,50 @@ class DuplexOutgoingSession implements MessagingSession, EventListener { dbExecutor.execute(new GenerateBatch()); dbExecutor.execute(new GenerateOffer()); dbExecutor.execute(new GenerateRequest()); + long now = clock.currentTimeMillis(); + nextKeepalive = now + maxIdleTime; + nextRetxQuery = now + RETX_QUERY_INTERVAL; // Write packets until interrupted try { while(!interrupted) { - // Flush the stream if it's going to be idle - if(writerTasks.isEmpty()) out.flush(); - ThrowingRunnable<IOException> task = writerTasks.take(); - if(task == CLOSE) break; - task.run(); + // Work out how long we should wait for a packet + now = clock.currentTimeMillis(); + long wait = Math.min(nextKeepalive, nextRetxQuery) - now; + if(wait < 0) wait = 0; + // Flush any unflushed data if we're going to wait + if(wait > 0 && dataToFlush && writerTasks.isEmpty()) { + packetWriter.flush(); + dataToFlush = false; + nextKeepalive = now + maxIdleTime; + } + // Wait for a packet + ThrowingRunnable<IOException> task = writerTasks.poll(wait, + MILLISECONDS); + if(task == null) { + now = clock.currentTimeMillis(); + if(now >= nextRetxQuery) { + // Check for retransmittable packets + dbExecutor.execute(new GenerateTransportUpdates()); + dbExecutor.execute(new GenerateSubscriptionUpdate()); + dbExecutor.execute(new GenerateRetentionUpdate()); + dbExecutor.execute(new GenerateBatch()); + dbExecutor.execute(new GenerateOffer()); + nextRetxQuery = now + RETX_QUERY_INTERVAL; + } + if(now >= nextKeepalive) { + // Flush the stream to keep it alive + packetWriter.flush(); + dataToFlush = false; + nextKeepalive = now + maxIdleTime; + } + } else if(task == CLOSE) { + break; + } else { + task.run(); + dataToFlush = true; + } } - out.flush(); + if(dataToFlush) packetWriter.flush(); } catch(InterruptedException e) { LOG.info("Interrupted while waiting for a packet to write"); Thread.currentThread().interrupt(); diff --git a/briar-core/src/org/briarproject/messaging/IncomingSession.java b/briar-core/src/org/briarproject/messaging/IncomingSession.java index b85ccda0db618ae8aef73072f6d1602913f8f91a..1bd8acd66cbdeee28ade9ef0537cbf1a715e174e 100644 --- a/briar-core/src/org/briarproject/messaging/IncomingSession.java +++ b/briar-core/src/org/briarproject/messaging/IncomingSession.java @@ -3,7 +3,6 @@ package org.briarproject.messaging; import static java.util.logging.Level.WARNING; import java.io.IOException; -import java.io.InputStream; import java.security.GeneralSecurityException; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -25,7 +24,6 @@ import org.briarproject.api.messaging.MessageVerifier; import org.briarproject.api.messaging.MessagingSession; import org.briarproject.api.messaging.Offer; import org.briarproject.api.messaging.PacketReader; -import org.briarproject.api.messaging.PacketReaderFactory; import org.briarproject.api.messaging.Request; import org.briarproject.api.messaging.RetentionAck; import org.briarproject.api.messaging.RetentionUpdate; @@ -56,9 +54,8 @@ class IncomingSession implements MessagingSession, EventListener { IncomingSession(DatabaseComponent db, Executor dbExecutor, Executor cryptoExecutor, EventBus eventBus, - MessageVerifier messageVerifier, - PacketReaderFactory packetReaderFactory, ContactId contactId, - TransportId transportId, InputStream in) { + MessageVerifier messageVerifier, ContactId contactId, + TransportId transportId, PacketReader packetReader) { this.db = db; this.dbExecutor = dbExecutor; this.cryptoExecutor = cryptoExecutor; @@ -66,7 +63,7 @@ class IncomingSession implements MessagingSession, EventListener { this.messageVerifier = messageVerifier; this.contactId = contactId; this.transportId = transportId; - packetReader = packetReaderFactory.createPacketReader(in); + this.packetReader = packetReader; } public void run() throws IOException { diff --git a/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java b/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java index 04895580ccdd61b606970c60e30c672c5ffbfdb5..c7a31f6094642f983c2549ff460b989c3579f6c5 100644 --- a/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java +++ b/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java @@ -15,8 +15,11 @@ import org.briarproject.api.event.EventBus; import org.briarproject.api.messaging.MessageVerifier; import org.briarproject.api.messaging.MessagingSession; import org.briarproject.api.messaging.MessagingSessionFactory; +import org.briarproject.api.messaging.PacketReader; import org.briarproject.api.messaging.PacketReaderFactory; +import org.briarproject.api.messaging.PacketWriter; import org.briarproject.api.messaging.PacketWriterFactory; +import org.briarproject.api.system.Clock; class MessagingSessionFactoryImpl implements MessagingSessionFactory { @@ -24,6 +27,7 @@ class MessagingSessionFactoryImpl implements MessagingSessionFactory { private final Executor dbExecutor, cryptoExecutor; private final MessageVerifier messageVerifier; private final EventBus eventBus; + private final Clock clock; private final PacketReaderFactory packetReaderFactory; private final PacketWriterFactory packetWriterFactory; @@ -31,7 +35,7 @@ class MessagingSessionFactoryImpl implements MessagingSessionFactory { MessagingSessionFactoryImpl(DatabaseComponent db, @DatabaseExecutor Executor dbExecutor, @CryptoExecutor Executor cryptoExecutor, - MessageVerifier messageVerifier, EventBus eventBus, + MessageVerifier messageVerifier, EventBus eventBus, Clock clock, PacketReaderFactory packetReaderFactory, PacketWriterFactory packetWriterFactory) { this.db = db; @@ -39,21 +43,29 @@ class MessagingSessionFactoryImpl implements MessagingSessionFactory { this.cryptoExecutor = cryptoExecutor; this.messageVerifier = messageVerifier; this.eventBus = eventBus; + this.clock = clock; this.packetReaderFactory = packetReaderFactory; this.packetWriterFactory = packetWriterFactory; } public MessagingSession createIncomingSession(ContactId c, TransportId t, InputStream in) { + PacketReader packetReader = packetReaderFactory.createPacketReader(in); return new IncomingSession(db, dbExecutor, cryptoExecutor, eventBus, - messageVerifier, packetReaderFactory, c, t, in); + messageVerifier, c, t, packetReader); } - public MessagingSession createOutgoingSession(ContactId c, TransportId t, - long maxLatency, boolean duplex, OutputStream out) { - if(duplex) return new DuplexOutgoingSession(db, dbExecutor, eventBus, - packetWriterFactory, c, t, maxLatency, out); - else return new SimplexOutgoingSession(db, dbExecutor, eventBus, - packetWriterFactory, c, t, maxLatency, out); + public MessagingSession createSimplexOutgoingSession(ContactId c, + TransportId t, int maxLatency, OutputStream out) { + PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out); + return new SimplexOutgoingSession(db, dbExecutor, eventBus, c, t, + maxLatency, packetWriter); + } + + public MessagingSession createDuplexOutgoingSession(ContactId c, + TransportId t, int maxLatency, int maxIdleTime, OutputStream out) { + PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out); + return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c, t, + maxLatency, maxIdleTime, packetWriter); } } diff --git a/briar-core/src/org/briarproject/messaging/PacketWriterImpl.java b/briar-core/src/org/briarproject/messaging/PacketWriterImpl.java index 4b0efd4a7ea5fcec06f1be25205502458717b7bf..1ef8e348c92c4efc555f94c909c757fb674d079f 100644 --- a/briar-core/src/org/briarproject/messaging/PacketWriterImpl.java +++ b/briar-core/src/org/briarproject/messaging/PacketWriterImpl.java @@ -143,4 +143,8 @@ class PacketWriterImpl implements PacketWriter { w.writeInteger(u.getVersion()); w.writeStructEnd(); } + + public void flush() throws IOException { + out.flush(); + } } diff --git a/briar-core/src/org/briarproject/messaging/SimplexOutgoingSession.java b/briar-core/src/org/briarproject/messaging/SimplexOutgoingSession.java index 51ec4d81a49d124a5d2db805b9c47f12c1070870..6d8dfabf1ae815bdebaba6fef642a40717c7368f 100644 --- a/briar-core/src/org/briarproject/messaging/SimplexOutgoingSession.java +++ b/briar-core/src/org/briarproject/messaging/SimplexOutgoingSession.java @@ -5,7 +5,6 @@ import static java.util.logging.Level.WARNING; import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH; import java.io.IOException; -import java.io.OutputStream; import java.util.Collection; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; @@ -26,7 +25,6 @@ import org.briarproject.api.event.TransportRemovedEvent; import org.briarproject.api.messaging.Ack; import org.briarproject.api.messaging.MessagingSession; import org.briarproject.api.messaging.PacketWriter; -import org.briarproject.api.messaging.PacketWriterFactory; import org.briarproject.api.messaging.RetentionAck; import org.briarproject.api.messaging.RetentionUpdate; import org.briarproject.api.messaging.SubscriptionAck; @@ -55,8 +53,7 @@ class SimplexOutgoingSession implements MessagingSession, EventListener { private final EventBus eventBus; private final ContactId contactId; private final TransportId transportId; - private final long maxLatency; - private final OutputStream out; + private final int maxLatency; private final PacketWriter packetWriter; private final AtomicInteger outstandingQueries; private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks; @@ -64,17 +61,15 @@ class SimplexOutgoingSession implements MessagingSession, EventListener { private volatile boolean interrupted = false; SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, - EventBus eventBus, PacketWriterFactory packetWriterFactory, - ContactId contactId, TransportId transportId, long maxLatency, - OutputStream out) { + EventBus eventBus, ContactId contactId, TransportId transportId, + int maxLatency, PacketWriter packetWriter) { this.db = db; this.dbExecutor = dbExecutor; this.eventBus = eventBus; this.contactId = contactId; this.transportId = transportId; this.maxLatency = maxLatency; - this.out = out; - packetWriter = packetWriterFactory.createPacketWriter(out); + this.packetWriter = packetWriter; outstandingQueries = new AtomicInteger(8); // One per type of packet writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>(); } @@ -98,7 +93,7 @@ class SimplexOutgoingSession implements MessagingSession, EventListener { if(task == CLOSE) break; task.run(); } - out.flush(); + packetWriter.flush(); } catch(InterruptedException e) { LOG.info("Interrupted while waiting for a packet to write"); Thread.currentThread().interrupt(); diff --git a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java index d94e1f80ec0a562b5b6eed312ef4120267efd4ad..b7cb70c1bd8fc9b90560145603d1d49722c8992c 100644 --- a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java @@ -6,6 +6,7 @@ import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -24,12 +25,9 @@ import org.briarproject.api.plugins.TransportConnectionReader; import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.api.transport.TagRecogniser; -import org.briarproject.util.ByteUtils; class ConnectionManagerImpl implements ConnectionManager { @@ -96,28 +94,28 @@ class ConnectionManagerImpl implements ConnectionManager { private MessagingSession createIncomingSession(StreamContext ctx, TransportConnectionReader r) throws IOException { - try { - StreamReader streamReader = streamReaderFactory.createStreamReader( - r.getInputStream(), r.getMaxFrameLength(), ctx); - return messagingSessionFactory.createIncomingSession( - ctx.getContactId(), ctx.getTransportId(), - streamReader.getInputStream()); - } finally { - ByteUtils.erase(ctx.getSecret()); - } + InputStream streamReader = streamReaderFactory.createStreamReader( + r.getInputStream(), ctx); + return messagingSessionFactory.createIncomingSession( + ctx.getContactId(), ctx.getTransportId(), streamReader); } - private MessagingSession createOutgoingSession(StreamContext ctx, - TransportConnectionWriter w, boolean duplex) throws IOException { - try { - StreamWriter streamWriter = streamWriterFactory.createStreamWriter( - w.getOutputStream(), w.getMaxFrameLength(), ctx); - return messagingSessionFactory.createOutgoingSession( - ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(), - duplex, streamWriter.getOutputStream()); - } finally { - ByteUtils.erase(ctx.getSecret()); - } + private MessagingSession createSimplexOutgoingSession(StreamContext ctx, + TransportConnectionWriter w) throws IOException { + OutputStream streamWriter = streamWriterFactory.createStreamWriter( + w.getOutputStream(), ctx); + return messagingSessionFactory.createSimplexOutgoingSession( + ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(), + streamWriter); + } + + private MessagingSession createDuplexOutgoingSession(StreamContext ctx, + TransportConnectionWriter w) throws IOException { + OutputStream streamWriter = streamWriterFactory.createStreamWriter( + w.getOutputStream(), ctx); + return messagingSessionFactory.createDuplexOutgoingSession( + ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(), + w.getMaxIdleTime(), streamWriter); } private class ManageIncomingSimplexConnection implements Runnable { @@ -199,7 +197,7 @@ class ConnectionManagerImpl implements ConnectionManager { connectionRegistry.registerConnection(contactId, transportId); try { // Create and run the outgoing session - createOutgoingSession(ctx, writer, false).run(); + createSimplexOutgoingSession(ctx, writer).run(); disposeWriter(false); } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); @@ -287,7 +285,7 @@ class ConnectionManagerImpl implements ConnectionManager { } try { // Create and run the outgoing session - outgoingSession = createOutgoingSession(ctx, writer, true); + outgoingSession = createDuplexOutgoingSession(ctx, writer); outgoingSession.run(); disposeWriter(false); } catch(IOException e) { @@ -353,7 +351,7 @@ class ConnectionManagerImpl implements ConnectionManager { }); try { // Create and run the outgoing session - outgoingSession = createOutgoingSession(ctx, writer, true); + outgoingSession = createDuplexOutgoingSession(ctx, writer); outgoingSession.run(); disposeWriter(false); } catch(IOException e) { diff --git a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java index 1af4215ba6afb3fdf547cebc033cb07dde57e157..abaa6349256d34c3d8e663922f78e2d28e5b74dd 100644 --- a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java +++ b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java @@ -28,8 +28,7 @@ public abstract class FilePlugin implements SimplexPlugin { protected final Executor ioExecutor; protected final FileUtils fileUtils; protected final SimplexPluginCallback callback; - protected final int maxFrameLength; - protected final long maxLatency; + protected final int maxLatency; protected volatile boolean running = false; @@ -39,21 +38,19 @@ public abstract class FilePlugin implements SimplexPlugin { protected abstract void readerFinished(File f); protected FilePlugin(Executor ioExecutor, FileUtils fileUtils, - SimplexPluginCallback callback, int maxFrameLength, - long maxLatency) { + SimplexPluginCallback callback, int maxLatency) { this.ioExecutor = ioExecutor; this.fileUtils = fileUtils; this.callback = callback; - this.maxFrameLength = maxFrameLength; this.maxLatency = maxLatency; } - public int getMaxFrameLength() { - return maxFrameLength; + public int getMaxLatency() { + return maxLatency; } - public long getMaxLatency() { - return maxLatency; + public int getMaxIdleTime() { + return Integer.MAX_VALUE; // We don't need keepalives } public boolean isRunning() { diff --git a/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java b/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java index 316773ec01270fc0b45282edd62a6b40785985e5..4c434faa621a94788ad9b0f3649fb61518081de3 100644 --- a/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java +++ b/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java @@ -24,10 +24,6 @@ class FileTransportReader implements TransportConnectionReader { this.plugin = plugin; } - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); - } - public long getMaxLatency() { return plugin.getMaxLatency(); } diff --git a/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java b/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java index 2ca55593fc6cdb63b0789a4e9dac14a233b335d4..f9b14651e1e4a3ed8b4a3913faf3511f7795bd3d 100644 --- a/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java +++ b/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java @@ -27,12 +27,12 @@ class FileTransportWriter implements TransportConnectionWriter { this.plugin = plugin; } - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); + public int getMaxLatency() { + return plugin.getMaxLatency(); } - public long getMaxLatency() { - return plugin.getMaxLatency(); + public int getMaxIdleTime() { + return plugin.getMaxIdleTime(); } public long getCapacity() { diff --git a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java index 39e4b94184f48dce3b6c073903713b7a59f34aba..ea030c40ee629ed527a9393dc51fd04bb5a9a713 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java @@ -17,9 +17,8 @@ class LanTcpPlugin extends TcpPlugin { static final TransportId ID = new TransportId("lan"); LanTcpPlugin(Executor ioExecutor, DuplexPluginCallback callback, - int maxFrameLength, long maxLatency, long pollingInterval) { - super(ioExecutor, callback, maxFrameLength, maxLatency, - pollingInterval); + int maxLatency, int maxIdleTime, int pollingInterval) { + super(ioExecutor, callback, maxLatency, maxIdleTime, pollingInterval); } public TransportId getId() { diff --git a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java index 02b53eab951ec8326377073e2a01bdc5d5b2559d..68f2edc0e77aab769e242a3e63f4334376ac5083 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java +++ b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPluginFactory.java @@ -9,9 +9,9 @@ import org.briarproject.api.plugins.duplex.DuplexPluginFactory; public class LanTcpPluginFactory implements DuplexPluginFactory { - private static final int MAX_FRAME_LENGTH = 1024; - private static final long MAX_LATENCY = 60 * 1000; // 1 minute - private static final long POLLING_INTERVAL = 60 * 1000; // 1 minute + private static final int MAX_LATENCY = 30 * 1000; // 30 seconds + private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds + private static final int POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes private final Executor ioExecutor; @@ -24,7 +24,7 @@ public class LanTcpPluginFactory implements DuplexPluginFactory { } public DuplexPlugin createPlugin(DuplexPluginCallback callback) { - return new LanTcpPlugin(ioExecutor, callback, MAX_FRAME_LENGTH, - MAX_LATENCY, POLLING_INTERVAL); + return new LanTcpPlugin(ioExecutor, callback, MAX_LATENCY, + MAX_IDLE_TIME, POLLING_INTERVAL); } } diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java index a8d3e8dde839007fbe89ff25e6a22bed292627c4..083f46598d04095aa9e3a094901462f3c421afef 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java @@ -37,8 +37,7 @@ abstract class TcpPlugin implements DuplexPlugin { protected final Executor ioExecutor; protected final DuplexPluginCallback callback; - protected final int maxFrameLength; - protected final long maxLatency, pollingInterval; + protected final int maxLatency, maxIdleTime, pollingInterval, socketTimeout; protected volatile boolean running = false; protected volatile ServerSocket socket = null; @@ -53,20 +52,23 @@ abstract class TcpPlugin implements DuplexPlugin { protected abstract boolean isConnectable(InetSocketAddress remote); protected TcpPlugin(Executor ioExecutor, DuplexPluginCallback callback, - int maxFrameLength, long maxLatency, long pollingInterval) { + int maxLatency, int maxIdleTime, int pollingInterval) { this.ioExecutor = ioExecutor; this.callback = callback; - this.maxFrameLength = maxFrameLength; this.maxLatency = maxLatency; + this.maxIdleTime = maxIdleTime; this.pollingInterval = pollingInterval; + if(maxIdleTime > Integer.MAX_VALUE / 2) + socketTimeout = Integer.MAX_VALUE; + else socketTimeout = maxIdleTime * 2; } - public int getMaxFrameLength() { - return maxFrameLength; + public int getMaxLatency() { + return maxLatency; } - public long getMaxLatency() { - return maxLatency; + public int getMaxIdleTime() { + return maxIdleTime; } public boolean start() { @@ -136,6 +138,7 @@ abstract class TcpPlugin implements DuplexPlugin { Socket s; try { s = socket.accept(); + s.setSoTimeout(socketTimeout); } catch(IOException e) { // This is expected when the socket is closed if(LOG.isLoggable(INFO)) LOG.info(e.toString()); @@ -161,7 +164,7 @@ abstract class TcpPlugin implements DuplexPlugin { return true; } - public long getPollingInterval() { + public int getPollingInterval() { return pollingInterval; } @@ -195,6 +198,7 @@ abstract class TcpPlugin implements DuplexPlugin { try { if(LOG.isLoggable(INFO)) LOG.info("Connecting to " + remote); s.connect(remote); + s.setSoTimeout(socketTimeout); if(LOG.isLoggable(INFO)) LOG.info("Connected to " + remote); return new TcpTransportConnection(this, s); } catch(IOException e) { diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java b/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java index 83506a211682a611ddf4a6918079336e4530eaa4..aae3c53ba86ba2232d0f8313f0ce27983d058ea4 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java +++ b/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java @@ -38,10 +38,6 @@ class TcpTransportConnection implements DuplexTransportConnection { private class Reader implements TransportConnectionReader { - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); - } - public long getMaxLatency() { return plugin.getMaxLatency(); } @@ -59,12 +55,12 @@ class TcpTransportConnection implements DuplexTransportConnection { private class Writer implements TransportConnectionWriter { - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); + public int getMaxLatency() { + return plugin.getMaxLatency(); } - public long getMaxLatency() { - return plugin.getMaxLatency(); + public int getMaxIdleTime() { + return plugin.getMaxIdleTime(); } public long getCapacity() { diff --git a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java index 6e2e3e754cf814278a0929ad5363db17d50bb466..15a4dfde82ebfc8e4094decafecb92ad257915b5 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java @@ -20,11 +20,10 @@ class WanTcpPlugin extends TcpPlugin { private volatile MappingResult mappingResult; - WanTcpPlugin(Executor ioExecutor, DuplexPluginCallback callback, - int maxFrameLength, long maxLatency, long pollingInterval, - PortMapper portMapper) { - super(ioExecutor, callback, maxFrameLength, maxLatency, - pollingInterval); + WanTcpPlugin(Executor ioExecutor, PortMapper portMapper, + DuplexPluginCallback callback, int maxLatency, int maxIdleTime, + int pollingInterval) { + super(ioExecutor, callback, maxLatency, maxIdleTime, pollingInterval); this.portMapper = portMapper; } diff --git a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java index f478bbc295fe13b3825536309fadf3be9f590dcc..bd326ebbc0e8281f47511ceae223cc536caaa25d 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java +++ b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPluginFactory.java @@ -10,9 +10,9 @@ import org.briarproject.api.plugins.duplex.DuplexPluginFactory; public class WanTcpPluginFactory implements DuplexPluginFactory { - private static final int MAX_FRAME_LENGTH = 1024; - private static final long MAX_LATENCY = 60 * 1000; // 1 minute - private static final long POLLING_INTERVAL = 5 * 60 * 1000; // 5 minutes + private static final int MAX_LATENCY = 30 * 1000; // 30 seconds + private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds + private static final int POLLING_INTERVAL = 5 * 60 * 1000; // 5 minutes private final Executor ioExecutor; private final ShutdownManager shutdownManager; @@ -28,8 +28,7 @@ public class WanTcpPluginFactory implements DuplexPluginFactory { } public DuplexPlugin createPlugin(DuplexPluginCallback callback) { - return new WanTcpPlugin(ioExecutor, callback, MAX_FRAME_LENGTH, - MAX_LATENCY, POLLING_INTERVAL, - new PortMapperImpl(shutdownManager)); + return new WanTcpPlugin(ioExecutor, new PortMapperImpl(shutdownManager), + callback, MAX_LATENCY, MAX_IDLE_TIME, POLLING_INTERVAL); } } diff --git a/briar-core/src/org/briarproject/transport/FrameReader.java b/briar-core/src/org/briarproject/transport/FrameReader.java deleted file mode 100644 index 8284c47b8d4e322a8f18cc57ae071d8f2bef97c5..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/transport/FrameReader.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.briarproject.transport; - -import java.io.IOException; - -interface FrameReader { - - /** - * Reads a frame into the given buffer and returns its payload length, or - * -1 if no more frames can be read from the connection. - */ - int readFrame(byte[] frame) throws IOException; -} diff --git a/briar-core/src/org/briarproject/transport/FrameWriter.java b/briar-core/src/org/briarproject/transport/FrameWriter.java deleted file mode 100644 index 4f29b7999a08fa1c7964fc65140124232f0b9028..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/transport/FrameWriter.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.briarproject.transport; - -import java.io.IOException; - -interface FrameWriter { - - /** Writes the given frame. */ - void writeFrame(byte[] frame, int payloadLength, boolean finalFrame) - throws IOException; - - /** Flushes the stream. */ - void flush() throws IOException; -} diff --git a/briar-core/src/org/briarproject/transport/IncomingEncryptionLayer.java b/briar-core/src/org/briarproject/transport/IncomingEncryptionLayer.java deleted file mode 100644 index e5e4381d8cfcccf4af9444bc798026687995edf5..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/transport/IncomingEncryptionLayer.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.briarproject.transport; - -import static org.briarproject.api.transport.TransportConstants.AAD_LENGTH; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.IV_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.security.GeneralSecurityException; - -import org.briarproject.api.FormatException; -import org.briarproject.api.crypto.AuthenticatedCipher; -import org.briarproject.api.crypto.SecretKey; - -class IncomingEncryptionLayer implements FrameReader { - - private final InputStream in; - private final AuthenticatedCipher frameCipher; - private final SecretKey frameKey; - private final byte[] iv, aad, ciphertext; - private final int frameLength; - - private long frameNumber; - private boolean finalFrame; - - IncomingEncryptionLayer(InputStream in, AuthenticatedCipher frameCipher, - SecretKey frameKey, int frameLength) { - this.in = in; - this.frameCipher = frameCipher; - this.frameKey = frameKey; - this.frameLength = frameLength; - iv = new byte[IV_LENGTH]; - aad = new byte[AAD_LENGTH]; - ciphertext = new byte[frameLength]; - frameNumber = 0; - finalFrame = false; - } - - public int readFrame(byte[] frame) throws IOException { - if(finalFrame) return -1; - // Read the frame - int ciphertextLength = 0; - try { - while(ciphertextLength < frameLength) { - int read = in.read(ciphertext, ciphertextLength, - frameLength - ciphertextLength); - if(read == -1) break; // We'll check the length later - ciphertextLength += read; - } - } catch(IOException e) { - frameKey.erase(); - throw e; - } - int plaintextLength = ciphertextLength - MAC_LENGTH; - if(plaintextLength < HEADER_LENGTH) throw new EOFException(); - // Decrypt and authenticate the frame - FrameEncoder.encodeIv(iv, frameNumber); - FrameEncoder.encodeAad(aad, frameNumber, plaintextLength); - try { - frameCipher.init(false, frameKey, iv, aad); - int decrypted = frameCipher.doFinal(ciphertext, 0, ciphertextLength, - frame, 0); - if(decrypted != plaintextLength) throw new RuntimeException(); - } catch(GeneralSecurityException e) { - throw new FormatException(); - } - // Decode and validate the header - finalFrame = FrameEncoder.isFinalFrame(frame); - if(!finalFrame && ciphertextLength < frameLength) - throw new FormatException(); - int payloadLength = FrameEncoder.getPayloadLength(frame); - if(payloadLength > plaintextLength - HEADER_LENGTH) - throw new FormatException(); - // If there's any padding it must be all zeroes - for(int i = HEADER_LENGTH + payloadLength; i < plaintextLength; i++) { - if(frame[i] != 0) throw new FormatException(); - } - frameNumber++; - return payloadLength; - } -} \ No newline at end of file diff --git a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java index 6ab1688506300266c960a55e95ab3b5534e8ea1b..d6ec3aa675244c7e491ec0ea8ea59bb0a4564ca2 100644 --- a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java +++ b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java @@ -35,7 +35,6 @@ import org.briarproject.api.transport.Endpoint; import org.briarproject.api.transport.StreamContext; import org.briarproject.api.transport.TagRecogniser; import org.briarproject.api.transport.TemporarySecret; -import org.briarproject.util.ByteUtils; // FIXME: Don't make alien calls with a lock held class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { @@ -52,7 +51,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { private final Clock clock; private final Timer timer; - private final Map<TransportId, Long> maxLatencies; + private final Map<TransportId, Integer> maxLatencies; private final Map<EndpointKey, TemporarySecret> oldSecrets; private final Map<EndpointKey, TemporarySecret> currentSecrets; private final Map<EndpointKey, TemporarySecret> newSecrets; @@ -69,7 +68,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { this.tagRecogniser = tagRecogniser; this.clock = clock; this.timer = timer; - maxLatencies = new HashMap<TransportId, Long>(); + maxLatencies = new HashMap<TransportId, Integer>(); oldSecrets = new HashMap<EndpointKey, TemporarySecret>(); currentSecrets = new HashMap<EndpointKey, TemporarySecret>(); newSecrets = new HashMap<EndpointKey, TemporarySecret>(); @@ -124,10 +123,9 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { Collection<TemporarySecret> dead = new ArrayList<TemporarySecret>(); for(TemporarySecret s : secrets) { // Discard the secret if the transport has been removed - Long maxLatency = maxLatencies.get(s.getTransportId()); + Integer maxLatency = maxLatencies.get(s.getTransportId()); if(maxLatency == null) { LOG.info("Discarding obsolete secret"); - ByteUtils.erase(s.getSecret()); continue; } long rotation = maxLatency + MAX_CLOCK_DIFFERENCE; @@ -151,7 +149,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { return dead; } - // Replaces and erases the given secrets and returns any secrets created + // Replaces the given secrets and returns any secrets created private Collection<TemporarySecret> replaceDeadSecrets(long now, Collection<TemporarySecret> dead) { // If there are several dead secrets for an endpoint, use the newest @@ -164,18 +162,16 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { // There's no other secret for this endpoint newest.put(k, s); } else if(exists.getPeriod() < s.getPeriod()) { - // There's an older secret - erase it and use this one instead - ByteUtils.erase(exists.getSecret()); + // There's an older secret - use this one instead newest.put(k, s); } else { - // There's a newer secret - erase this one - ByteUtils.erase(s.getSecret()); + // There's a newer secret - keep using it } } Collection<TemporarySecret> created = new ArrayList<TemporarySecret>(); for(Entry<EndpointKey, TemporarySecret> e : newest.entrySet()) { TemporarySecret s = e.getValue(); - Long maxLatency = maxLatencies.get(s.getTransportId()); + Integer maxLatency = maxLatencies.get(s.getTransportId()); if(maxLatency == null) throw new IllegalStateException(); // Work out which rotation period we're in long elapsed = now - s.getEpoch(); @@ -186,34 +182,23 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { throw new IllegalStateException(); // Derive the old, current and new secrets byte[] b1 = s.getSecret(); - for(long p = s.getPeriod() + 1; p < period; p++) { - byte[] temp = crypto.deriveNextSecret(b1, p); - ByteUtils.erase(b1); - b1 = temp; - } + for(long p = s.getPeriod() + 1; p < period; p++) + b1 = crypto.deriveNextSecret(b1, p); byte[] b2 = crypto.deriveNextSecret(b1, period); byte[] b3 = crypto.deriveNextSecret(b2, period + 1); - // Add the secrets to their respective maps - copies may already - // exist, in which case erase the new copies (the old copies are - // referenced by the connection recogniser) + // Add the secrets to their respective maps if not already present EndpointKey k = e.getKey(); - if(oldSecrets.containsKey(k)) { - ByteUtils.erase(b1); - } else { + if(!oldSecrets.containsKey(k)) { TemporarySecret s1 = new TemporarySecret(s, period - 1, b1); oldSecrets.put(k, s1); created.add(s1); } - if(currentSecrets.containsKey(k)) { - ByteUtils.erase(b2); - } else { + if(!currentSecrets.containsKey(k)) { TemporarySecret s2 = new TemporarySecret(s, period, b2); currentSecrets.put(k, s2); created.add(s2); } - if(newSecrets.containsKey(k)) { - ByteUtils.erase(b3); - } else { + if(!newSecrets.containsKey(k)) { TemporarySecret s3 = new TemporarySecret(s, period + 1, b3); newSecrets.put(k, s3); created.add(s3); @@ -229,9 +214,9 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { timer.cancel(); tagRecogniser.removeSecrets(); maxLatencies.clear(); - removeAndEraseSecrets(oldSecrets); - removeAndEraseSecrets(currentSecrets); - removeAndEraseSecrets(newSecrets); + oldSecrets.clear(); + currentSecrets.clear(); + newSecrets.clear(); return true; } finally{ @@ -239,11 +224,6 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { } } - private void removeAndEraseSecrets(Map<?, TemporarySecret> m) { - for(TemporarySecret s : m.values()) ByteUtils.erase(s.getSecret()); - m.clear(); - } - public StreamContext getStreamContext(ContactId c, TransportId t) { synchLock.lock(); @@ -264,8 +244,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); return null; } - // Clone the secret - the original will be erased - byte[] secret = s.getSecret().clone(); + byte[] secret = s.getSecret(); return new StreamContext(c, t, secret, streamNumber, s.getAlice()); } finally{ @@ -273,7 +252,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { } } - public void endpointAdded(Endpoint ep, long maxLatency, + public synchronized void endpointAdded(Endpoint ep, int maxLatency, byte[] initialSecret) { synchLock.lock(); try{ @@ -285,11 +264,8 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { if(period < 1) throw new IllegalStateException(); // Derive the old, current and new secrets byte[] b1 = initialSecret; - for(long p = 0; p < period; p++) { - byte[] temp = crypto.deriveNextSecret(b1, p); - ByteUtils.erase(b1); - b1 = temp; - } + for(long p = 0; p < period; p++) + b1 = crypto.deriveNextSecret(b1, p); byte[] b2 = crypto.deriveNextSecret(b1, period); byte[] b3 = crypto.deriveNextSecret(b2, period + 1); TemporarySecret s1 = new TemporarySecret(ep, period - 1, b1); @@ -370,27 +346,16 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { } } - private void removeAndEraseSecrets(ContactId c, Map<?, TemporarySecret> m) { + private void removeSecrets(ContactId c, Map<?, TemporarySecret> m) { Iterator<TemporarySecret> it = m.values().iterator(); - while(it.hasNext()) { - TemporarySecret s = it.next(); - if(s.getContactId().equals(c)) { - ByteUtils.erase(s.getSecret()); - it.remove(); - } - } + while(it.hasNext()) + if(it.next().getContactId().equals(c)) it.remove(); } - private void removeAndEraseSecrets(TransportId t, - Map<?, TemporarySecret> m) { + private void removeSecrets(TransportId t, Map<?, TemporarySecret> m) { Iterator<TemporarySecret> it = m.values().iterator(); - while(it.hasNext()) { - TemporarySecret s = it.next(); - if(s.getTransportId().equals(t)) { - ByteUtils.erase(s.getSecret()); - it.remove(); - } - } + while(it.hasNext()) + if(it.next().getTransportId().equals(t)) it.remove(); } private static class EndpointKey { @@ -436,10 +401,10 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { ContactId c = event.getContactId(); tagRecogniser.removeSecrets(c); synchLock.lock(); - try { - removeAndEraseSecrets(c, oldSecrets); - removeAndEraseSecrets(c, currentSecrets); - removeAndEraseSecrets(c, newSecrets); + try{ + removeSecrets(c, oldSecrets); + removeSecrets(c, currentSecrets); + removeSecrets(c, newSecrets); } finally{ synchLock.unlock(); @@ -482,9 +447,9 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { synchLock.lock(); try { maxLatencies.remove(t); - removeAndEraseSecrets(t, oldSecrets); - removeAndEraseSecrets(t, currentSecrets); - removeAndEraseSecrets(t, newSecrets); + removeSecrets(t, oldSecrets); + removeSecrets(t, currentSecrets); + removeSecrets(t, newSecrets); } finally{ synchLock.unlock(); diff --git a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java.orig b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java.orig new file mode 100644 index 0000000000000000000000000000000000000000..88863dadaa19b1fdbec84aed9fa57809942fb228 --- /dev/null +++ b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java.orig @@ -0,0 +1,546 @@ +package org.briarproject.transport; + +import static java.util.logging.Level.WARNING; +import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TimerTask; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import org.briarproject.api.ContactId; +import org.briarproject.api.TransportId; +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.crypto.KeyManager; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.DbException; +import org.briarproject.api.event.ContactRemovedEvent; +import org.briarproject.api.event.Event; +import org.briarproject.api.event.EventBus; +import org.briarproject.api.event.EventListener; +import org.briarproject.api.event.TransportAddedEvent; +import org.briarproject.api.event.TransportRemovedEvent; +import org.briarproject.api.system.Clock; +import org.briarproject.api.system.Timer; +import org.briarproject.api.transport.Endpoint; +import org.briarproject.api.transport.StreamContext; +import org.briarproject.api.transport.TagRecogniser; +import org.briarproject.api.transport.TemporarySecret; + +// FIXME: Don't make alien calls with a lock held +class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { + + private static final int MS_BETWEEN_CHECKS = 60 * 1000; + + private static final Logger LOG = + Logger.getLogger(KeyManagerImpl.class.getName()); + + private final CryptoComponent crypto; + private final DatabaseComponent db; + private final EventBus eventBus; + private final TagRecogniser tagRecogniser; + private final Clock clock; + private final Timer timer; + +<<<<<<< HEAD + private final Map<TransportId, Long> maxLatencies; +======= + // All of the following are locking: this + private final Map<TransportId, Integer> maxLatencies; +>>>>>>> theSource + private final Map<EndpointKey, TemporarySecret> oldSecrets; + private final Map<EndpointKey, TemporarySecret> currentSecrets; + private final Map<EndpointKey, TemporarySecret> newSecrets; + + private final Lock synchLock = new ReentrantLock(); + + @Inject + KeyManagerImpl(CryptoComponent crypto, DatabaseComponent db, + EventBus eventBus, TagRecogniser tagRecogniser, Clock clock, + Timer timer) { + this.crypto = crypto; + this.db = db; + this.eventBus = eventBus; + this.tagRecogniser = tagRecogniser; + this.clock = clock; + this.timer = timer; + maxLatencies = new HashMap<TransportId, Integer>(); + oldSecrets = new HashMap<EndpointKey, TemporarySecret>(); + currentSecrets = new HashMap<EndpointKey, TemporarySecret>(); + newSecrets = new HashMap<EndpointKey, TemporarySecret>(); + } + + public boolean start() { + synchLock.lock(); + try { + eventBus.addListener(this); + // Load the temporary secrets and transport latencies from the database + Collection<TemporarySecret> secrets; + try { + secrets = db.getSecrets(); + maxLatencies.putAll(db.getTransportLatencies()); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + return false; + } + // Work out what phase of its lifecycle each secret is in + long now = clock.currentTimeMillis(); + Collection<TemporarySecret> dead = assignSecretsToMaps(now, secrets); + // Replace any dead secrets + Collection<TemporarySecret> created = replaceDeadSecrets(now, dead); + if(!created.isEmpty()) { + // Store any secrets that have been created, removing any dead ones + try { + db.addSecrets(created); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + return false; + } + } + // Pass the old, current and new secrets to the recogniser + for(TemporarySecret s : oldSecrets.values()) + tagRecogniser.addSecret(s); + for(TemporarySecret s : currentSecrets.values()) + tagRecogniser.addSecret(s); + for(TemporarySecret s : newSecrets.values()) + tagRecogniser.addSecret(s); + // Schedule periodic key rotation + timer.scheduleAtFixedRate(this, MS_BETWEEN_CHECKS, MS_BETWEEN_CHECKS); + return true; + } + finally{ + synchLock.unlock(); + } + } + + // Assigns secrets to the appropriate maps and returns any dead secrets + private Collection<TemporarySecret> assignSecretsToMaps(long now, + Collection<TemporarySecret> secrets) { + Collection<TemporarySecret> dead = new ArrayList<TemporarySecret>(); + for(TemporarySecret s : secrets) { + // Discard the secret if the transport has been removed + Integer maxLatency = maxLatencies.get(s.getTransportId()); + if(maxLatency == null) { + LOG.info("Discarding obsolete secret"); + continue; + } + long rotation = maxLatency + MAX_CLOCK_DIFFERENCE; + long creationTime = s.getEpoch() + rotation * (s.getPeriod() - 2); + long activationTime = creationTime + rotation; + long deactivationTime = activationTime + rotation; + long destructionTime = deactivationTime + rotation; + if(now >= destructionTime) { + dead.add(s); + } else if(now >= deactivationTime) { + oldSecrets.put(new EndpointKey(s), s); + } else if(now >= activationTime) { + currentSecrets.put(new EndpointKey(s), s); + } else if(now >= creationTime) { + newSecrets.put(new EndpointKey(s), s); + } else { + // FIXME: Work out what to do here + throw new Error("Clock has moved backwards"); + } + } + return dead; + } + +<<<<<<< HEAD + // Replaces and erases the given secrets and returns any secrets created +======= + // Replaces the given secrets and returns any secrets created + // Locking: this +>>>>>>> theSource + private Collection<TemporarySecret> replaceDeadSecrets(long now, + Collection<TemporarySecret> dead) { + // If there are several dead secrets for an endpoint, use the newest + Map<EndpointKey, TemporarySecret> newest = + new HashMap<EndpointKey, TemporarySecret>(); + for(TemporarySecret s : dead) { + EndpointKey k = new EndpointKey(s); + TemporarySecret exists = newest.get(k); + if(exists == null) { + // There's no other secret for this endpoint + newest.put(k, s); + } else if(exists.getPeriod() < s.getPeriod()) { + // There's an older secret - use this one instead + newest.put(k, s); + } else { + // There's a newer secret - keep using it + } + } + Collection<TemporarySecret> created = new ArrayList<TemporarySecret>(); + for(Entry<EndpointKey, TemporarySecret> e : newest.entrySet()) { + TemporarySecret s = e.getValue(); + Integer maxLatency = maxLatencies.get(s.getTransportId()); + if(maxLatency == null) throw new IllegalStateException(); + // Work out which rotation period we're in + long elapsed = now - s.getEpoch(); + long rotation = maxLatency + MAX_CLOCK_DIFFERENCE; + long period = (elapsed / rotation) + 1; + if(period < 1) throw new IllegalStateException(); + if(period - s.getPeriod() < 2) + throw new IllegalStateException(); + // Derive the old, current and new secrets + byte[] b1 = s.getSecret(); + for(long p = s.getPeriod() + 1; p < period; p++) + b1 = crypto.deriveNextSecret(b1, p); + byte[] b2 = crypto.deriveNextSecret(b1, period); + byte[] b3 = crypto.deriveNextSecret(b2, period + 1); + // Add the secrets to their respective maps if not already present + EndpointKey k = e.getKey(); + if(!oldSecrets.containsKey(k)) { + TemporarySecret s1 = new TemporarySecret(s, period - 1, b1); + oldSecrets.put(k, s1); + created.add(s1); + } + if(!currentSecrets.containsKey(k)) { + TemporarySecret s2 = new TemporarySecret(s, period, b2); + currentSecrets.put(k, s2); + created.add(s2); + } + if(!newSecrets.containsKey(k)) { + TemporarySecret s3 = new TemporarySecret(s, period + 1, b3); + newSecrets.put(k, s3); + created.add(s3); + } + } + return created; + } + +<<<<<<< HEAD + public boolean stop() { + synchLock.lock(); + try{ + eventBus.removeListener(this); + timer.cancel(); + tagRecogniser.removeSecrets(); + maxLatencies.clear(); + removeAndEraseSecrets(oldSecrets); + removeAndEraseSecrets(currentSecrets); + removeAndEraseSecrets(newSecrets); + return true; + } + finally{ + synchLock.unlock(); + } + } + + private void removeAndEraseSecrets(Map<?, TemporarySecret> m) { + for(TemporarySecret s : m.values()) ByteUtils.erase(s.getSecret()); + m.clear(); + } + + public StreamContext getStreamContext(ContactId c, +======= + public synchronized boolean stop() { + eventBus.removeListener(this); + timer.cancel(); + tagRecogniser.removeSecrets(); + maxLatencies.clear(); + oldSecrets.clear(); + currentSecrets.clear(); + newSecrets.clear(); + return true; + } + + public synchronized StreamContext getStreamContext(ContactId c, +>>>>>>> theSource + TransportId t) { + synchLock.lock(); + try{ + TemporarySecret s = currentSecrets.get(new EndpointKey(c, t)); + if(s == null) { + LOG.info("No secret for endpoint"); + return null; + } + long streamNumber; + try { + streamNumber = db.incrementStreamCounter(c, t, s.getPeriod()); + if(streamNumber == -1) { + LOG.info("No counter for period"); + return null; + } + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + return null; + } + // Clone the secret - the original will be erased + byte[] secret = s.getSecret().clone(); + return new StreamContext(c, t, secret, streamNumber, s.getAlice()); + } + finally{ + synchLock.unlock(); + } +<<<<<<< HEAD + } + + public void endpointAdded(Endpoint ep, long maxLatency, + byte[] initialSecret) { + synchLock.lock(); + try{ + maxLatencies.put(ep.getTransportId(), maxLatency); + // Work out which rotation period we're in + long elapsed = clock.currentTimeMillis() - ep.getEpoch(); + long rotation = maxLatency + MAX_CLOCK_DIFFERENCE; + long period = (elapsed / rotation) + 1; + if(period < 1) throw new IllegalStateException(); + // Derive the old, current and new secrets + byte[] b1 = initialSecret; + for(long p = 0; p < period; p++) { + byte[] temp = crypto.deriveNextSecret(b1, p); + ByteUtils.erase(b1); + b1 = temp; + } + byte[] b2 = crypto.deriveNextSecret(b1, period); + byte[] b3 = crypto.deriveNextSecret(b2, period + 1); + TemporarySecret s1 = new TemporarySecret(ep, period - 1, b1); + TemporarySecret s2 = new TemporarySecret(ep, period, b2); + TemporarySecret s3 = new TemporarySecret(ep, period + 1, b3); + // Add the incoming secrets to their respective maps + EndpointKey k = new EndpointKey(ep); + oldSecrets.put(k, s1); + currentSecrets.put(k, s2); + newSecrets.put(k, s3); + // Store the new secrets + try { + db.addSecrets(Arrays.asList(s1, s2, s3)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + return; + } + // Pass the new secrets to the recogniser + tagRecogniser.addSecret(s1); + tagRecogniser.addSecret(s2); + tagRecogniser.addSecret(s3); + } + finally{ + synchLock.unlock(); +======= + byte[] secret = s.getSecret(); + return new StreamContext(c, t, secret, streamNumber, s.getAlice()); + } + + public synchronized void endpointAdded(Endpoint ep, int maxLatency, + byte[] initialSecret) { + maxLatencies.put(ep.getTransportId(), maxLatency); + // Work out which rotation period we're in + long elapsed = clock.currentTimeMillis() - ep.getEpoch(); + long rotation = maxLatency + MAX_CLOCK_DIFFERENCE; + long period = (elapsed / rotation) + 1; + if(period < 1) throw new IllegalStateException(); + // Derive the old, current and new secrets + byte[] b1 = initialSecret; + for(long p = 0; p < period; p++) + b1 = crypto.deriveNextSecret(b1, p); + byte[] b2 = crypto.deriveNextSecret(b1, period); + byte[] b3 = crypto.deriveNextSecret(b2, period + 1); + TemporarySecret s1 = new TemporarySecret(ep, period - 1, b1); + TemporarySecret s2 = new TemporarySecret(ep, period, b2); + TemporarySecret s3 = new TemporarySecret(ep, period + 1, b3); + // Add the incoming secrets to their respective maps + EndpointKey k = new EndpointKey(ep); + oldSecrets.put(k, s1); + currentSecrets.put(k, s2); + newSecrets.put(k, s3); + // Store the new secrets + try { + db.addSecrets(Arrays.asList(s1, s2, s3)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + return; +>>>>>>> theSource + } + } + + @Override + public void run() { + synchLock.lock(); + try{ + // Rebuild the maps because we may be running a whole period late + Collection<TemporarySecret> secrets = new ArrayList<TemporarySecret>(); + secrets.addAll(oldSecrets.values()); + secrets.addAll(currentSecrets.values()); + secrets.addAll(newSecrets.values()); + oldSecrets.clear(); + currentSecrets.clear(); + newSecrets.clear(); + // Work out what phase of its lifecycle each secret is in + long now = clock.currentTimeMillis(); + Collection<TemporarySecret> dead = assignSecretsToMaps(now, secrets); + // Remove any dead secrets from the recogniser + for(TemporarySecret s : dead) { + ContactId c = s.getContactId(); + TransportId t = s.getTransportId(); + long period = s.getPeriod(); + tagRecogniser.removeSecret(c, t, period); + } + // Replace any dead secrets + Collection<TemporarySecret> created = replaceDeadSecrets(now, dead); + if(!created.isEmpty()) { + // Store any secrets that have been created + try { + db.addSecrets(created); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } + // Pass any secrets that have been created to the recogniser + for(TemporarySecret s : created) tagRecogniser.addSecret(s); + } + } + finally{ + synchLock.unlock(); + } + } + + public void eventOccurred(Event e) { + if(e instanceof ContactRemovedEvent) { + ContactRemovedEvent c = (ContactRemovedEvent) e; + timer.schedule(new ContactRemovedTask(c), 0); + } else if(e instanceof TransportAddedEvent) { + TransportAddedEvent t = (TransportAddedEvent) e; + timer.schedule(new TransportAddedTask(t), 0); + } else if(e instanceof TransportRemovedEvent) { + TransportRemovedEvent t = (TransportRemovedEvent) e; + timer.schedule(new TransportRemovedTask(t), 0); + } + } + +<<<<<<< HEAD + private void removeAndEraseSecrets(ContactId c, Map<?, TemporarySecret> m) { +======= + // Locking: this + private void removeSecrets(ContactId c, Map<?, TemporarySecret> m) { +>>>>>>> theSource + Iterator<TemporarySecret> it = m.values().iterator(); + while(it.hasNext()) + if(it.next().getContactId().equals(c)) it.remove(); + } + +<<<<<<< HEAD + private void removeAndEraseSecrets(TransportId t, + Map<?, TemporarySecret> m) { +======= + // Locking: this + private void removeSecrets(TransportId t, Map<?, TemporarySecret> m) { +>>>>>>> theSource + Iterator<TemporarySecret> it = m.values().iterator(); + while(it.hasNext()) + if(it.next().getTransportId().equals(t)) it.remove(); + } + + private static class EndpointKey { + + private final ContactId contactId; + private final TransportId transportId; + + private EndpointKey(ContactId contactId, TransportId transportId) { + this.contactId = contactId; + this.transportId = transportId; + } + + private EndpointKey(Endpoint ep) { + this(ep.getContactId(), ep.getTransportId()); + } + + @Override + public int hashCode() { + return contactId.hashCode() ^ transportId.hashCode(); + } + + @Override + public boolean equals(Object o) { + if(o instanceof EndpointKey) { + EndpointKey k = (EndpointKey) o; + return contactId.equals(k.contactId) && + transportId.equals(k.transportId); + } + return false; + } + } + + private class ContactRemovedTask extends TimerTask { + + private final ContactRemovedEvent event; + + private ContactRemovedTask(ContactRemovedEvent event) { + this.event = event; + } + + @Override + public void run() { + ContactId c = event.getContactId(); + tagRecogniser.removeSecrets(c); +<<<<<<< HEAD + synchLock.lock(); + try { + removeAndEraseSecrets(c, oldSecrets); + removeAndEraseSecrets(c, currentSecrets); + removeAndEraseSecrets(c, newSecrets); +======= + synchronized(KeyManagerImpl.this) { + removeSecrets(c, oldSecrets); + removeSecrets(c, currentSecrets); + removeSecrets(c, newSecrets); +>>>>>>> theSource + } + finally{ + synchLock.unlock(); + } + } + } + + private class TransportAddedTask extends TimerTask { + + private final TransportAddedEvent event; + + private TransportAddedTask(TransportAddedEvent event) { + this.event = event; + } + + @Override + public void run() { + synchLock.lock(); + try { + maxLatencies.put(event.getTransportId(), event.getMaxLatency()); + } + finally{ + synchLock.unlock(); + } + } + } + + private class TransportRemovedTask extends TimerTask { + + private TransportRemovedEvent event; + + private TransportRemovedTask(TransportRemovedEvent event) { + this.event = event; + } + + @Override + public void run() { + TransportId t = event.getTransportId(); + tagRecogniser.removeSecrets(t); + synchLock.lock(); + try { + maxLatencies.remove(t); + removeSecrets(t, oldSecrets); + removeSecrets(t, currentSecrets); + removeSecrets(t, newSecrets); + } + finally{ + synchLock.unlock(); + } + } + } +} diff --git a/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java b/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java deleted file mode 100644 index 1bb90c1e8cf51d321fe8318a557052eb7d045d3a..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.briarproject.transport; - -import static org.briarproject.api.transport.TransportConstants.AAD_LENGTH; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.IV_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; -import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED; - -import java.io.IOException; -import java.io.OutputStream; -import java.security.GeneralSecurityException; - -import org.briarproject.api.crypto.AuthenticatedCipher; -import org.briarproject.api.crypto.SecretKey; - -class OutgoingEncryptionLayer implements FrameWriter { - - private final OutputStream out; - private final AuthenticatedCipher frameCipher; - private final SecretKey frameKey; - private final byte[] tag, iv, aad, ciphertext; - private final int frameLength; - - private long frameNumber; - private boolean writeTag; - - OutgoingEncryptionLayer(OutputStream out, AuthenticatedCipher frameCipher, - SecretKey frameKey, int frameLength, byte[] tag) { - this.out = out; - this.frameCipher = frameCipher; - this.frameKey = frameKey; - this.frameLength = frameLength; - this.tag = tag; - iv = new byte[IV_LENGTH]; - aad = new byte[AAD_LENGTH]; - ciphertext = new byte[frameLength]; - frameNumber = 0; - writeTag = (tag != null); - } - - public void writeFrame(byte[] frame, int payloadLength, boolean finalFrame) - throws IOException { - if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); - // Write the tag if required - if(writeTag) { - try { - out.write(tag, 0, tag.length); - } catch(IOException e) { - frameKey.erase(); - throw e; - } - writeTag = false; - } - // Encode the header - FrameEncoder.encodeHeader(frame, finalFrame, payloadLength); - // Don't pad the final frame - int plaintextLength, ciphertextLength; - if(finalFrame) { - plaintextLength = HEADER_LENGTH + payloadLength; - ciphertextLength = plaintextLength + MAC_LENGTH; - } else { - plaintextLength = frameLength - MAC_LENGTH; - ciphertextLength = frameLength; - } - // If there's any padding it must all be zeroes - for(int i = HEADER_LENGTH + payloadLength; i < plaintextLength; i++) { - frame[i] = 0; - } - // Encrypt and authenticate the frame - FrameEncoder.encodeIv(iv, frameNumber); - FrameEncoder.encodeAad(aad, frameNumber, plaintextLength); - try { - frameCipher.init(true, frameKey, iv, aad); - int encrypted = frameCipher.doFinal(frame, 0, plaintextLength, - ciphertext, 0); - if(encrypted != ciphertextLength) throw new RuntimeException(); - } catch(GeneralSecurityException badCipher) { - throw new RuntimeException(badCipher); - } - // Write the frame - try { - out.write(ciphertext, 0, ciphertextLength); - } catch(IOException e) { - frameKey.erase(); - throw e; - } - frameNumber++; - } - - public void flush() throws IOException { - // Write the tag if required - if(writeTag) { - try { - out.write(tag, 0, tag.length); - } catch(IOException e) { - frameKey.erase(); - throw e; - } - writeTag = false; - } - out.flush(); - } -} \ No newline at end of file diff --git a/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java b/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java index d71bfc50ac24c8cc72009a9df6689abee11ddfaa..d066cacd4f6dcefe45ea1a5ad230f6c3fa03b87e 100644 --- a/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java @@ -4,37 +4,28 @@ import java.io.InputStream; import javax.inject.Inject; -import org.briarproject.api.crypto.CryptoComponent; -import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamDecrypterFactory; import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; class StreamReaderFactoryImpl implements StreamReaderFactory { - private final CryptoComponent crypto; + private final StreamDecrypterFactory streamDecrypterFactory; @Inject - StreamReaderFactoryImpl(CryptoComponent crypto) { - this.crypto = crypto; + StreamReaderFactoryImpl(StreamDecrypterFactory streamDecrypterFactory) { + this.streamDecrypterFactory = streamDecrypterFactory; } - public StreamReader createStreamReader(InputStream in, - int maxFrameLength, StreamContext ctx) { - byte[] secret = ctx.getSecret(); - long streamNumber = ctx.getStreamNumber(); - boolean alice = !ctx.getAlice(); - SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); - FrameReader frameReader = new IncomingEncryptionLayer(in, - crypto.getFrameCipher(), frameKey, maxFrameLength); - return new StreamReaderImpl(frameReader, maxFrameLength); + public InputStream createStreamReader(InputStream in, StreamContext ctx) { + return new StreamReaderImpl( + streamDecrypterFactory.createStreamDecrypter(in, ctx)); } - public StreamReader createInvitationStreamReader(InputStream in, - int maxFrameLength, byte[] secret, boolean alice) { - SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice); - FrameReader frameReader = new IncomingEncryptionLayer(in, - crypto.getFrameCipher(), frameKey, maxFrameLength); - return new StreamReaderImpl(frameReader, maxFrameLength); + public InputStream createInvitationStreamReader(InputStream in, + byte[] secret, boolean alice) { + return new StreamReaderImpl( + streamDecrypterFactory.createInvitationStreamDecrypter(in, + secret, alice)); } } diff --git a/briar-core/src/org/briarproject/transport/StreamReaderImpl.java b/briar-core/src/org/briarproject/transport/StreamReaderImpl.java index 0de048a6fa67f805425a645c01affdc8fbd46cf0..cc94ff505bdd8fd203037b6a4cd955e852b4d07b 100644 --- a/briar-core/src/org/briarproject/transport/StreamReaderImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamReaderImpl.java @@ -1,27 +1,22 @@ package org.briarproject.transport; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; import java.io.IOException; import java.io.InputStream; -import org.briarproject.api.transport.StreamReader; +import org.briarproject.api.crypto.StreamDecrypter; -class StreamReaderImpl extends InputStream implements StreamReader { +class StreamReaderImpl extends InputStream { - private final FrameReader in; - private final byte[] frame; + private final StreamDecrypter decrypter; + private final byte[] payload; private int offset = 0, length = 0; - StreamReaderImpl(FrameReader in, int frameLength) { - this.in = in; - frame = new byte[frameLength - MAC_LENGTH]; - } - - public InputStream getInputStream() { - return this; + StreamReaderImpl(StreamDecrypter decrypter) { + this.decrypter = decrypter; + payload = new byte[MAX_PAYLOAD_LENGTH]; } @Override @@ -30,7 +25,7 @@ class StreamReaderImpl extends InputStream implements StreamReader { if(length == -1) return -1; readFrame(); } - int b = frame[offset] & 0xff; + int b = payload[offset] & 0xff; offset++; length--; return b; @@ -48,7 +43,7 @@ class StreamReaderImpl extends InputStream implements StreamReader { readFrame(); } len = Math.min(len, length); - System.arraycopy(frame, offset, b, off, len); + System.arraycopy(payload, offset, b, off, len); offset += len; length -= len; return len; @@ -56,7 +51,7 @@ class StreamReaderImpl extends InputStream implements StreamReader { private void readFrame() throws IOException { assert length == 0; - offset = HEADER_LENGTH; - length = in.readFrame(frame); + offset = 0; + length = decrypter.readFrame(payload); } } diff --git a/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java b/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java index 638ecdff03d2c162eec5ff323876f24db65cb5a0..89da3a46445d4dbed63895332179229562c38417 100644 --- a/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java @@ -1,46 +1,32 @@ package org.briarproject.transport; -import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; - import java.io.OutputStream; import javax.inject.Inject; -import org.briarproject.api.crypto.CryptoComponent; -import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamEncrypterFactory; import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; class StreamWriterFactoryImpl implements StreamWriterFactory { - private final CryptoComponent crypto; + private final StreamEncrypterFactory streamEncrypterFactory; @Inject - StreamWriterFactoryImpl(CryptoComponent crypto) { - this.crypto = crypto; + StreamWriterFactoryImpl(StreamEncrypterFactory streamEncrypterFactory) { + this.streamEncrypterFactory = streamEncrypterFactory; } - public StreamWriter createStreamWriter(OutputStream out, - int maxFrameLength, StreamContext ctx) { - byte[] secret = ctx.getSecret(); - long streamNumber = ctx.getStreamNumber(); - boolean alice = ctx.getAlice(); - byte[] tag = new byte[TAG_LENGTH]; - SecretKey tagKey = crypto.deriveTagKey(secret, alice); - crypto.encodeTag(tag, tagKey, streamNumber); - tagKey.erase(); - SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); - FrameWriter frameWriter = new OutgoingEncryptionLayer(out, - crypto.getFrameCipher(), frameKey, maxFrameLength, tag); - return new StreamWriterImpl(frameWriter, maxFrameLength); + public OutputStream createStreamWriter(OutputStream out, + StreamContext ctx) { + return new StreamWriterImpl( + streamEncrypterFactory.createStreamEncrypter(out, ctx)); } - public StreamWriter createInvitationStreamWriter(OutputStream out, - int maxFrameLength, byte[] secret, boolean alice) { - SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice); - FrameWriter frameWriter = new OutgoingEncryptionLayer(out, - crypto.getFrameCipher(), frameKey, maxFrameLength, null); - return new StreamWriterImpl(frameWriter, maxFrameLength); + public OutputStream createInvitationStreamWriter(OutputStream out, + byte[] secret, boolean alice) { + return new StreamWriterImpl( + streamEncrypterFactory.createInvitationStreamEncrypter(out, + secret, alice)); } } \ No newline at end of file diff --git a/briar-core/src/org/briarproject/transport/StreamWriterImpl.java b/briar-core/src/org/briarproject/transport/StreamWriterImpl.java index 7a65a12199a7133a79928c768d38f0d3d7be0e68..6d7b90ff714e3add2a7c1e3a8941056ee652616d 100644 --- a/briar-core/src/org/briarproject/transport/StreamWriterImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamWriterImpl.java @@ -1,12 +1,11 @@ package org.briarproject.transport; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; import java.io.IOException; import java.io.OutputStream; -import org.briarproject.api.transport.StreamWriter; +import org.briarproject.api.crypto.StreamEncrypter; /** * A {@link org.briarproject.api.transport.StreamWriter StreamWriter} that @@ -15,43 +14,36 @@ import org.briarproject.api.transport.StreamWriter; * <p> * This class is not thread-safe. */ -class StreamWriterImpl extends OutputStream implements StreamWriter { +class StreamWriterImpl extends OutputStream { - private final FrameWriter out; - private final byte[] frame; - private final int frameLength; + private final StreamEncrypter encrypter; + private final byte[] payload; private int length = 0; - StreamWriterImpl(FrameWriter out, int frameLength) { - this.out = out; - this.frameLength = frameLength; - frame = new byte[frameLength - MAC_LENGTH]; - } - - public OutputStream getOutputStream() { - return this; + StreamWriterImpl(StreamEncrypter encrypter) { + this.encrypter = encrypter; + payload = new byte[MAX_PAYLOAD_LENGTH]; } @Override public void close() throws IOException { writeFrame(true); - out.flush(); + encrypter.flush(); super.close(); } @Override public void flush() throws IOException { - if(length > 0) writeFrame(false); - out.flush(); + writeFrame(false); + encrypter.flush(); } @Override public void write(int b) throws IOException { - frame[HEADER_LENGTH + length] = (byte) b; + payload[length] = (byte) b; length++; - if(HEADER_LENGTH + length + MAC_LENGTH == frameLength) - writeFrame(false); + if(length == payload.length) writeFrame(false); } @Override @@ -61,21 +53,21 @@ class StreamWriterImpl extends OutputStream implements StreamWriter { @Override public void write(byte[] b, int off, int len) throws IOException { - int available = frameLength - HEADER_LENGTH - length - MAC_LENGTH; + int available = payload.length - length; while(available <= len) { - System.arraycopy(b, off, frame, HEADER_LENGTH + length, available); + System.arraycopy(b, off, payload, length, available); length += available; writeFrame(false); off += available; len -= available; - available = frameLength - HEADER_LENGTH - length - MAC_LENGTH; + available = payload.length - length; } - System.arraycopy(b, off, frame, HEADER_LENGTH + length, len); + System.arraycopy(b, off, payload, length, len); length += len; } private void writeFrame(boolean finalFrame) throws IOException { - out.writeFrame(frame, length, finalFrame); + encrypter.writeFrame(payload, length, 0, finalFrame); length = 0; } } diff --git a/briar-core/src/org/briarproject/transport/TransportTagRecogniser.java b/briar-core/src/org/briarproject/transport/TransportTagRecogniser.java index 353681d498280ac4260fd40ac670439407974a3a..b7042429ab1764bafdd6cc3761cf4e44fface658 100644 --- a/briar-core/src/org/briarproject/transport/TransportTagRecogniser.java +++ b/briar-core/src/org/briarproject/transport/TransportTagRecogniser.java @@ -62,13 +62,10 @@ class TransportTagRecogniser { assert duplicate == null; } } - key.erase(); // Store the updated reordering window in the DB db.setReorderingWindow(t.contactId, transportId, t.period, t.window.getCentre(), t.window.getBitmap()); - // Clone the secret - the key manager will erase the original - byte[] secret = t.secret.clone(); - return new StreamContext(t.contactId, transportId, secret, + return new StreamContext(t.contactId, transportId, t.secret, t.streamNumber, t.alice); } finally{ @@ -96,7 +93,6 @@ class TransportTagRecogniser { TagContext duplicate = tagMap.put(new Bytes(tag), added); assert duplicate == null; } - key.erase(); // Create a removal context to remove the window and the tags later RemovalContext r = new RemovalContext(window, secret, alice); removalMap.put(new RemovalKey(contactId, period), r); @@ -128,7 +124,6 @@ class TransportTagRecogniser { TagContext removed = tagMap.remove(new Bytes(tag)); assert removed != null; } - key.erase(); } void removeSecrets(ContactId c) { diff --git a/briar-core/src/org/briarproject/transport/TransportTagRecogniser.java.orig b/briar-core/src/org/briarproject/transport/TransportTagRecogniser.java.orig new file mode 100644 index 0000000000000000000000000000000000000000..0e46da82ef5c701c70825abf4ac4512d27bce212 --- /dev/null +++ b/briar-core/src/org/briarproject/transport/TransportTagRecogniser.java.orig @@ -0,0 +1,235 @@ +package org.briarproject.transport; + +import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.briarproject.api.Bytes; +import org.briarproject.api.ContactId; +import org.briarproject.api.TransportId; +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.DbException; +import org.briarproject.api.transport.StreamContext; +import org.briarproject.api.transport.TemporarySecret; + +// FIXME: Don't make alien calls with a lock held +/** + * A {@link org.briarproject.api.transport.TagRecogniser TagRecogniser} for a + * specific transport. + */ +class TransportTagRecogniser { + + private final CryptoComponent crypto; + private final DatabaseComponent db; + private final TransportId transportId; + private final Map<Bytes, TagContext> tagMap; + private final Map<RemovalKey, RemovalContext> removalMap; + + private final Lock synchLock = new ReentrantLock(); + + TransportTagRecogniser(CryptoComponent crypto, DatabaseComponent db, + TransportId transportId) { + this.crypto = crypto; + this.db = db; + this.transportId = transportId; + tagMap = new HashMap<Bytes, TagContext>(); + removalMap = new HashMap<RemovalKey, RemovalContext>(); + } + + StreamContext recogniseTag(byte[] tag) throws DbException { + synchLock.lock(); + try{ + TagContext t = tagMap.remove(new Bytes(tag)); + if(t == null) return null; // The tag was not expected + // Update the reordering window and the expected tags + SecretKey key = crypto.deriveTagKey(t.secret, !t.alice); + for(long streamNumber : t.window.setSeen(t.streamNumber)) { + byte[] tag1 = new byte[TAG_LENGTH]; + crypto.encodeTag(tag1, key, streamNumber); + if(streamNumber < t.streamNumber) { + TagContext removed = tagMap.remove(new Bytes(tag1)); + assert removed != null; + } else { + TagContext added = new TagContext(t, streamNumber); + TagContext duplicate = tagMap.put(new Bytes(tag1), added); + assert duplicate == null; + } + } + key.erase(); + // Store the updated reordering window in the DB + db.setReorderingWindow(t.contactId, transportId, t.period, + t.window.getCentre(), t.window.getBitmap()); + // Clone the secret - the key manager will erase the original + byte[] secret = t.secret.clone(); + return new StreamContext(t.contactId, transportId, secret, + t.streamNumber, t.alice); + } + finally{ + synchLock.unlock(); + } +<<<<<<< HEAD +======= + // Store the updated reordering window in the DB + db.setReorderingWindow(t.contactId, transportId, t.period, + t.window.getCentre(), t.window.getBitmap()); + return new StreamContext(t.contactId, transportId, t.secret, + t.streamNumber, t.alice); +>>>>>>> theSource + } + + void addSecret(TemporarySecret s) { + synchLock.lock(); + try{ + ContactId contactId = s.getContactId(); + boolean alice = s.getAlice(); + long period = s.getPeriod(); + byte[] secret = s.getSecret(); + long centre = s.getWindowCentre(); + byte[] bitmap = s.getWindowBitmap(); + // Create the reordering window and the expected tags + SecretKey key = crypto.deriveTagKey(secret, !alice); + ReorderingWindow window = new ReorderingWindow(centre, bitmap); + for(long streamNumber : window.getUnseen()) { + byte[] tag = new byte[TAG_LENGTH]; + crypto.encodeTag(tag, key, streamNumber); + TagContext added = new TagContext(contactId, alice, period, + secret, window, streamNumber); + TagContext duplicate = tagMap.put(new Bytes(tag), added); + assert duplicate == null; + } + key.erase(); + // Create a removal context to remove the window and the tags later + RemovalContext r = new RemovalContext(window, secret, alice); + removalMap.put(new RemovalKey(contactId, period), r); + } + finally{ + synchLock.unlock(); + } +<<<<<<< HEAD +======= + // Create a removal context to remove the window and the tags later + RemovalContext r = new RemovalContext(window, secret, alice); + removalMap.put(new RemovalKey(contactId, period), r); +>>>>>>> theSource + } + + void removeSecret(ContactId contactId, long period) { + synchLock.lock(); + try{ + RemovalKey k = new RemovalKey(contactId, period); + RemovalContext removed = removalMap.remove(k); + if(removed == null) throw new IllegalArgumentException(); + removeSecret(removed); + } + finally{ + synchLock.unlock(); + } + } + + private void removeSecret(RemovalContext r) { + // Remove the expected tags + SecretKey key = crypto.deriveTagKey(r.secret, !r.alice); + byte[] tag = new byte[TAG_LENGTH]; + for(long streamNumber : r.window.getUnseen()) { + crypto.encodeTag(tag, key, streamNumber); + TagContext removed = tagMap.remove(new Bytes(tag)); + assert removed != null; + } + } + + void removeSecrets(ContactId c) { + synchLock.lock(); + try{ + Collection<RemovalKey> keysToRemove = new ArrayList<RemovalKey>(); + for(RemovalKey k : removalMap.keySet()) + if(k.contactId.equals(c)) keysToRemove.add(k); + for(RemovalKey k : keysToRemove) removeSecret(k.contactId, k.period); + } + finally{ + synchLock.unlock(); + } + } + + void removeSecrets() { + synchLock.lock(); + try{ + for(RemovalContext r : removalMap.values()) removeSecret(r); + assert tagMap.isEmpty(); + removalMap.clear(); + } + finally{ + synchLock.unlock(); + } + } + + private static class TagContext { + + private final ContactId contactId; + private final boolean alice; + private final long period; + private final byte[] secret; + private final ReorderingWindow window; + private final long streamNumber; + + private TagContext(ContactId contactId, boolean alice, long period, + byte[] secret, ReorderingWindow window, long streamNumber) { + this.contactId = contactId; + this.alice = alice; + this.period = period; + this.secret = secret; + this.window = window; + this.streamNumber = streamNumber; + } + + private TagContext(TagContext t, long streamNumber) { + this(t.contactId, t.alice, t.period, t.secret, t.window, + streamNumber); + } + } + + private static class RemovalKey { + + private final ContactId contactId; + private final long period; + + private RemovalKey(ContactId contactId, long period) { + this.contactId = contactId; + this.period = period; + } + + @Override + public int hashCode() { + return contactId.hashCode() ^ (int) (period ^ (period >>> 32)); + } + + @Override + public boolean equals(Object o) { + if(o instanceof RemovalKey) { + RemovalKey k = (RemovalKey) o; + return contactId.equals(k.contactId) && period == k.period; + } + return false; + } + } + + private static class RemovalContext { + + private final ReorderingWindow window; + private final byte[] secret; + private final boolean alice; + + private RemovalContext(ReorderingWindow window, byte[] secret, + boolean alice) { + this.window = window; + this.secret = secret; + this.alice = alice; + } + } +} diff --git a/briar-core/src/org/briarproject/util/ByteUtils.java b/briar-core/src/org/briarproject/util/ByteUtils.java index ed40306e5931afcfed36dca9def9e540f2a7259f..9cc9e6bc8826f36847c1d14b639b8d2a897c7900 100644 --- a/briar-core/src/org/briarproject/util/ByteUtils.java +++ b/briar-core/src/org/briarproject/util/ByteUtils.java @@ -48,10 +48,6 @@ public class ByteUtils { | ((b[offset + 2] & 0xFFL) << 8) | (b[offset + 3] & 0xFFL); } - public static void erase(byte[] b) { - for(int i = 0; i < b.length; i++) b[i] = 0; - } - public static int readUint(byte[] b, int bits) { if(b.length << 3 < bits) throw new IllegalArgumentException(); int result = 0; diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java index 82f69bee237d01ba3ccd0de5a4537507eda9b198..45dca315f2a2b6ad64e7f21cc20e1a6b721c0b16 100644 --- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java @@ -46,8 +46,7 @@ class BluetoothPlugin implements DuplexPlugin { private final Clock clock; private final SecureRandom secureRandom; private final DuplexPluginCallback callback; - private final int maxFrameLength; - private final long maxLatency, pollingInterval; + private final int maxLatency, pollingInterval; private final Semaphore discoverySemaphore = new Semaphore(1); private volatile boolean running = false; @@ -55,13 +54,12 @@ class BluetoothPlugin implements DuplexPlugin { private volatile LocalDevice localDevice = null; BluetoothPlugin(Executor ioExecutor, Clock clock, SecureRandom secureRandom, - DuplexPluginCallback callback, int maxFrameLength, long maxLatency, - long pollingInterval) { + DuplexPluginCallback callback, int maxLatency, + int pollingInterval) { this.ioExecutor = ioExecutor; this.clock = clock; this.secureRandom = secureRandom; this.callback = callback; - this.maxFrameLength = maxFrameLength; this.maxLatency = maxLatency; this.pollingInterval = pollingInterval; } @@ -70,12 +68,13 @@ class BluetoothPlugin implements DuplexPlugin { return ID; } - public int getMaxFrameLength() { - return maxFrameLength; + public int getMaxLatency() { + return maxLatency; } - public long getMaxLatency() { - return maxLatency; + public int getMaxIdleTime() { + // Bluetooth detects dead connections so we don't need keepalives + return Integer.MAX_VALUE; } public boolean start() throws IOException { @@ -181,7 +180,7 @@ class BluetoothPlugin implements DuplexPlugin { return true; } - public long getPollingInterval() { + public int getPollingInterval() { return pollingInterval; } diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java index 446846305b840605d64c9504fc2a2a3077689920..28859c4d7eae9e79ff42baf2081b70f71dafc809 100644 --- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java +++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPluginFactory.java @@ -12,9 +12,8 @@ import org.briarproject.system.SystemClock; public class BluetoothPluginFactory implements DuplexPluginFactory { - private static final int MAX_FRAME_LENGTH = 1024; - private static final long MAX_LATENCY = 60 * 1000; // 1 minute - private static final long POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes + private static final int MAX_LATENCY = 30 * 1000; // 30 seconds + private static final int POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes private final Executor ioExecutor; private final SecureRandom secureRandom; @@ -33,6 +32,6 @@ public class BluetoothPluginFactory implements DuplexPluginFactory { public DuplexPlugin createPlugin(DuplexPluginCallback callback) { return new BluetoothPlugin(ioExecutor, clock, secureRandom, callback, - MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL); + MAX_LATENCY, POLLING_INTERVAL); } } diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java index 8a2c7967257c223d5e34c08444eab33663ab3afd..8e70db480b0cfa70cdb256c110ffb5c275740a02 100644 --- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java +++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java @@ -39,10 +39,6 @@ class BluetoothTransportConnection implements DuplexTransportConnection { private class Reader implements TransportConnectionReader { - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); - } - public long getMaxLatency() { return plugin.getMaxLatency(); } @@ -60,12 +56,12 @@ class BluetoothTransportConnection implements DuplexTransportConnection { private class Writer implements TransportConnectionWriter { - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); + public int getMaxLatency() { + return plugin.getMaxLatency(); } - public long getMaxLatency() { - return plugin.getMaxLatency(); + public int getMaxIdleTime() { + return plugin.getMaxIdleTime(); } public long getCapacity() { diff --git a/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java b/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java index 4ca9e3ce66fcd30bbe931774f0849aa25784b822..8938e0776e7be907616adbd4994b59a7e41406fa 100644 --- a/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java +++ b/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java @@ -17,7 +17,7 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable { private final Executor ioExecutor; private final RemovableDriveFinder finder; - private final long pollingInterval; + private final int pollingInterval; private volatile boolean running = false; private volatile Callback callback = null; @@ -27,7 +27,7 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable { public PollingRemovableDriveMonitor(Executor ioExecutor, - RemovableDriveFinder finder, long pollingInterval) { + RemovableDriveFinder finder, int pollingInterval) { this.ioExecutor = ioExecutor; this.finder = finder; this.pollingInterval = pollingInterval; diff --git a/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java.orig b/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java.orig new file mode 100644 index 0000000000000000000000000000000000000000..d27f65a0a1226dac3b5507636a6994ea76a88802 --- /dev/null +++ b/briar-desktop/src/org/briarproject/plugins/file/PollingRemovableDriveMonitor.java.orig @@ -0,0 +1,83 @@ +package org.briarproject.plugins.file; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Logger; + +class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable { + + private static final Logger LOG = + Logger.getLogger(PollingRemovableDriveMonitor.class.getName()); + + private final Executor ioExecutor; + private final RemovableDriveFinder finder; +<<<<<<< HEAD + private final long pollingInterval; +======= + private final int pollingInterval; + private final Object pollingLock = new Object(); +>>>>>>> theSource + + private volatile boolean running = false; + private volatile Callback callback = null; + + private final Lock pollingLock = new ReentrantLock(); + private final Condition stopPolling = pollingLock.newCondition(); + + + public PollingRemovableDriveMonitor(Executor ioExecutor, + RemovableDriveFinder finder, int pollingInterval) { + this.ioExecutor = ioExecutor; + this.finder = finder; + this.pollingInterval = pollingInterval; + } + + public void start(Callback callback) throws IOException { + this.callback = callback; + running = true; + ioExecutor.execute(this); + } + + public void stop() throws IOException { + running = false; + pollingLock.lock(); + try { + stopPolling.signalAll(); + } + finally { + pollingLock.unlock(); + } + } + + public void run() { + try { + Collection<File> drives = finder.findRemovableDrives(); + while(running) { + pollingLock.lock(); + try { + stopPolling.await(pollingInterval, TimeUnit.MILLISECONDS); + } + finally{ + pollingLock.unlock(); + } + if(!running) return; + Collection<File> newDrives = finder.findRemovableDrives(); + for(File f : newDrives) { + if(!drives.contains(f)) callback.driveInserted(f); + } + drives = newDrives; + } + } catch(InterruptedException e) { + LOG.warning("Interrupted while waiting to poll"); + Thread.currentThread().interrupt(); + } catch(IOException e) { + callback.exceptionThrown(e); + } + } +} diff --git a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java index 09ffd6f663592668cc148c7ae74af1b1cc065005..f648ff092c402c0df55d767838a64d92b22ef996 100644 --- a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java @@ -29,9 +29,8 @@ implements RemovableDriveMonitor.Callback { RemovableDrivePlugin(Executor ioExecutor, FileUtils fileUtils, SimplexPluginCallback callback, RemovableDriveFinder finder, - RemovableDriveMonitor monitor, int maxFrameLength, - long maxLatency) { - super(ioExecutor, fileUtils, callback, maxFrameLength, maxLatency); + RemovableDriveMonitor monitor, int maxLatency) { + super(ioExecutor, fileUtils, callback, maxLatency); this.finder = finder; this.monitor = monitor; } @@ -55,7 +54,7 @@ implements RemovableDriveMonitor.Callback { return false; } - public long getPollingInterval() { + public int getPollingInterval() { throw new UnsupportedOperationException(); } diff --git a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePluginFactory.java b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePluginFactory.java index 01688d40344aee7171076077cdd4ca3b09ecfda5..68b1eaf31c8bb2a4e856f8f21b19b0705db99e66 100644 --- a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePluginFactory.java +++ b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePluginFactory.java @@ -1,7 +1,5 @@ package org.briarproject.plugins.file; -import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; - import java.util.concurrent.Executor; import org.briarproject.api.TransportId; @@ -14,8 +12,8 @@ import org.briarproject.util.OsUtils; public class RemovableDrivePluginFactory implements SimplexPluginFactory { // Maximum latency 14 days (Royal Mail or lackadaisical carrier pigeon) - private static final long MAX_LATENCY = 14 * 24 * 60 * 60 * 1000; - private static final long POLLING_INTERVAL = 10 * 1000; // 10 seconds + private static final int MAX_LATENCY = 14 * 24 * 60 * 60 * 1000; + private static final int POLLING_INTERVAL = 10 * 1000; // 10 seconds private final Executor ioExecutor; private final FileUtils fileUtils; @@ -52,6 +50,6 @@ public class RemovableDrivePluginFactory implements SimplexPluginFactory { return null; } return new RemovableDrivePlugin(ioExecutor, fileUtils, callback, - finder, monitor, MAX_FRAME_LENGTH, MAX_LATENCY); + finder, monitor, MAX_LATENCY); } } diff --git a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java index 686f1c540473701a543ec962df43063f09739f45..b0a58969cb6ccb1a533994b7640c5e00f8a81ad8 100644 --- a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java @@ -32,33 +32,30 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { private final ModemFactory modemFactory; private final SerialPortList serialPortList; private final DuplexPluginCallback callback; - private final int maxFrameLength; - private final long maxLatency, pollingInterval; + private final int maxLatency; private volatile boolean running = false; private volatile Modem modem = null; ModemPlugin(ModemFactory modemFactory, SerialPortList serialPortList, - DuplexPluginCallback callback, int maxFrameLength, long maxLatency, - long pollingInterval) { + DuplexPluginCallback callback, int maxLatency) { this.modemFactory = modemFactory; this.serialPortList = serialPortList; this.callback = callback; - this.maxFrameLength = maxFrameLength; this.maxLatency = maxLatency; - this.pollingInterval = pollingInterval; } public TransportId getId() { return ID; } - public int getMaxFrameLength() { - return maxFrameLength; + public int getMaxLatency() { + return maxLatency; } - public long getMaxLatency() { - return maxLatency; + public int getMaxIdleTime() { + // FIXME: Do we need keepalives for this transport? + return Integer.MAX_VALUE; } public boolean start() { @@ -98,8 +95,8 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { return false; } - public long getPollingInterval() { - return pollingInterval; + public int getPollingInterval() { + throw new UnsupportedOperationException(); } public void poll(Collection<ContactId> connected) { @@ -197,10 +194,6 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { private class Reader implements TransportConnectionReader { - public int getMaxFrameLength() { - return maxFrameLength; - } - public long getMaxLatency() { return maxLatency; } @@ -217,12 +210,12 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { private class Writer implements TransportConnectionWriter { - public int getMaxFrameLength() { - return maxFrameLength; + public int getMaxLatency() { + return getMaxLatency(); } - public long getMaxLatency() { - return maxLatency; + public int getMaxIdleTime() { + return getMaxIdleTime(); } public long getCapacity() { diff --git a/briar-desktop/src/org/briarproject/plugins/modem/ModemPluginFactory.java b/briar-desktop/src/org/briarproject/plugins/modem/ModemPluginFactory.java index 2732bbbc7cd569922cb2dc00ff942961f9fcee26..f70fc3be712fc4a5ca32cd415b0aadadd8177a05 100644 --- a/briar-desktop/src/org/briarproject/plugins/modem/ModemPluginFactory.java +++ b/briar-desktop/src/org/briarproject/plugins/modem/ModemPluginFactory.java @@ -11,9 +11,7 @@ import org.briarproject.util.StringUtils; public class ModemPluginFactory implements DuplexPluginFactory { - private static final int MAX_FRAME_LENGTH = 1024; - private static final long MAX_LATENCY = 60 * 1000; // 1 minute - private static final long POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour + private static final int MAX_LATENCY = 30 * 1000; // 30 seconds private final ModemFactory modemFactory; private final SerialPortList serialPortList; @@ -33,6 +31,6 @@ public class ModemPluginFactory implements DuplexPluginFactory { String enabled = callback.getConfig().get("enabled"); if(StringUtils.isNullOrEmpty(enabled)) return null; return new ModemPlugin(modemFactory, serialPortList, callback, - MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL); + MAX_LATENCY); } } diff --git a/briar-tests/build.xml b/briar-tests/build.xml index 2ab788bba3b4ecc03415ed0f4aabe0343e183b1f..eb4766a090b7d734a50b0f2c83bc06af304eccc6 100644 --- a/briar-tests/build.xml +++ b/briar-tests/build.xml @@ -100,8 +100,9 @@ <test name='org.briarproject.crypto.KeyDerivationTest'/> <test name='org.briarproject.crypto.KeyEncodingAndParsingTest'/> <test name="org.briarproject.crypto.PasswordBasedKdfTest"/> - <test name="org.briarproject.crypto.PasswordStrengthEstimatorTest"/> - <test name='org.briarproject.crypto.SecretKeyImplTest'/> + <test name="org.briarproject.crypto.PasswordStrengthEstimatorImplTest"/> + <test name='org.briarproject.crypto.StreamDecrypterImplTest'/> + <test name='org.briarproject.crypto.StreamEncrypterImplTest'/> <test name='org.briarproject.db.BasicH2Test'/> <test name='org.briarproject.db.DatabaseCleanerImplTest'/> <test name='org.briarproject.db.DatabaseComponentImplTest'/> @@ -126,10 +127,8 @@ <test name='org.briarproject.serial.ReaderImplTest'/> <test name='org.briarproject.serial.WriterImplTest'/> <test name='org.briarproject.system.LinuxSeedProviderTest'/> - <test name='org.briarproject.transport.IncomingEncryptionLayerTest'/> <test name='org.briarproject.transport.KeyManagerImplTest'/> <test name='org.briarproject.transport.KeyRotationIntegrationTest'/> - <test name='org.briarproject.transport.OutgoingEncryptionLayerTest'/> <test name='org.briarproject.transport.ReorderingWindowTest'/> <test name='org.briarproject.transport.StreamReaderImplTest'/> <test name='org.briarproject.transport.StreamWriterImplTest'/> diff --git a/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java b/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java index cebd634d4d63631403419599edaf4a15af47edd3..633a4929ad23c580685f96f2a63cbc05d20682ca 100644 --- a/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java +++ b/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java @@ -1,12 +1,12 @@ package org.briarproject; -import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import static org.junit.Assert.assertArrayEquals; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; +import java.io.OutputStream; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -36,9 +36,7 @@ import org.briarproject.api.messaging.SubscriptionUpdate; import org.briarproject.api.messaging.TransportUpdate; import org.briarproject.api.messaging.UnverifiedMessage; import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.crypto.CryptoModule; import org.briarproject.db.DatabaseModule; @@ -117,12 +115,12 @@ public class ProtocolIntegrationTest extends BriarTestCase { private byte[] write() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - StreamContext ctx = new StreamContext(contactId, transportId, - secret.clone(), 0, true); - StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out, - MAX_FRAME_LENGTH, ctx); + StreamContext ctx = new StreamContext(contactId, transportId, secret, + 0, true); + OutputStream streamWriter = + streamWriterFactory.createStreamWriter(out, ctx); PacketWriter packetWriter = packetWriterFactory.createPacketWriter( - streamWriter.getOutputStream()); + streamWriter); packetWriter.writeAck(new Ack(messageIds)); @@ -140,7 +138,7 @@ public class ProtocolIntegrationTest extends BriarTestCase { transportProperties, 1); packetWriter.writeTransportUpdate(tu); - streamWriter.getOutputStream().flush(); + streamWriter.flush(); return out.toByteArray(); } @@ -149,12 +147,12 @@ public class ProtocolIntegrationTest extends BriarTestCase { byte[] tag = new byte[TAG_LENGTH]; assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH)); // FIXME: Check that the expected tag was received - StreamContext ctx = new StreamContext(contactId, transportId, - secret.clone(), 0, false); - StreamReader streamReader = streamReaderFactory.createStreamReader(in, - MAX_FRAME_LENGTH, ctx); + StreamContext ctx = new StreamContext(contactId, transportId, secret, + 0, false); + InputStream streamReader = + streamReaderFactory.createStreamReader(in, ctx); PacketReader packetReader = packetReaderFactory.createPacketReader( - streamReader.getInputStream()); + streamReader); // Read the ack assertTrue(packetReader.hasAck()); diff --git a/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java b/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java index 54c4a3c1560d6363bbe277a740a358ae811aff30..d0d71a7eb77dadec8b18cdad72d6f1095780d492 100644 --- a/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java +++ b/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java @@ -30,9 +30,9 @@ public class KeyDerivationTest extends BriarTestCase { keys.add(crypto.deriveTagKey(secret, true)); keys.add(crypto.deriveTagKey(secret, false)); for(int i = 0; i < 4; i++) { - byte[] keyI = keys.get(i).getEncoded(); + byte[] keyI = keys.get(i).getBytes(); for(int j = 0; j < 4; j++) { - byte[] keyJ = keys.get(j).getEncoded(); + byte[] keyJ = keys.get(j).getBytes(); assertEquals(i == j, Arrays.equals(keyI, keyJ)); } } @@ -59,9 +59,8 @@ public class KeyDerivationTest extends BriarTestCase { @Test public void testStreamNumberAffectsDerivation() { List<byte[]> secrets = new ArrayList<byte[]>(); - for(int i = 0; i < 20; i++) { - secrets.add(crypto.deriveNextSecret(secret.clone(), i)); - } + for(int i = 0; i < 20; i++) + secrets.add(crypto.deriveNextSecret(secret, i)); for(int i = 0; i < 20; i++) { byte[] secretI = secrets.get(i); for(int j = 0; j < 20; j++) { diff --git a/briar-tests/src/org/briarproject/crypto/PasswordBasedKdfTest.java b/briar-tests/src/org/briarproject/crypto/PasswordBasedKdfTest.java index 151f78fb57777f1ecffa4df16620bce3e9f3c64c..cdc59b3323a7e88b37b48ccc9bf2df006ac63744 100644 --- a/briar-tests/src/org/briarproject/crypto/PasswordBasedKdfTest.java +++ b/briar-tests/src/org/briarproject/crypto/PasswordBasedKdfTest.java @@ -18,7 +18,7 @@ public class PasswordBasedKdfTest extends BriarTestCase { Random random = new Random(); byte[] input = new byte[1234]; random.nextBytes(input); - char[] password = "password".toCharArray(); + String password = "password"; byte[] ciphertext = crypto.encryptWithPassword(input, password); byte[] output = crypto.decryptWithPassword(ciphertext, password); assertArrayEquals(input, output); @@ -29,7 +29,7 @@ public class PasswordBasedKdfTest extends BriarTestCase { Random random = new Random(); byte[] input = new byte[1234]; random.nextBytes(input); - char[] password = "password".toCharArray(); + String password = "password"; byte[] ciphertext = crypto.encryptWithPassword(input, password); // Modify the ciphertext int position = random.nextInt(ciphertext.length); diff --git a/briar-tests/src/org/briarproject/crypto/PasswordStrengthEstimatorTest.java b/briar-tests/src/org/briarproject/crypto/PasswordStrengthEstimatorImplTest.java similarity index 50% rename from briar-tests/src/org/briarproject/crypto/PasswordStrengthEstimatorTest.java rename to briar-tests/src/org/briarproject/crypto/PasswordStrengthEstimatorImplTest.java index 9f703b378f7acaa13670691c8101a156279f132e..5407daf9ae72feb4331f2b71d87134d05b10db46 100644 --- a/briar-tests/src/org/briarproject/crypto/PasswordStrengthEstimatorTest.java +++ b/briar-tests/src/org/briarproject/crypto/PasswordStrengthEstimatorImplTest.java @@ -6,24 +6,23 @@ import org.briarproject.BriarTestCase; import org.briarproject.api.crypto.PasswordStrengthEstimator; import org.junit.Test; -public class PasswordStrengthEstimatorTest extends BriarTestCase { +public class PasswordStrengthEstimatorImplTest extends BriarTestCase { @Test public void testWeakPasswords() { PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl(); - assertTrue(e.estimateStrength("".toCharArray()) < QUITE_STRONG); - assertTrue(e.estimateStrength("password".toCharArray()) < QUITE_STRONG); - assertTrue(e.estimateStrength("letmein".toCharArray()) < QUITE_STRONG); - assertTrue(e.estimateStrength("123456".toCharArray()) < QUITE_STRONG); + assertTrue(e.estimateStrength("") < QUITE_STRONG); + assertTrue(e.estimateStrength("password") < QUITE_STRONG); + assertTrue(e.estimateStrength("letmein") < QUITE_STRONG); + assertTrue(e.estimateStrength("123456") < QUITE_STRONG); } @Test public void testStrongPasswords() { PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl(); // Industry standard - assertTrue(e.estimateStrength("Tr0ub4dor&3".toCharArray()) - > QUITE_STRONG); - assertTrue(e.estimateStrength("correcthorsebatterystaple".toCharArray()) + assertTrue(e.estimateStrength("Tr0ub4dor&3") > QUITE_STRONG); + assertTrue(e.estimateStrength("correcthorsebatterystaple") > QUITE_STRONG); } } diff --git a/briar-tests/src/org/briarproject/crypto/SecretKeyImplTest.java b/briar-tests/src/org/briarproject/crypto/SecretKeyImplTest.java deleted file mode 100644 index 20ee3605bf954d68df560c0d11bfb47d4bbeb599..0000000000000000000000000000000000000000 --- a/briar-tests/src/org/briarproject/crypto/SecretKeyImplTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.briarproject.crypto; - -import static org.junit.Assert.assertArrayEquals; - -import java.util.Random; - -import org.briarproject.BriarTestCase; -import org.briarproject.api.crypto.SecretKey; -import org.junit.Test; - -public class SecretKeyImplTest extends BriarTestCase { - - private static final int KEY_BYTES = 32; // 256 bits - - @Test - public void testCopiesAreErased() { - byte[] master = new byte[KEY_BYTES]; - new Random().nextBytes(master); - SecretKey k = new SecretKeyImpl(master); - byte[] copy = k.getEncoded(); - assertArrayEquals(master, copy); - k.erase(); - byte[] blank = new byte[KEY_BYTES]; - assertArrayEquals(blank, master); - assertArrayEquals(blank, copy); - } -} diff --git a/briar-tests/src/org/briarproject/crypto/StreamDecrypterImplTest.java b/briar-tests/src/org/briarproject/crypto/StreamDecrypterImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c907f37e246939b7db1c13491317772fb0aa44b5 --- /dev/null +++ b/briar-tests/src/org/briarproject/crypto/StreamDecrypterImplTest.java @@ -0,0 +1,37 @@ +package org.briarproject.crypto; + +import org.briarproject.BriarTestCase; +import org.junit.Test; + +public class StreamDecrypterImplTest extends BriarTestCase { + + @Test + public void testReadValidFrames() throws Exception { + // FIXME + } + + @Test + public void testTruncatedFrameThrowsException() throws Exception { + // FIXME + } + + @Test + public void testModifiedFrameThrowsException() throws Exception { + // FIXME + } + + @Test + public void testInvalidPayloadLengthThrowsException() throws Exception { + // FIXME + } + + @Test + public void testNonZeroPaddingThrowsException() throws Exception { + // FIXME + } + + @Test + public void testCannotReadBeyondFinalFrame() throws Exception { + // FIXME + } +} diff --git a/briar-tests/src/org/briarproject/crypto/StreamEncrypterImplTest.java b/briar-tests/src/org/briarproject/crypto/StreamEncrypterImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a874314165b0e3b6de8b38c93c443136e0d928a0 --- /dev/null +++ b/briar-tests/src/org/briarproject/crypto/StreamEncrypterImplTest.java @@ -0,0 +1,255 @@ +package org.briarproject.crypto; + +import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; +import static org.junit.Assert.assertArrayEquals; + +import java.io.ByteArrayOutputStream; +import java.util.Random; + +import org.briarproject.BriarTestCase; +import org.briarproject.api.crypto.AuthenticatedCipher; +import org.briarproject.api.crypto.SecretKey; +import org.junit.Test; + +public class StreamEncrypterImplTest extends BriarTestCase { + + private final AuthenticatedCipher frameCipher; + private final SecretKey frameKey; + private final byte[] tag; + + public StreamEncrypterImplTest() { + frameCipher = new TestAuthenticatedCipher(); + frameKey = new SecretKey(new byte[32]); + tag = new byte[TAG_LENGTH]; + new Random().nextBytes(tag); + } + + @Test + public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, tag); + int payloadLength = 123; + byte[] payload = new byte[payloadLength]; + new Random().nextBytes(payload); + + s.writeFrame(payload, payloadLength, 0, false); + + byte[] header = new byte[HEADER_LENGTH]; + FrameEncoder.encodeHeader(header, false, payloadLength, 0); + byte[] expected = new byte[TAG_LENGTH + HEADER_LENGTH + payloadLength + + MAC_LENGTH]; + System.arraycopy(tag, 0, expected, 0, TAG_LENGTH); + System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH); + System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH, + payloadLength); + assertArrayEquals(expected, out.toByteArray()); + } + + @Test + public void testWriteUnpaddedFinalFrameWithTag() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, tag); + int payloadLength = 123; + byte[] payload = new byte[payloadLength]; + new Random().nextBytes(payload); + + s.writeFrame(payload, payloadLength, 0, true); + + byte[] header = new byte[HEADER_LENGTH]; + FrameEncoder.encodeHeader(header, true, payloadLength, 0); + byte[] expected = new byte[TAG_LENGTH + HEADER_LENGTH + payloadLength + + MAC_LENGTH]; + System.arraycopy(tag, 0, expected, 0, TAG_LENGTH); + System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH); + System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH, + payloadLength); + assertArrayEquals(expected, out.toByteArray()); + } + + @Test + public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, null); + int payloadLength = 123; + byte[] payload = new byte[payloadLength]; + new Random().nextBytes(payload); + + s.writeFrame(payload, payloadLength, 0, false); + + byte[] header = new byte[HEADER_LENGTH]; + FrameEncoder.encodeHeader(header, false, payloadLength, 0); + byte[] expected = new byte[HEADER_LENGTH + payloadLength + MAC_LENGTH]; + System.arraycopy(header, 0, expected, 0, HEADER_LENGTH); + System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength); + assertArrayEquals(expected, out.toByteArray()); + } + + @Test + public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, null); + int payloadLength = 123; + byte[] payload = new byte[payloadLength]; + new Random().nextBytes(payload); + + s.writeFrame(payload, payloadLength, 0, true); + + byte[] header = new byte[HEADER_LENGTH]; + FrameEncoder.encodeHeader(header, true, payloadLength, 0); + byte[] expected = new byte[HEADER_LENGTH + payloadLength + MAC_LENGTH]; + System.arraycopy(header, 0, expected, 0, HEADER_LENGTH); + System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength); + assertArrayEquals(expected, out.toByteArray()); + } + + @Test + public void testWritePaddedNonFinalFrameWithTag() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, tag); + int payloadLength = 123, paddingLength = 234; + byte[] payload = new byte[payloadLength]; + new Random().nextBytes(payload); + + s.writeFrame(payload, payloadLength, paddingLength, false); + + byte[] header = new byte[HEADER_LENGTH]; + FrameEncoder.encodeHeader(header, false, payloadLength, paddingLength); + byte[] expected = new byte[TAG_LENGTH + HEADER_LENGTH + payloadLength + + paddingLength + MAC_LENGTH]; + System.arraycopy(tag, 0, expected, 0, TAG_LENGTH); + System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH); + System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH, + payloadLength); + assertArrayEquals(expected, out.toByteArray()); + } + + @Test + public void testWritePaddedFinalFrameWithTag() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, tag); + int payloadLength = 123, paddingLength = 234; + byte[] payload = new byte[payloadLength]; + new Random().nextBytes(payload); + + s.writeFrame(payload, payloadLength, paddingLength, true); + + byte[] header = new byte[HEADER_LENGTH]; + FrameEncoder.encodeHeader(header, true, payloadLength, paddingLength); + byte[] expected = new byte[TAG_LENGTH + HEADER_LENGTH + payloadLength + + paddingLength + MAC_LENGTH]; + System.arraycopy(tag, 0, expected, 0, TAG_LENGTH); + System.arraycopy(header, 0, expected, TAG_LENGTH, HEADER_LENGTH); + System.arraycopy(payload, 0, expected, TAG_LENGTH + HEADER_LENGTH, + payloadLength); + assertArrayEquals(expected, out.toByteArray()); + } + + @Test + public void testWritePaddedNonFinalFrameWithoutTag() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, null); + int payloadLength = 123, paddingLength = 234; + byte[] payload = new byte[payloadLength]; + new Random().nextBytes(payload); + + s.writeFrame(payload, payloadLength, paddingLength, false); + + byte[] header = new byte[HEADER_LENGTH]; + FrameEncoder.encodeHeader(header, false, payloadLength, paddingLength); + byte[] expected = new byte[HEADER_LENGTH + payloadLength + + paddingLength + MAC_LENGTH]; + System.arraycopy(header, 0, expected, 0, HEADER_LENGTH); + System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength); + assertArrayEquals(expected, out.toByteArray()); + } + + @Test + public void testWritePaddedFinalFrameWithoutTag() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, null); + int payloadLength = 123, paddingLength = 234; + byte[] payload = new byte[payloadLength]; + new Random().nextBytes(payload); + + s.writeFrame(payload, payloadLength, paddingLength, true); + + byte[] header = new byte[HEADER_LENGTH]; + FrameEncoder.encodeHeader(header, true, payloadLength, paddingLength); + byte[] expected = new byte[HEADER_LENGTH + payloadLength + + paddingLength + MAC_LENGTH]; + System.arraycopy(header, 0, expected, 0, HEADER_LENGTH); + System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength); + assertArrayEquals(expected, out.toByteArray()); + } + + @Test + public void testWriteTwoFrames() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, null); + int payloadLength = 123, paddingLength = 234; + byte[] payload = new byte[payloadLength]; + new Random().nextBytes(payload); + int payloadLength1 = 345, paddingLength1 = 456; + byte[] payload1 = new byte[payloadLength1]; + new Random().nextBytes(payload1); + + s.writeFrame(payload, payloadLength, paddingLength, false); + s.writeFrame(payload1, payloadLength1, paddingLength1, true); + + byte[] header = new byte[HEADER_LENGTH]; + FrameEncoder.encodeHeader(header, false, payloadLength, paddingLength); + byte[] header1 = new byte[HEADER_LENGTH]; + FrameEncoder.encodeHeader(header1, true, payloadLength1, + paddingLength1); + byte[] expected = new byte[HEADER_LENGTH + payloadLength + + paddingLength + MAC_LENGTH + + HEADER_LENGTH + payloadLength1 + + paddingLength1 + MAC_LENGTH]; + System.arraycopy(header, 0, expected, 0, HEADER_LENGTH); + System.arraycopy(payload, 0, expected, HEADER_LENGTH, payloadLength); + System.arraycopy(header1, 0, expected, HEADER_LENGTH + payloadLength + + paddingLength + MAC_LENGTH, HEADER_LENGTH); + System.arraycopy(payload1, 0, expected, HEADER_LENGTH + payloadLength + + paddingLength + MAC_LENGTH + HEADER_LENGTH, payloadLength1); + assertArrayEquals(expected, out.toByteArray()); + } + + @Test + public void testFlushWritesTagIfNotAlreadyWritten() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, tag); + s.flush(); + assertArrayEquals(tag, out.toByteArray()); + } + + @Test + public void testFlushDoesNotWriteTagIfAlreadyWritten() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, tag); + s.flush(); + s.flush(); + assertArrayEquals(tag, out.toByteArray()); + } + + @Test + public void testFlushDoesNotWriteTagIfNull() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamEncrypterImpl s = new StreamEncrypterImpl(out, frameCipher, + frameKey, null); + s.flush(); + assertEquals(0, out.size()); + } +} diff --git a/briar-tests/src/org/briarproject/crypto/TestAuthenticatedCipher.java b/briar-tests/src/org/briarproject/crypto/TestAuthenticatedCipher.java new file mode 100644 index 0000000000000000000000000000000000000000..0271975bd43f5b64368486dbfdb2c5f0d9f0f64e --- /dev/null +++ b/briar-tests/src/org/briarproject/crypto/TestAuthenticatedCipher.java @@ -0,0 +1,45 @@ +package org.briarproject.crypto; + +import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; + +import java.security.GeneralSecurityException; + +import org.briarproject.api.crypto.AuthenticatedCipher; +import org.briarproject.api.crypto.SecretKey; + +class TestAuthenticatedCipher implements AuthenticatedCipher { + + private static final int BLOCK_BYTES = 16; + + private boolean encrypt = false; + + public void init(boolean encrypt, SecretKey key, byte[] iv, byte[] aad) + throws GeneralSecurityException { + this.encrypt = encrypt; + } + + public int process(byte[] input, int inputOff, int len, byte[] output, + int outputOff) throws GeneralSecurityException { + if(encrypt) { + System.arraycopy(input, inputOff, output, outputOff, len); + for(int i = 0; i < MAC_LENGTH; i++) + output[outputOff + len + i] = 0; + return len + MAC_LENGTH; + } else { + for(int i = 0; i < MAC_LENGTH; i++) + if(input[inputOff + len - MAC_LENGTH + i] != 0) + throw new GeneralSecurityException(); + System.arraycopy(input, inputOff, output, outputOff, + len - MAC_LENGTH); + return len - MAC_LENGTH; + } + } + + public int getMacLength() { + return MAC_LENGTH; + } + + public int getBlockSize() { + return BLOCK_BYTES; + } +} diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentTest.java index 57855cfb6f8e8c6277ec501ca7acd720a99343e4..338b8c9577655a2f4c2dec5542c682a4fae77e7f 100644 --- a/briar-tests/src/org/briarproject/db/DatabaseComponentTest.java +++ b/briar-tests/src/org/briarproject/db/DatabaseComponentTest.java @@ -75,6 +75,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { protected final Message message, message1; protected final TransportId transportId; protected final TransportProperties transportProperties; + protected final int maxLatency; protected final ContactId contactId; protected final Contact contact; protected final Endpoint endpoint; @@ -102,6 +103,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { transportId = new TransportId("id"); transportProperties = new TransportProperties(Collections.singletonMap( "bar", "baz")); + maxLatency = Integer.MAX_VALUE; contactId = new ContactId(234); contact = new Contact(contactId, author, localAuthorId); endpoint = new Endpoint(contactId, transportId, 123, true); @@ -691,11 +693,11 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).getRawMessage(txn, messageId); will(returnValue(raw)); oneOf(database).updateExpiryTime(txn, contactId, messageId, - Long.MAX_VALUE); + maxLatency); oneOf(database).getRawMessage(txn, messageId1); will(returnValue(raw1)); oneOf(database).updateExpiryTime(txn, contactId, messageId1, - Long.MAX_VALUE); + maxLatency); oneOf(database).lowerRequestedFlag(txn, contactId, ids); oneOf(database).commitTransaction(txn); }}); @@ -703,7 +705,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { eventBus, shutdown); assertEquals(messages, db.generateBatch(contactId, size * 2, - Long.MAX_VALUE)); + maxLatency)); context.assertIsSatisfied(); } @@ -726,15 +728,15 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).getMessagesToOffer(txn, contactId, 123); will(returnValue(ids)); oneOf(database).updateExpiryTime(txn, contactId, messageId, - Long.MAX_VALUE); + maxLatency); oneOf(database).updateExpiryTime(txn, contactId, messageId1, - Long.MAX_VALUE); + maxLatency); oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, eventBus, shutdown); - Offer o = db.generateOffer(contactId, 123, Long.MAX_VALUE); + Offer o = db.generateOffer(contactId, 123, maxLatency); assertEquals(ids, o.getMessageIds()); context.assertIsSatisfied(); @@ -791,11 +793,11 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).getRawMessage(txn, messageId); will(returnValue(raw)); oneOf(database).updateExpiryTime(txn, contactId, messageId, - Long.MAX_VALUE); + maxLatency); oneOf(database).getRawMessage(txn, messageId1); will(returnValue(raw1)); oneOf(database).updateExpiryTime(txn, contactId, messageId1, - Long.MAX_VALUE); + maxLatency); oneOf(database).lowerRequestedFlag(txn, contactId, ids); oneOf(database).commitTransaction(txn); }}); @@ -803,7 +805,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { eventBus, shutdown); assertEquals(messages, db.generateRequestedBatch(contactId, size * 2, - Long.MAX_VALUE)); + maxLatency)); context.assertIsSatisfied(); } @@ -821,14 +823,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(txn)); oneOf(database).containsContact(txn, contactId); will(returnValue(true)); - oneOf(database).getRetentionUpdate(txn, contactId, Long.MAX_VALUE); + oneOf(database).getRetentionUpdate(txn, contactId, maxLatency); will(returnValue(null)); oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, eventBus, shutdown); - assertNull(db.generateRetentionUpdate(contactId, Long.MAX_VALUE)); + assertNull(db.generateRetentionUpdate(contactId, maxLatency)); context.assertIsSatisfied(); } @@ -846,15 +848,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(txn)); oneOf(database).containsContact(txn, contactId); will(returnValue(true)); - oneOf(database).getRetentionUpdate(txn, contactId, Long.MAX_VALUE); + oneOf(database).getRetentionUpdate(txn, contactId, maxLatency); will(returnValue(new RetentionUpdate(0, 1))); oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, eventBus, shutdown); - RetentionUpdate u = db.generateRetentionUpdate(contactId, - Long.MAX_VALUE); + RetentionUpdate u = db.generateRetentionUpdate(contactId, maxLatency); assertEquals(0, u.getRetentionTime()); assertEquals(1, u.getVersion()); @@ -874,15 +875,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(txn)); oneOf(database).containsContact(txn, contactId); will(returnValue(true)); - oneOf(database).getSubscriptionUpdate(txn, contactId, - Long.MAX_VALUE); + oneOf(database).getSubscriptionUpdate(txn, contactId, maxLatency); will(returnValue(null)); oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, eventBus, shutdown); - assertNull(db.generateSubscriptionUpdate(contactId, Long.MAX_VALUE)); + assertNull(db.generateSubscriptionUpdate(contactId, maxLatency)); context.assertIsSatisfied(); } @@ -900,8 +900,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(txn)); oneOf(database).containsContact(txn, contactId); will(returnValue(true)); - oneOf(database).getSubscriptionUpdate(txn, contactId, - Long.MAX_VALUE); + oneOf(database).getSubscriptionUpdate(txn, contactId, maxLatency); will(returnValue(new SubscriptionUpdate(Arrays.asList(group), 1))); oneOf(database).commitTransaction(txn); }}); @@ -909,7 +908,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { eventBus, shutdown); SubscriptionUpdate u = db.generateSubscriptionUpdate(contactId, - Long.MAX_VALUE); + maxLatency); assertEquals(Arrays.asList(group), u.getGroups()); assertEquals(1, u.getVersion()); @@ -929,14 +928,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(txn)); oneOf(database).containsContact(txn, contactId); will(returnValue(true)); - oneOf(database).getTransportUpdates(txn, contactId, Long.MAX_VALUE); + oneOf(database).getTransportUpdates(txn, contactId, maxLatency); will(returnValue(null)); oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, eventBus, shutdown); - assertNull(db.generateTransportUpdates(contactId, Long.MAX_VALUE)); + assertNull(db.generateTransportUpdates(contactId, maxLatency)); context.assertIsSatisfied(); } @@ -954,7 +953,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(txn)); oneOf(database).containsContact(txn, contactId); will(returnValue(true)); - oneOf(database).getTransportUpdates(txn, contactId, Long.MAX_VALUE); + oneOf(database).getTransportUpdates(txn, contactId, maxLatency); will(returnValue(Arrays.asList(new TransportUpdate(transportId, transportProperties, 1)))); oneOf(database).commitTransaction(txn); @@ -963,7 +962,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { eventBus, shutdown); Collection<TransportUpdate> updates = - db.generateTransportUpdates(contactId, Long.MAX_VALUE); + db.generateTransportUpdates(contactId, maxLatency); assertNotNull(updates); assertEquals(1, updates.size()); TransportUpdate u = updates.iterator().next(); diff --git a/briar-tests/src/org/briarproject/db/ExponentialBackoffTest.java b/briar-tests/src/org/briarproject/db/ExponentialBackoffTest.java index 923939e304409933a61520588f800dfdc94aa35a..f758e8c9e0c0be5ab1150d9f8cf58d44be5a2cd7 100644 --- a/briar-tests/src/org/briarproject/db/ExponentialBackoffTest.java +++ b/briar-tests/src/org/briarproject/db/ExponentialBackoffTest.java @@ -32,32 +32,30 @@ public class ExponentialBackoffTest extends BriarTestCase { assertEquals(now, fromNow - fromZero); } - @Test - public void testRoundTripTimeOverflow() { - long maxLatency = Long.MAX_VALUE / 2 + 1; // RTT will overflow - long expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 0); - assertEquals(Long.MAX_VALUE, expiry); // Overflow caught - } - @Test public void testTransmissionCountOverflow() { - long maxLatency = (Long.MAX_VALUE - 1) / 2; // RTT will not overflow + int maxLatency = Integer.MAX_VALUE; // RTT will not overflow long expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 0); - assertEquals(Long.MAX_VALUE - 1, expiry); // No overflow - expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 1); + assertEquals(Integer.MAX_VALUE * 2L, expiry); // No overflow + expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 31); + assertEquals(Integer.MAX_VALUE * (2L << 31), expiry); // No overflow + expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 32); assertEquals(Long.MAX_VALUE, expiry); // Overflow caught - expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 2); + expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 33); assertEquals(Long.MAX_VALUE, expiry); // Overflow caught } @Test public void testCurrentTimeOverflow() { - long maxLatency = (Long.MAX_VALUE - 1) / 2; // RTT will not overflow - long expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 0); + int maxLatency = Integer.MAX_VALUE; // RTT will not overflow + long now = Long.MAX_VALUE - (Integer.MAX_VALUE * (2L << 31)); + long expiry = ExponentialBackoff.calculateExpiry(now, maxLatency, 0); + assertEquals(now + Integer.MAX_VALUE * 2L, expiry); // No overflow + expiry = ExponentialBackoff.calculateExpiry(now - 1, maxLatency, 31); assertEquals(Long.MAX_VALUE - 1, expiry); // No overflow - expiry = ExponentialBackoff.calculateExpiry(1, maxLatency, 0); + expiry = ExponentialBackoff.calculateExpiry(now, maxLatency, 31); assertEquals(Long.MAX_VALUE, expiry); // No overflow - expiry = ExponentialBackoff.calculateExpiry(2, maxLatency, 0); + expiry = ExponentialBackoff.calculateExpiry(now + 1, maxLatency, 32); assertEquals(Long.MAX_VALUE, expiry); // Overflow caught } } diff --git a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java index 3ecaf692a1501f0bc085b8d2c25c463bfce7828c..b567c2778ca97c8473bed2d2a2ba5b5d486f59f2 100644 --- a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java +++ b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java @@ -381,7 +381,7 @@ public class H2DatabaseTest extends BriarTestCase { assertTrue(it.hasNext()); assertEquals(messageId, it.next()); assertFalse(it.hasNext()); - db.updateExpiryTime(txn, contactId, messageId, Long.MAX_VALUE); + db.updateExpiryTime(txn, contactId, messageId, Integer.MAX_VALUE); // The message should no longer be sendable it = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE).iterator(); @@ -1109,7 +1109,8 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testTemporarySecrets() throws Exception { // Create an endpoint and four consecutive temporary secrets - long epoch = 123, latency = 234; + long epoch = 123; + int latency = 234; boolean alice = false; long outgoing1 = 345, centre1 = 456; long outgoing2 = 567, centre2 = 678; @@ -1235,7 +1236,8 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testIncrementStreamCounter() throws Exception { // Create an endpoint and a temporary secret - long epoch = 123, latency = 234; + long epoch = 123; + int latency = 234; boolean alice = false; long period = 345, outgoing = 456, centre = 567; Endpoint ep = new Endpoint(contactId, transportId, epoch, alice); @@ -1290,7 +1292,8 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSetReorderingWindow() throws Exception { // Create an endpoint and a temporary secret - long epoch = 123, latency = 234; + long epoch = 123; + int latency = 234; boolean alice = false; long period = 345, outgoing = 456, centre = 567; Endpoint ep = new Endpoint(contactId, transportId, epoch, alice); @@ -1359,8 +1362,8 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testEndpoints() throws Exception { // Create some endpoints - long epoch1 = 123, latency1 = 234; - long epoch2 = 345, latency2 = 456; + long epoch1 = 123, epoch2 = 234; + int latency1 = 345, latency2 = 456; boolean alice1 = true, alice2 = false; TransportId transportId1 = new TransportId("bar"); TransportId transportId2 = new TransportId("baz"); diff --git a/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java b/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java index 4d5b25767efae6b90f9a055a6d6f99eac920aba9..75583607001c8e619b3dd5771deb991acec71b8a 100644 --- a/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java +++ b/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java @@ -2,12 +2,14 @@ package org.briarproject.messaging; import static org.briarproject.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.api.messaging.MessagingConstants.GROUP_SALT_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Random; import org.briarproject.BriarTestCase; @@ -32,13 +34,13 @@ import org.briarproject.api.messaging.Message; import org.briarproject.api.messaging.MessageFactory; import org.briarproject.api.messaging.MessageVerifier; import org.briarproject.api.messaging.MessagingSession; +import org.briarproject.api.messaging.PacketReader; import org.briarproject.api.messaging.PacketReaderFactory; +import org.briarproject.api.messaging.PacketWriter; import org.briarproject.api.messaging.PacketWriterFactory; import org.briarproject.api.transport.Endpoint; import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.api.transport.TagRecogniser; import org.briarproject.crypto.CryptoModule; @@ -56,8 +58,9 @@ import com.google.inject.Injector; public class SimplexMessagingIntegrationTest extends BriarTestCase { - private static final long CLOCK_DIFFERENCE = 60 * 1000; - private static final long LATENCY = 60 * 1000; + private static final int MAX_LATENCY = 2 * 60 * 1000; // 2 minutes + private static final int ROTATION_PERIOD = + MAX_CLOCK_DIFFERENCE + MAX_LATENCY; private final File testDir = TestUtils.getTestDirectory(); private final File aliceDir = new File(testDir, "alice"); @@ -73,8 +76,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { // Create matching secrets for Alice and Bob initialSecret = new byte[32]; new Random().nextBytes(initialSecret); - long rotationPeriod = 2 * CLOCK_DIFFERENCE + LATENCY; - epoch = System.currentTimeMillis() - 2 * rotationPeriod; + epoch = System.currentTimeMillis() - 2 * ROTATION_PERIOD; } @Override @@ -121,10 +123,10 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { db.addGroup(group); db.setInboxGroup(contactId, group); // Add the transport and the endpoint - db.addTransport(transportId, LATENCY); + db.addTransport(transportId, MAX_LATENCY); Endpoint ep = new Endpoint(contactId, transportId, epoch, true); db.addEndpoint(ep); - keyManager.endpointAdded(ep, LATENCY, initialSecret.clone()); + keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret); // Send Bob a message String contentType = "text/plain"; long timestamp = System.currentTimeMillis(); @@ -133,25 +135,27 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { Message message = messageFactory.createAnonymousMessage(null, group, contentType, timestamp, body); db.addLocalMessage(message); + // Get a stream context + StreamContext ctx = keyManager.getStreamContext(contactId, transportId); + assertNotNull(ctx); // Create a stream writer ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamWriterFactory streamWriterFactory = alice.getInstance(StreamWriterFactory.class); - StreamContext ctx = keyManager.getStreamContext(contactId, transportId); - assertNotNull(ctx); - StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out, - MAX_FRAME_LENGTH, ctx); + OutputStream streamWriter = + streamWriterFactory.createStreamWriter(out, ctx); // Create an outgoing messaging session EventBus eventBus = alice.getInstance(EventBus.class); PacketWriterFactory packetWriterFactory = alice.getInstance(PacketWriterFactory.class); + PacketWriter packetWriter = packetWriterFactory.createPacketWriter( + streamWriter); MessagingSession session = new SimplexOutgoingSession(db, - new ImmediateExecutor(), eventBus, packetWriterFactory, - contactId, transportId, Long.MAX_VALUE, - streamWriter.getOutputStream()); + new ImmediateExecutor(), eventBus, contactId, transportId, + MAX_LATENCY, packetWriter); // Write whatever needs to be written session.run(); - streamWriter.getOutputStream().close(); + streamWriter.close(); // Clean up keyManager.stop(); db.close(); @@ -182,10 +186,10 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { db.addGroup(group); db.setInboxGroup(contactId, group); // Add the transport and the endpoint - db.addTransport(transportId, LATENCY); + db.addTransport(transportId, MAX_LATENCY); Endpoint ep = new Endpoint(contactId, transportId, epoch, false); db.addEndpoint(ep); - keyManager.endpointAdded(ep, LATENCY, initialSecret.clone()); + keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret); // Set up an event listener MessageListener listener = new MessageListener(); bob.getInstance(EventBus.class).addListener(listener); @@ -200,23 +204,24 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { // Create a stream reader StreamReaderFactory streamReaderFactory = bob.getInstance(StreamReaderFactory.class); - StreamReader streamReader = streamReaderFactory.createStreamReader(in, - MAX_FRAME_LENGTH, ctx); + InputStream streamReader = + streamReaderFactory.createStreamReader(in, ctx); // Create an incoming messaging session EventBus eventBus = bob.getInstance(EventBus.class); MessageVerifier messageVerifier = bob.getInstance(MessageVerifier.class); PacketReaderFactory packetReaderFactory = bob.getInstance(PacketReaderFactory.class); + PacketReader packetReader = packetReaderFactory.createPacketReader( + streamReader); MessagingSession session = new IncomingSession(db, new ImmediateExecutor(), new ImmediateExecutor(), eventBus, - messageVerifier, packetReaderFactory, contactId, transportId, - streamReader.getInputStream()); + messageVerifier, contactId, transportId, packetReader); // No messages should have been added yet assertFalse(listener.messageAdded); // Read whatever needs to be read session.run(); - streamReader.getInputStream().close(); + streamReader.close(); // The private message from Alice should have been added assertTrue(listener.messageAdded); // Clean up diff --git a/briar-tests/src/org/briarproject/messaging/SimplexOutgoingSessionTest.java b/briar-tests/src/org/briarproject/messaging/SimplexOutgoingSessionTest.java index f9fa68c6fdd0efef239201b32a131848093983de..fa13af51024f6cdc9938635c41ff3def51032182 100644 --- a/briar-tests/src/org/briarproject/messaging/SimplexOutgoingSessionTest.java +++ b/briar-tests/src/org/briarproject/messaging/SimplexOutgoingSessionTest.java @@ -1,72 +1,53 @@ package org.briarproject.messaging; -import java.io.ByteArrayOutputStream; import java.util.Arrays; -import java.util.Random; import java.util.concurrent.Executor; import org.briarproject.BriarTestCase; import org.briarproject.TestUtils; import org.briarproject.api.ContactId; import org.briarproject.api.TransportId; -import org.briarproject.api.UniqueId; import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.event.EventBus; import org.briarproject.api.messaging.Ack; import org.briarproject.api.messaging.MessageId; -import org.briarproject.api.messaging.PacketWriterFactory; +import org.briarproject.api.messaging.PacketWriter; import org.briarproject.plugins.ImmediateExecutor; -import org.briarproject.serial.SerialModule; import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.Test; -import com.google.inject.AbstractModule; -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.Module; - public class SimplexOutgoingSessionTest extends BriarTestCase { - // FIXME: This is an integration test, not a unit test + private static final int MAX_MESSAGES_PER_ACK = 10; private final Mockery context; private final DatabaseComponent db; private final Executor dbExecutor; private final EventBus eventBus; - private final PacketWriterFactory packetWriterFactory; private final ContactId contactId; private final TransportId transportId; private final MessageId messageId; - private final byte[] secret; + private final int maxLatency; + private final PacketWriter packetWriter; public SimplexOutgoingSessionTest() { context = new Mockery(); db = context.mock(DatabaseComponent.class); dbExecutor = new ImmediateExecutor(); - Module testModule = new AbstractModule() { - @Override - public void configure() { - bind(PacketWriterFactory.class).to( - PacketWriterFactoryImpl.class); - } - }; - Injector i = Guice.createInjector(testModule, new SerialModule()); eventBus = context.mock(EventBus.class); - packetWriterFactory = i.getInstance(PacketWriterFactory.class); + packetWriter = context.mock(PacketWriter.class); contactId = new ContactId(234); transportId = new TransportId("id"); messageId = new MessageId(TestUtils.getRandomId()); - secret = new byte[32]; - new Random().nextBytes(secret); + maxLatency = Integer.MAX_VALUE; } @Test public void testNothingToSend() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); final SimplexOutgoingSession session = new SimplexOutgoingSession(db, - dbExecutor, eventBus, packetWriterFactory, contactId, - transportId, Long.MAX_VALUE, out); + dbExecutor, eventBus, contactId, transportId, maxLatency, + packetWriter); context.checking(new Expectations() {{ // Add listener oneOf(eventBus).addListener(session); @@ -74,46 +55,45 @@ public class SimplexOutgoingSessionTest extends BriarTestCase { oneOf(db).generateTransportAcks(contactId); will(returnValue(null)); // No transport updates to send - oneOf(db).generateTransportUpdates(with(contactId), - with(any(long.class))); + oneOf(db).generateTransportUpdates(contactId, maxLatency); will(returnValue(null)); // No subscription ack to send oneOf(db).generateSubscriptionAck(contactId); will(returnValue(null)); // No subscription update to send - oneOf(db).generateSubscriptionUpdate(with(contactId), - with(any(long.class))); + oneOf(db).generateSubscriptionUpdate(contactId, maxLatency); will(returnValue(null)); // No retention ack to send oneOf(db).generateRetentionAck(contactId); will(returnValue(null)); // No retention update to send - oneOf(db).generateRetentionUpdate(with(contactId), - with(any(long.class))); + oneOf(db).generateRetentionUpdate(contactId, maxLatency); will(returnValue(null)); // No acks to send - oneOf(db).generateAck(with(contactId), with(any(int.class))); + oneOf(packetWriter).getMaxMessagesForAck(with(any(long.class))); + will(returnValue(MAX_MESSAGES_PER_ACK)); + oneOf(db).generateAck(contactId, MAX_MESSAGES_PER_ACK); will(returnValue(null)); // No messages to send oneOf(db).generateBatch(with(contactId), with(any(int.class)), - with(any(long.class))); + with(maxLatency)); will(returnValue(null)); + // Flush the output stream + oneOf(packetWriter).flush(); // Remove listener oneOf(eventBus).removeListener(session); }}); session.run(); - // Nothing should have been written - assertEquals(0, out.size()); context.assertIsSatisfied(); } @Test public void testSomethingToSend() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - final SimplexOutgoingSession session = new SimplexOutgoingSession(db, - dbExecutor, eventBus, packetWriterFactory, contactId, - transportId, Long.MAX_VALUE, out); + final Ack ack = new Ack(Arrays.asList(messageId)); final byte[] raw = new byte[1234]; + final SimplexOutgoingSession session = new SimplexOutgoingSession(db, + dbExecutor, eventBus, contactId, transportId, maxLatency, + packetWriter); context.checking(new Expectations() {{ // Add listener oneOf(eventBus).addListener(session); @@ -121,43 +101,46 @@ public class SimplexOutgoingSessionTest extends BriarTestCase { oneOf(db).generateTransportAcks(contactId); will(returnValue(null)); // No transport updates to send - oneOf(db).generateTransportUpdates(with(contactId), - with(any(long.class))); + oneOf(db).generateTransportUpdates(contactId, maxLatency); will(returnValue(null)); // No subscription ack to send oneOf(db).generateSubscriptionAck(contactId); will(returnValue(null)); // No subscription update to send - oneOf(db).generateSubscriptionUpdate(with(contactId), - with(any(long.class))); + oneOf(db).generateSubscriptionUpdate(contactId, maxLatency); will(returnValue(null)); // No retention ack to send oneOf(db).generateRetentionAck(contactId); will(returnValue(null)); // No retention update to send - oneOf(db).generateRetentionUpdate(with(contactId), - with(any(long.class))); + oneOf(db).generateRetentionUpdate(contactId, maxLatency); will(returnValue(null)); // One ack to send - oneOf(db).generateAck(with(contactId), with(any(int.class))); - will(returnValue(new Ack(Arrays.asList(messageId)))); + oneOf(packetWriter).getMaxMessagesForAck(with(any(long.class))); + will(returnValue(MAX_MESSAGES_PER_ACK)); + oneOf(db).generateAck(contactId, MAX_MESSAGES_PER_ACK); + will(returnValue(ack)); + oneOf(packetWriter).writeAck(ack); // No more acks - oneOf(db).generateAck(with(contactId), with(any(int.class))); + oneOf(packetWriter).getMaxMessagesForAck(with(any(long.class))); + will(returnValue(MAX_MESSAGES_PER_ACK)); + oneOf(db).generateAck(contactId, MAX_MESSAGES_PER_ACK); will(returnValue(null)); // One message to send oneOf(db).generateBatch(with(contactId), with(any(int.class)), - with(any(long.class))); + with(maxLatency)); will(returnValue(Arrays.asList(raw))); + oneOf(packetWriter).writeMessage(raw); // No more messages oneOf(db).generateBatch(with(contactId), with(any(int.class)), - with(any(long.class))); + with(maxLatency)); will(returnValue(null)); + // Flush the output stream + oneOf(packetWriter).flush(); // Remove listener oneOf(eventBus).removeListener(session); }}); session.run(); - // Something should have been written - assertTrue(out.size() > UniqueId.LENGTH + raw.length); context.assertIsSatisfied(); } } diff --git a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java index 2e088fa3066b09da872c8c1adb5fb9db60ed8efa..8dab883cb74cade22f78f7e63d188c9156a2d5e8 100644 --- a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java +++ b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java @@ -44,19 +44,19 @@ public class PluginManagerImplTest extends BriarTestCase { context.mock(SimplexPluginFactory.class); final SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class); final TransportId simplexId = new TransportId("simplex"); - final long simplexLatency = 12345; + final int simplexLatency = 12345; final SimplexPluginFactory simplexFailFactory = context.mock(SimplexPluginFactory.class, "simplexFailFactory"); final SimplexPlugin simplexFailPlugin = context.mock(SimplexPlugin.class, "simplexFailPlugin"); final TransportId simplexFailId = new TransportId("simplex1"); - final long simplexFailLatency = 23456; + final int simplexFailLatency = 23456; // Two duplex plugin factories: one creates a plugin, the other fails final DuplexPluginFactory duplexFactory = context.mock(DuplexPluginFactory.class); final DuplexPlugin duplexPlugin = context.mock(DuplexPlugin.class); final TransportId duplexId = new TransportId("duplex"); - final long duplexLatency = 34567; + final int duplexLatency = 34567; final DuplexPluginFactory duplexFailFactory = context.mock(DuplexPluginFactory.class, "duplexFailFactory"); final TransportId duplexFailId = new TransportId("duplex1"); diff --git a/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothClientTest.java b/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothClientTest.java index 3b86da8a1bd3a8f80c79445f80d14082261921c9..b046f3a6bc615862785b3c9c564c45342fd25bdd 100644 --- a/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothClientTest.java +++ b/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothClientTest.java @@ -23,12 +23,12 @@ public class BluetoothClientTest extends DuplexClientTest { p.put("address", serverAddress); p.put("uuid", BluetoothTest.EMPTY_UUID); Map<ContactId, TransportProperties> remote = - Collections.singletonMap(contactId, p); + Collections.singletonMap(contactId, p); // Create the plugin callback = new ClientCallback(new TransportConfig(), new TransportProperties(), remote); plugin = new BluetoothPlugin(executor, new SystemClock(), - new SecureRandom(), callback, 0, 0, 0); + new SecureRandom(), callback, 0, 0); } public static void main(String[] args) throws Exception { diff --git a/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothServerTest.java b/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothServerTest.java index 1dfbf841dcc52f64bf50f2e94e2d5a5422186c8a..121f71b4031db95c9a114cc1540e7397e565f405 100644 --- a/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothServerTest.java +++ b/briar-tests/src/org/briarproject/plugins/bluetooth/BluetoothServerTest.java @@ -23,7 +23,7 @@ public class BluetoothServerTest extends DuplexServerTest { callback = new ServerCallback(new TransportConfig(), local, Collections.singletonMap(contactId, new TransportProperties())); plugin = new BluetoothPlugin(executor, new SystemClock(), - new SecureRandom(), callback, 0, 0, 0); + new SecureRandom(), callback, 0, 0); } public static void main(String[] args) throws Exception { diff --git a/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java b/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java index b8e4ee88b24ac4fe00e2a7babdb3c973d32f465e..5ae0bbb5030235a60ebb45aa0e05fb146155a34a 100644 --- a/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java +++ b/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java @@ -1,6 +1,5 @@ package org.briarproject.plugins.file; -import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static org.briarproject.api.transport.TransportConstants.MIN_STREAM_LENGTH; import java.io.File; @@ -58,7 +57,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { }}); RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, - fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0); + fileUtils, callback, finder, monitor, 0); plugin.start(); assertNull(plugin.createWriter(contactId)); @@ -93,7 +92,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { }}); RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, - fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0); + fileUtils, callback, finder, monitor, 0); plugin.start(); assertNull(plugin.createWriter(contactId)); @@ -130,7 +129,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { }}); RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, - fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0); + fileUtils, callback, finder, monitor, 0); plugin.start(); assertNull(plugin.createWriter(contactId)); @@ -169,7 +168,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { }}); RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, - fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0); + fileUtils, callback, finder, monitor, 0); plugin.start(); assertNull(plugin.createWriter(contactId)); @@ -208,7 +207,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { }}); RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, - fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0); + fileUtils, callback, finder, monitor, 0); plugin.start(); assertNotNull(plugin.createWriter(contactId)); @@ -251,7 +250,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { }}); RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, - fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0); + fileUtils, callback, finder, monitor, 0); plugin.start(); TransportConnectionWriter writer = plugin.createWriter(contactId); @@ -290,7 +289,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { }}); RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, - fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0); + fileUtils, callback, finder, monitor, 0); plugin.start(); plugin.driveInserted(testDir); @@ -310,7 +309,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { context.mock(RemovableDriveMonitor.class); RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, - fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0); + fileUtils, callback, finder, monitor, 0); assertFalse(plugin.isPossibleConnectionFilename("abcdefg.dat")); assertFalse(plugin.isPossibleConnectionFilename("abcdefghi.dat")); @@ -339,7 +338,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { RemovableDrivePlugin plugin = new RemovableDrivePlugin( new ImmediateExecutor(), fileUtils, callback, finder, monitor, - MAX_FRAME_LENGTH, 0); + 0); plugin.start(); File f = new File(testDir, "abcdefgh.dat"); diff --git a/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java b/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java index 3371bd7e0dfe715f69cbaf55c3bfef7e4ff2afbb..7966ef8c489d4de3d23f7a11786a1f6ad4f6e702 100644 --- a/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java +++ b/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java @@ -24,7 +24,7 @@ public class ModemPluginTest extends BriarTestCase { final SerialPortList serialPortList = context.mock(SerialPortList.class); final ModemPlugin plugin = new ModemPlugin(modemFactory, - serialPortList, null, 0, 0, 0); + serialPortList, null, 0); final Modem modem = context.mock(Modem.class); context.checking(new Expectations() {{ oneOf(serialPortList).getPortNames(); @@ -58,7 +58,7 @@ public class ModemPluginTest extends BriarTestCase { final DuplexPluginCallback callback = context.mock(DuplexPluginCallback.class); final ModemPlugin plugin = new ModemPlugin(modemFactory, - serialPortList, callback, 0, 0, 0); + serialPortList, callback, 0); final Modem modem = context.mock(Modem.class); final TransportProperties local = new TransportProperties(); local.put("iso3166", ISO_1336); @@ -99,7 +99,7 @@ public class ModemPluginTest extends BriarTestCase { final DuplexPluginCallback callback = context.mock(DuplexPluginCallback.class); final ModemPlugin plugin = new ModemPlugin(modemFactory, - serialPortList, callback, 0, 0, 0); + serialPortList, callback, 0); final Modem modem = context.mock(Modem.class); final TransportProperties local = new TransportProperties(); local.put("iso3166", ISO_1336); @@ -140,7 +140,7 @@ public class ModemPluginTest extends BriarTestCase { final DuplexPluginCallback callback = context.mock(DuplexPluginCallback.class); final ModemPlugin plugin = new ModemPlugin(modemFactory, - serialPortList, callback, 0, 0, 0); + serialPortList, callback, 0); final Modem modem = context.mock(Modem.class); final TransportProperties local = new TransportProperties(); local.put("iso3166", ISO_1336); diff --git a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpClientTest.java b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpClientTest.java index 8991467a9188b5034326c938f358bd57ae514f64..19168a5675aec21ec229602734a213081ac41285 100644 --- a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpClientTest.java +++ b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpClientTest.java @@ -15,6 +15,10 @@ import org.briarproject.plugins.DuplexClientTest; // is running on another machine public class LanTcpClientTest extends DuplexClientTest { + private static final int MAX_LATENCY = 60 * 1000; + private static final int MAX_IDLE_TIME = 30 * 1000; + private static final int POLLING_INTERVAL = 60 * 1000; + private LanTcpClientTest(Executor executor, String serverAddress, String serverPort) { // Store the server's internal address and port @@ -22,11 +26,12 @@ public class LanTcpClientTest extends DuplexClientTest { p.put("address", serverAddress); p.put("port", serverPort); Map<ContactId, TransportProperties> remote = - Collections.singletonMap(contactId, p); + Collections.singletonMap(contactId, p); // Create the plugin callback = new ClientCallback(new TransportConfig(), new TransportProperties(), remote); - plugin = new LanTcpPlugin(executor, callback, 0, 0, 0); + plugin = new LanTcpPlugin(executor, callback, MAX_LATENCY, + MAX_IDLE_TIME, POLLING_INTERVAL); } public static void main(String[] args) throws Exception { diff --git a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpServerTest.java b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpServerTest.java index ff96ae16d2a15848ee48fe3fda1b123b61b1c513..7531978148639e5b2b1eb232ecacbb8caf505582 100644 --- a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpServerTest.java +++ b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpServerTest.java @@ -13,11 +13,16 @@ import org.briarproject.plugins.DuplexServerTest; // is running on another machine public class LanTcpServerTest extends DuplexServerTest { + private static final int MAX_LATENCY = 60 * 1000; + private static final int MAX_IDLE_TIME = 30 * 1000; + private static final int POLLING_INTERVAL = 60 * 1000; + private LanTcpServerTest(Executor executor) { callback = new ServerCallback(new TransportConfig(), new TransportProperties(), Collections.singletonMap(contactId, new TransportProperties())); - plugin = new LanTcpPlugin(executor, callback, 0, 0, 0); + plugin = new LanTcpPlugin(executor, callback, MAX_LATENCY, + MAX_IDLE_TIME, POLLING_INTERVAL); } public static void main(String[] args) throws Exception { diff --git a/briar-tests/src/org/briarproject/transport/IncomingEncryptionLayerTest.java b/briar-tests/src/org/briarproject/transport/IncomingEncryptionLayerTest.java deleted file mode 100644 index 8e1791785632439f24962584d36c03e6d41f06de..0000000000000000000000000000000000000000 --- a/briar-tests/src/org/briarproject/transport/IncomingEncryptionLayerTest.java +++ /dev/null @@ -1,183 +0,0 @@ -package org.briarproject.transport; - -import static org.briarproject.api.transport.TransportConstants.AAD_LENGTH; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.IV_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; - -import java.io.ByteArrayInputStream; - -import org.briarproject.BriarTestCase; -import org.briarproject.TestLifecycleModule; -import org.briarproject.TestSystemModule; -import org.briarproject.api.FormatException; -import org.briarproject.api.crypto.AuthenticatedCipher; -import org.briarproject.api.crypto.CryptoComponent; -import org.briarproject.api.crypto.SecretKey; -import org.briarproject.crypto.CryptoModule; -import org.junit.Test; - -import com.google.inject.Guice; -import com.google.inject.Injector; - -public class IncomingEncryptionLayerTest extends BriarTestCase { - - // FIXME: This is an integration test, not a unit test - - private static final int FRAME_LENGTH = 1024; - private static final int MAX_PAYLOAD_LENGTH = - FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; - - private final CryptoComponent crypto; - private final AuthenticatedCipher frameCipher; - private final SecretKey frameKey; - - public IncomingEncryptionLayerTest() { - Injector i = Guice.createInjector(new CryptoModule(), - new TestLifecycleModule(), new TestSystemModule()); - crypto = i.getInstance(CryptoComponent.class); - frameCipher = crypto.getFrameCipher(); - frameKey = crypto.generateSecretKey(); - } - - @Test - public void testReadValidFrames() throws Exception { - // Generate two valid frames - byte[] frame = generateFrame(0, FRAME_LENGTH, 123, false, false); - byte[] frame1 = generateFrame(1, FRAME_LENGTH, 123, false, false); - // Concatenate the frames - byte[] valid = new byte[FRAME_LENGTH * 2]; - System.arraycopy(frame, 0, valid, 0, FRAME_LENGTH); - System.arraycopy(frame1, 0, valid, FRAME_LENGTH, FRAME_LENGTH); - // Read the frames - ByteArrayInputStream in = new ByteArrayInputStream(valid); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, - frameKey, FRAME_LENGTH); - byte[] buf = new byte[FRAME_LENGTH - MAC_LENGTH]; - assertEquals(123, i.readFrame(buf)); - assertEquals(123, i.readFrame(buf)); - } - - @Test - public void testTruncatedFrameThrowsException() throws Exception { - // Generate a valid frame - byte[] frame = generateFrame(0, FRAME_LENGTH, 123, false, false); - // Chop off the last byte - byte[] truncated = new byte[FRAME_LENGTH - 1]; - System.arraycopy(frame, 0, truncated, 0, FRAME_LENGTH - 1); - // Try to read the frame, which should fail due to truncation - ByteArrayInputStream in = new ByteArrayInputStream(truncated); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, - frameKey, FRAME_LENGTH); - try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); - fail(); - } catch(FormatException expected) {} - } - - @Test - public void testModifiedFrameThrowsException() throws Exception { - // Generate a valid frame - byte[] frame = generateFrame(0, FRAME_LENGTH, 123, false, false); - // Modify a randomly chosen byte of the frame - frame[(int) (Math.random() * FRAME_LENGTH)] ^= 1; - // Try to read the frame, which should fail due to modification - ByteArrayInputStream in = new ByteArrayInputStream(frame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, - frameKey, FRAME_LENGTH); - try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); - fail(); - } catch(FormatException expected) {} - } - - @Test - public void testShortNonFinalFrameThrowsException() throws Exception { - // Generate a short non-final frame - byte[] frame = generateFrame(0, FRAME_LENGTH - 1, 123, false, false); - // Try to read the frame, which should fail due to invalid length - ByteArrayInputStream in = new ByteArrayInputStream(frame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, - frameKey, FRAME_LENGTH); - try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); - fail(); - } catch(FormatException expected) {} - } - - @Test - public void testShortFinalFrameDoesNotThrowException() throws Exception { - // Generate a short final frame - byte[] frame = generateFrame(0, FRAME_LENGTH - 1, 123, true, false); - // Read the frame - ByteArrayInputStream in = new ByteArrayInputStream(frame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, - frameKey, FRAME_LENGTH); - int length = i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); - assertEquals(123, length); - } - - @Test - public void testInvalidPayloadLengthThrowsException() throws Exception { - // Generate a frame with an invalid payload length - byte[] frame = generateFrame(0, FRAME_LENGTH, MAX_PAYLOAD_LENGTH + 1, - false, false); - // Try to read the frame, which should fail due to invalid length - ByteArrayInputStream in = new ByteArrayInputStream(frame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, - frameKey, FRAME_LENGTH); - try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); - fail(); - } catch(FormatException expected) {} - } - - @Test - public void testNonZeroPaddingThrowsException() throws Exception { - // Generate a frame with bad padding - byte[] frame = generateFrame(0, FRAME_LENGTH, 123, false, true); - // Try to read the frame, which should fail due to bad padding - ByteArrayInputStream in = new ByteArrayInputStream(frame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, - frameKey, FRAME_LENGTH); - try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); - fail(); - } catch(FormatException expected) {} - } - - @Test - public void testCannotReadBeyondFinalFrame() throws Exception { - // Generate a valid final frame and another valid final frame after it - byte[] frame = generateFrame(0, FRAME_LENGTH, MAX_PAYLOAD_LENGTH, true, - false); - byte[] frame1 = generateFrame(1, FRAME_LENGTH, 123, true, false); - // Concatenate the frames - byte[] extraFrame = new byte[FRAME_LENGTH * 2]; - System.arraycopy(frame, 0, extraFrame, 0, FRAME_LENGTH); - System.arraycopy(frame1, 0, extraFrame, FRAME_LENGTH, FRAME_LENGTH); - // Read the final frame, which should first read the tag - ByteArrayInputStream in = new ByteArrayInputStream(extraFrame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, - frameKey, FRAME_LENGTH); - byte[] buf = new byte[FRAME_LENGTH - MAC_LENGTH]; - assertEquals(MAX_PAYLOAD_LENGTH, i.readFrame(buf)); - // The frame after the final frame should not be read - assertEquals(-1, i.readFrame(buf)); - } - - private byte[] generateFrame(long frameNumber, int frameLength, - int payloadLength, boolean finalFrame, boolean badPadding) - throws Exception { - byte[] iv = new byte[IV_LENGTH], aad = new byte[AAD_LENGTH]; - byte[] plaintext = new byte[frameLength - MAC_LENGTH]; - byte[] ciphertext = new byte[frameLength]; - FrameEncoder.encodeIv(iv, frameNumber); - FrameEncoder.encodeAad(aad, frameNumber, plaintext.length); - frameCipher.init(true, frameKey, iv, aad); - FrameEncoder.encodeHeader(plaintext, finalFrame, payloadLength); - if(badPadding) plaintext[HEADER_LENGTH + payloadLength] = 1; - frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0); - return ciphertext; - } -} diff --git a/briar-tests/src/org/briarproject/transport/KeyManagerImplTest.java b/briar-tests/src/org/briarproject/transport/KeyManagerImplTest.java index f11f11f154260870fa75d7fa5afe93224497394f..12880b1b8ea8789ce2c690f30eac6c2ed430e2d7 100644 --- a/briar-tests/src/org/briarproject/transport/KeyManagerImplTest.java +++ b/briar-tests/src/org/briarproject/transport/KeyManagerImplTest.java @@ -26,9 +26,9 @@ import org.junit.Test; public class KeyManagerImplTest extends BriarTestCase { private static final long EPOCH = 1000L * 1000L * 1000L * 1000L; - private static final long MAX_LATENCY = 2 * 60 * 1000; // 2 minutes - private static final long ROTATION_PERIOD_LENGTH = - MAX_LATENCY + MAX_CLOCK_DIFFERENCE; + private static final int MAX_LATENCY = 2 * 60 * 1000; // 2 minutes + private static final int ROTATION_PERIOD = + MAX_CLOCK_DIFFERENCE + MAX_LATENCY; private final ContactId contactId; private final TransportId transportId; @@ -103,9 +103,9 @@ public class KeyManagerImplTest extends BriarTestCase { // The secrets for periods 0 - 2 should be derived Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); context.checking(new Expectations() {{ // start() @@ -123,11 +123,11 @@ public class KeyManagerImplTest extends BriarTestCase { oneOf(clock).currentTimeMillis(); will(returnValue(EPOCH)); oneOf(crypto).deriveNextSecret(initialSecret, 0); - will(returnValue(secret0.clone())); + will(returnValue(secret0)); oneOf(crypto).deriveNextSecret(secret0, 1); - will(returnValue(secret1.clone())); + will(returnValue(secret1)); oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(db).addSecrets(Arrays.asList(s0, s1, s2)); // The secrets for periods 0 - 2 should be added to the recogniser oneOf(tagRecogniser).addSecret(s0); @@ -140,7 +140,7 @@ public class KeyManagerImplTest extends BriarTestCase { }}); assertTrue(keyManager.start()); - keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); + keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret); keyManager.stop(); context.assertIsSatisfied(); @@ -161,9 +161,9 @@ public class KeyManagerImplTest extends BriarTestCase { // The secrets for periods 0 - 2 should be derived Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); context.checking(new Expectations() {{ // start() @@ -181,11 +181,11 @@ public class KeyManagerImplTest extends BriarTestCase { oneOf(clock).currentTimeMillis(); will(returnValue(EPOCH)); oneOf(crypto).deriveNextSecret(initialSecret, 0); - will(returnValue(secret0.clone())); + will(returnValue(secret0)); oneOf(crypto).deriveNextSecret(secret0, 1); - will(returnValue(secret1.clone())); + will(returnValue(secret1)); oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(db).addSecrets(Arrays.asList(s0, s1, s2)); // The secrets for periods 0 - 2 should be added to the recogniser oneOf(tagRecogniser).addSecret(s0); @@ -201,7 +201,7 @@ public class KeyManagerImplTest extends BriarTestCase { }}); assertTrue(keyManager.start()); - keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); + keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret); StreamContext ctx = keyManager.getStreamContext(contactId, transportId); assertNotNull(ctx); @@ -230,9 +230,9 @@ public class KeyManagerImplTest extends BriarTestCase { // The DB contains the secrets for periods 0 - 2 Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); context.checking(new Expectations() {{ // start() @@ -278,11 +278,11 @@ public class KeyManagerImplTest extends BriarTestCase { // The DB contains the secrets for periods 0 - 2 Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); // The secret for period 3 should be derived and stored - final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone()); + final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3); context.checking(new Expectations() {{ // start() @@ -294,14 +294,14 @@ public class KeyManagerImplTest extends BriarTestCase { MAX_LATENCY))); // The current time is the start of period 2 oneOf(clock).currentTimeMillis(); - will(returnValue(EPOCH + ROTATION_PERIOD_LENGTH)); + will(returnValue(EPOCH + ROTATION_PERIOD)); // The secret for period 3 should be derived and stored oneOf(crypto).deriveNextSecret(secret0, 1); - will(returnValue(secret1.clone())); + will(returnValue(secret1)); oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(crypto).deriveNextSecret(secret2, 3); - will(returnValue(secret3.clone())); + will(returnValue(secret3)); oneOf(db).addSecrets(Arrays.asList(s3)); // The secrets for periods 1 - 3 should be added to the recogniser oneOf(tagRecogniser).addSecret(s1); @@ -336,12 +336,12 @@ public class KeyManagerImplTest extends BriarTestCase { // The DB contains the secrets for periods 0 - 2 Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); // The secrets for periods 3 and 4 should be derived and stored - final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone()); - final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4.clone()); + final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3); + final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4); context.checking(new Expectations() {{ // start() @@ -353,14 +353,14 @@ public class KeyManagerImplTest extends BriarTestCase { MAX_LATENCY))); // The current time is the end of period 3 oneOf(clock).currentTimeMillis(); - will(returnValue(EPOCH + 3 * ROTATION_PERIOD_LENGTH - 1)); + will(returnValue(EPOCH + 3 * ROTATION_PERIOD - 1)); // The secrets for periods 3 and 4 should be derived from secret 1 oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(crypto).deriveNextSecret(secret2, 3); - will(returnValue(secret3.clone())); + will(returnValue(secret3)); oneOf(crypto).deriveNextSecret(secret3, 4); - will(returnValue(secret4.clone())); + will(returnValue(secret4)); // The new secrets should be stored oneOf(db).addSecrets(Arrays.asList(s3, s4)); // The secrets for periods 2 - 4 should be added to the recogniser @@ -396,9 +396,9 @@ public class KeyManagerImplTest extends BriarTestCase { // The DB contains the secrets for periods 0 - 2 Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); context.checking(new Expectations() {{ // start() @@ -459,11 +459,11 @@ public class KeyManagerImplTest extends BriarTestCase { // The DB contains the secrets for periods 0 - 2 Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); // The secret for period 3 should be derived and stored - final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone()); + final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3); context.checking(new Expectations() {{ // start() @@ -484,13 +484,13 @@ public class KeyManagerImplTest extends BriarTestCase { with(any(long.class)), with(any(long.class))); // run() during period 2: the secrets should be rotated oneOf(clock).currentTimeMillis(); - will(returnValue(EPOCH + ROTATION_PERIOD_LENGTH + 1)); + will(returnValue(EPOCH + ROTATION_PERIOD + 1)); oneOf(crypto).deriveNextSecret(secret0, 1); - will(returnValue(secret1.clone())); + will(returnValue(secret1)); oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(crypto).deriveNextSecret(secret2, 3); - will(returnValue(secret3.clone())); + will(returnValue(secret3)); oneOf(tagRecogniser).removeSecret(contactId, transportId, 0); oneOf(db).addSecrets(Arrays.asList(s3)); oneOf(tagRecogniser).addSecret(s3); @@ -533,12 +533,12 @@ public class KeyManagerImplTest extends BriarTestCase { // The DB contains the secrets for periods 0 - 2 Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); // The secrets for periods 3 and 4 should be derived and stored - final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone()); - final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4.clone()); + final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3); + final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4); context.checking(new Expectations() {{ // start() @@ -559,13 +559,13 @@ public class KeyManagerImplTest extends BriarTestCase { with(any(long.class)), with(any(long.class))); // run() during period 3 (late): the secrets should be rotated oneOf(clock).currentTimeMillis(); - will(returnValue(EPOCH + 2 * ROTATION_PERIOD_LENGTH + 1)); + will(returnValue(EPOCH + 2 * ROTATION_PERIOD + 1)); oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(crypto).deriveNextSecret(secret2, 3); - will(returnValue(secret3.clone())); + will(returnValue(secret3)); oneOf(crypto).deriveNextSecret(secret3, 4); - will(returnValue(secret4.clone())); + will(returnValue(secret4)); oneOf(tagRecogniser).removeSecret(contactId, transportId, 0); oneOf(tagRecogniser).removeSecret(contactId, transportId, 1); oneOf(db).addSecrets(Arrays.asList(s3, s4)); diff --git a/briar-tests/src/org/briarproject/transport/KeyRotationIntegrationTest.java b/briar-tests/src/org/briarproject/transport/KeyRotationIntegrationTest.java index 1207f9363e85b7042050a0cc2f8779148c391cb1..84b6f89289d70d4b868a349d3765cdd266d68167 100644 --- a/briar-tests/src/org/briarproject/transport/KeyRotationIntegrationTest.java +++ b/briar-tests/src/org/briarproject/transport/KeyRotationIntegrationTest.java @@ -32,14 +32,15 @@ import org.junit.Test; public class KeyRotationIntegrationTest extends BriarTestCase { private static final long EPOCH = 1000L * 1000L * 1000L * 1000L; - private static final long MAX_LATENCY = 2 * 60 * 1000; // 2 minutes - private static final long ROTATION_PERIOD_LENGTH = - MAX_LATENCY + MAX_CLOCK_DIFFERENCE; + private static final int MAX_LATENCY = 2 * 60 * 1000; // 2 minutes + private static final int ROTATION_PERIOD = + MAX_CLOCK_DIFFERENCE + MAX_LATENCY; private final ContactId contactId; private final TransportId transportId; private final byte[] secret0, secret1, secret2, secret3, secret4; private final byte[] key0, key1, key2, key3, key4; + private final SecretKey k0, k1, k2, k3, k4; private final byte[] initialSecret; public KeyRotationIntegrationTest() { @@ -60,6 +61,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase { key2 = new byte[32]; key3 = new byte[32]; key4 = new byte[32]; + k0 = new SecretKey(key0); + k1 = new SecretKey(key1); + k2 = new SecretKey(key2); + k3 = new SecretKey(key3); + k4 = new SecretKey(key4); for(int i = 0; i < key0.length; i++) key0[i] = 1; for(int i = 0; i < key1.length; i++) key1[i] = 2; for(int i = 0; i < key2.length; i++) key2[i] = 3; @@ -112,9 +118,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase { final EventBus eventBus = context.mock(EventBus.class); final Clock clock = context.mock(Clock.class); final Timer timer = context.mock(Timer.class); - final SecretKey k0 = context.mock(SecretKey.class, "k0"); - final SecretKey k1 = context.mock(SecretKey.class, "k1"); - final SecretKey k2 = context.mock(SecretKey.class, "k2"); final TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db); final KeyManagerImpl keyManager = new KeyManagerImpl(crypto, db, @@ -122,9 +125,9 @@ public class KeyRotationIntegrationTest extends BriarTestCase { // The secrets for periods 0 - 2 should be derived Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); context.checking(new Expectations() {{ // start() @@ -142,11 +145,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(clock).currentTimeMillis(); will(returnValue(EPOCH)); oneOf(crypto).deriveNextSecret(initialSecret, 0); - will(returnValue(secret0.clone())); + will(returnValue(secret0)); oneOf(crypto).deriveNextSecret(secret0, 1); - will(returnValue(secret1.clone())); + will(returnValue(secret1)); oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(db).addSecrets(Arrays.asList(s0, s1, s2)); // The recogniser should derive the tags for period 0 oneOf(crypto).deriveTagKey(secret0, false); @@ -155,10 +158,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), with((long) i)); will(new EncodeTagAction()); - oneOf(k0).getEncoded(); - will(returnValue(key0)); } - oneOf(k0).erase(); // The recogniser should derive the tags for period 1 oneOf(crypto).deriveTagKey(secret1, false); will(returnValue(k1)); @@ -166,10 +166,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), with((long) i)); will(new EncodeTagAction()); - oneOf(k1).getEncoded(); - will(returnValue(key1)); } - oneOf(k1).erase(); // The recogniser should derive the tags for period 2 oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); @@ -177,10 +174,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // stop() // The recogniser should derive the tags for period 0 oneOf(crypto).deriveTagKey(secret0, false); @@ -189,10 +183,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), with((long) i)); will(new EncodeTagAction()); - oneOf(k0).getEncoded(); - will(returnValue(key0)); } - oneOf(k0).erase(); // The recogniser should derive the tags for period 1 oneOf(crypto).deriveTagKey(secret1, false); will(returnValue(k1)); @@ -200,10 +191,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), with((long) i)); will(new EncodeTagAction()); - oneOf(k1).getEncoded(); - will(returnValue(key1)); } - oneOf(k1).erase(); // The recogniser should derive the tags for period 2 oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); @@ -211,17 +199,14 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // Remove the listener and stop the timer oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(timer).cancel(); }}); assertTrue(keyManager.start()); - keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); + keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret); keyManager.stop(); context.assertIsSatisfied(); @@ -235,9 +220,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase { final EventBus eventBus = context.mock(EventBus.class); final Clock clock = context.mock(Clock.class); final Timer timer = context.mock(Timer.class); - final SecretKey k0 = context.mock(SecretKey.class, "k0"); - final SecretKey k1 = context.mock(SecretKey.class, "k1"); - final SecretKey k2 = context.mock(SecretKey.class, "k2"); final TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db); final KeyManagerImpl keyManager = new KeyManagerImpl(crypto, db, @@ -245,9 +227,9 @@ public class KeyRotationIntegrationTest extends BriarTestCase { // The secrets for periods 0 - 2 should be derived Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); context.checking(new Expectations() {{ // start() @@ -265,11 +247,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(clock).currentTimeMillis(); will(returnValue(EPOCH)); oneOf(crypto).deriveNextSecret(initialSecret, 0); - will(returnValue(secret0.clone())); + will(returnValue(secret0)); oneOf(crypto).deriveNextSecret(secret0, 1); - will(returnValue(secret1.clone())); + will(returnValue(secret1)); oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(db).addSecrets(Arrays.asList(s0, s1, s2)); // The recogniser should derive the tags for period 0 oneOf(crypto).deriveTagKey(secret0, false); @@ -278,10 +260,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), with((long) i)); will(new EncodeTagAction()); - oneOf(k0).getEncoded(); - will(returnValue(key0)); } - oneOf(k0).erase(); // The recogniser should derive the tags for period 1 oneOf(crypto).deriveTagKey(secret1, false); will(returnValue(k1)); @@ -289,10 +268,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), with((long) i)); will(new EncodeTagAction()); - oneOf(k1).getEncoded(); - will(returnValue(key1)); } - oneOf(k1).erase(); // The recogniser should derive the tags for period 2 oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); @@ -300,10 +276,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // getConnectionContext() oneOf(db).incrementStreamCounter(contactId, transportId, 1); will(returnValue(0L)); @@ -315,10 +288,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), with((long) i)); will(new EncodeTagAction()); - oneOf(k0).getEncoded(); - will(returnValue(key0)); } - oneOf(k0).erase(); // The recogniser should derive the tags for period 1 oneOf(crypto).deriveTagKey(secret1, false); will(returnValue(k1)); @@ -326,10 +296,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), with((long) i)); will(new EncodeTagAction()); - oneOf(k1).getEncoded(); - will(returnValue(key1)); } - oneOf(k1).erase(); // The recogniser should derive the tags for period 2 oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); @@ -337,17 +304,14 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // Remove the listener and stop the timer oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(timer).cancel(); }}); assertTrue(keyManager.start()); - keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); + keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret); StreamContext ctx = keyManager.getStreamContext(contactId, transportId); assertNotNull(ctx); @@ -369,9 +333,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase { final EventBus eventBus = context.mock(EventBus.class); final Clock clock = context.mock(Clock.class); final Timer timer = context.mock(Timer.class); - final SecretKey k0 = context.mock(SecretKey.class, "k0"); - final SecretKey k1 = context.mock(SecretKey.class, "k1"); - final SecretKey k2 = context.mock(SecretKey.class, "k2"); final TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db); final KeyManagerImpl keyManager = new KeyManagerImpl(crypto, db, @@ -379,9 +340,9 @@ public class KeyRotationIntegrationTest extends BriarTestCase { // The secrets for periods 0 - 2 should be derived Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); context.checking(new Expectations() {{ // start() @@ -399,11 +360,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(clock).currentTimeMillis(); will(returnValue(EPOCH)); oneOf(crypto).deriveNextSecret(initialSecret, 0); - will(returnValue(secret0.clone())); + will(returnValue(secret0)); oneOf(crypto).deriveNextSecret(secret0, 1); - will(returnValue(secret1.clone())); + will(returnValue(secret1)); oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(db).addSecrets(Arrays.asList(s0, s1, s2)); // The recogniser should derive the tags for period 0 oneOf(crypto).deriveTagKey(secret0, false); @@ -412,10 +373,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), with((long) i)); will(new EncodeTagAction()); - oneOf(k0).getEncoded(); - will(returnValue(key0)); } - oneOf(k0).erase(); // The recogniser should derive the tags for period 1 oneOf(crypto).deriveTagKey(secret1, false); will(returnValue(k1)); @@ -423,10 +381,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), with((long) i)); will(new EncodeTagAction()); - oneOf(k1).getEncoded(); - will(returnValue(key1)); } - oneOf(k1).erase(); // The recogniser should derive the tags for period 2 oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); @@ -434,21 +389,15 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // acceptConnection() oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); - oneOf(crypto).encodeTag(with(any(byte[].class)), - with(k2), with(16L)); + oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), + with(16L)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); oneOf(db).setReorderingWindow(contactId, transportId, 2, 1, new byte[] {0, 1, 0, 0}); - oneOf(k2).erase(); // stop() // The recogniser should derive the tags for period 0 oneOf(crypto).deriveTagKey(secret0, false); @@ -457,10 +406,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), with((long) i)); will(new EncodeTagAction()); - oneOf(k0).getEncoded(); - will(returnValue(key0)); } - oneOf(k0).erase(); // The recogniser should derive the tags for period 1 oneOf(crypto).deriveTagKey(secret1, false); will(returnValue(k1)); @@ -468,10 +414,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), with((long) i)); will(new EncodeTagAction()); - oneOf(k1).getEncoded(); - will(returnValue(key1)); } - oneOf(k1).erase(); // The recogniser should derive the updated tags for period 2 oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); @@ -479,17 +422,14 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // Remove the listener and stop the timer oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(timer).cancel(); }}); assertTrue(keyManager.start()); - keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); + keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret); // Recognise the tag for connection 0 in period 2 byte[] tag = new byte[TAG_LENGTH]; encodeTag(tag, key2, 0); @@ -513,9 +453,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase { final EventBus eventBus = context.mock(EventBus.class); final Clock clock = context.mock(Clock.class); final Timer timer = context.mock(Timer.class); - final SecretKey k0 = context.mock(SecretKey.class, "k0"); - final SecretKey k1 = context.mock(SecretKey.class, "k1"); - final SecretKey k2 = context.mock(SecretKey.class, "k2"); final TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db); final KeyManagerImpl keyManager = new KeyManagerImpl(crypto, db, @@ -523,9 +460,9 @@ public class KeyRotationIntegrationTest extends BriarTestCase { // The DB contains the secrets for periods 0 - 2 Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); context.checking(new Expectations() {{ // start() @@ -545,10 +482,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), with((long) i)); will(new EncodeTagAction()); - oneOf(k0).getEncoded(); - will(returnValue(key0)); } - oneOf(k0).erase(); // The recogniser should derive the tags for period 1 oneOf(crypto).deriveTagKey(secret1, false); will(returnValue(k1)); @@ -556,10 +490,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), with((long) i)); will(new EncodeTagAction()); - oneOf(k1).getEncoded(); - will(returnValue(key1)); } - oneOf(k1).erase(); // The recogniser should derive the tags for period 2 oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); @@ -567,10 +498,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // Start the timer oneOf(timer).scheduleAtFixedRate(with(keyManager), with(any(long.class)), with(any(long.class))); @@ -582,10 +510,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), with((long) i)); will(new EncodeTagAction()); - oneOf(k0).getEncoded(); - will(returnValue(key0)); } - oneOf(k0).erase(); // The recogniser should derive the tags for period 1 oneOf(crypto).deriveTagKey(secret1, false); will(returnValue(k1)); @@ -593,10 +518,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), with((long) i)); will(new EncodeTagAction()); - oneOf(k1).getEncoded(); - will(returnValue(key1)); } - oneOf(k1).erase(); // The recogniser should derive the tags for period 2 oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); @@ -604,10 +526,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // Remove the listener and stop the timer oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(timer).cancel(); @@ -627,9 +546,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase { final EventBus eventBus = context.mock(EventBus.class); final Clock clock = context.mock(Clock.class); final Timer timer = context.mock(Timer.class); - final SecretKey k1 = context.mock(SecretKey.class, "k1"); - final SecretKey k2 = context.mock(SecretKey.class, "k2"); - final SecretKey k3 = context.mock(SecretKey.class, "k3"); final TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db); final KeyManagerImpl keyManager = new KeyManagerImpl(crypto, db, @@ -637,11 +553,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase { // The DB contains the secrets for periods 0 - 2 Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); // The secret for period 3 should be derived and stored - final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone()); + final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3); context.checking(new Expectations() {{ // start() @@ -653,14 +569,14 @@ public class KeyRotationIntegrationTest extends BriarTestCase { MAX_LATENCY))); // The current time is the start of period 2 oneOf(clock).currentTimeMillis(); - will(returnValue(EPOCH + ROTATION_PERIOD_LENGTH)); + will(returnValue(EPOCH + ROTATION_PERIOD)); // The secret for period 3 should be derived and stored oneOf(crypto).deriveNextSecret(secret0, 1); - will(returnValue(secret1.clone())); + will(returnValue(secret1)); oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(crypto).deriveNextSecret(secret2, 3); - will(returnValue(secret3.clone())); + will(returnValue(secret3)); oneOf(db).addSecrets(Arrays.asList(s3)); // The recogniser should derive the tags for period 1 oneOf(crypto).deriveTagKey(secret1, false); @@ -669,10 +585,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), with((long) i)); will(new EncodeTagAction()); - oneOf(k1).getEncoded(); - will(returnValue(key1)); } - oneOf(k1).erase(); // The recogniser should derive the tags for period 2 oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); @@ -680,10 +593,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // The recogniser should derive the tags for period 3 oneOf(crypto).deriveTagKey(secret3, false); will(returnValue(k3)); @@ -691,10 +601,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3), with((long) i)); will(new EncodeTagAction()); - oneOf(k3).getEncoded(); - will(returnValue(key3)); } - oneOf(k3).erase(); // Start the timer oneOf(timer).scheduleAtFixedRate(with(keyManager), with(any(long.class)), with(any(long.class))); @@ -706,10 +613,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), with((long) i)); will(new EncodeTagAction()); - oneOf(k1).getEncoded(); - will(returnValue(key1)); } - oneOf(k1).erase(); // The recogniser should derive the tags for period 2 oneOf(crypto).deriveTagKey(secret2, false); will(returnValue(k2)); @@ -717,10 +621,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // The recogniser should remove the tags for period 3 oneOf(crypto).deriveTagKey(secret3, false); will(returnValue(k3)); @@ -728,10 +629,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3), with((long) i)); will(new EncodeTagAction()); - oneOf(k3).getEncoded(); - will(returnValue(key3)); } - oneOf(k3).erase(); // Remove the listener and stop the timer oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(timer).cancel(); @@ -751,9 +649,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase { final EventBus eventBus = context.mock(EventBus.class); final Clock clock = context.mock(Clock.class); final Timer timer = context.mock(Timer.class); - final SecretKey k2 = context.mock(SecretKey.class, "k2"); - final SecretKey k3 = context.mock(SecretKey.class, "k3"); - final SecretKey k4 = context.mock(SecretKey.class, "k4"); final TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db); final KeyManagerImpl keyManager = new KeyManagerImpl(crypto, db, @@ -761,12 +656,12 @@ public class KeyRotationIntegrationTest extends BriarTestCase { // The DB contains the secrets for periods 0 - 2 Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); - final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); - final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); - final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); + final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0); + final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1); + final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2); // The secrets for periods 3 and 4 should be derived and stored - final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone()); - final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4.clone()); + final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3); + final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4); context.checking(new Expectations() {{ // start() @@ -778,14 +673,14 @@ public class KeyRotationIntegrationTest extends BriarTestCase { MAX_LATENCY))); // The current time is the end of period 3 oneOf(clock).currentTimeMillis(); - will(returnValue(EPOCH + 3 * ROTATION_PERIOD_LENGTH - 1)); + will(returnValue(EPOCH + 3 * ROTATION_PERIOD - 1)); // The secrets for periods 3 and 4 should be derived from secret 1 oneOf(crypto).deriveNextSecret(secret1, 2); - will(returnValue(secret2.clone())); + will(returnValue(secret2)); oneOf(crypto).deriveNextSecret(secret2, 3); - will(returnValue(secret3.clone())); + will(returnValue(secret3)); oneOf(crypto).deriveNextSecret(secret3, 4); - will(returnValue(secret4.clone())); + will(returnValue(secret4)); // The new secrets should be stored oneOf(db).addSecrets(Arrays.asList(s3, s4)); // The recogniser should derive the tags for period 2 @@ -795,10 +690,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // The recogniser should derive the tags for period 3 oneOf(crypto).deriveTagKey(secret3, false); will(returnValue(k3)); @@ -806,10 +698,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3), with((long) i)); will(new EncodeTagAction()); - oneOf(k3).getEncoded(); - will(returnValue(key3)); } - oneOf(k3).erase(); // The recogniser should derive the tags for period 4 oneOf(crypto).deriveTagKey(secret4, false); will(returnValue(k4)); @@ -817,10 +706,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k4), with((long) i)); will(new EncodeTagAction()); - oneOf(k4).getEncoded(); - will(returnValue(key4)); } - oneOf(k4).erase(); // Start the timer oneOf(timer).scheduleAtFixedRate(with(keyManager), with(any(long.class)), with(any(long.class))); @@ -832,10 +718,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), with((long) i)); will(new EncodeTagAction()); - oneOf(k2).getEncoded(); - will(returnValue(key2)); } - oneOf(k2).erase(); // The recogniser should remove the tags for period 3 oneOf(crypto).deriveTagKey(secret3, false); will(returnValue(k3)); @@ -843,10 +726,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3), with((long) i)); will(new EncodeTagAction()); - oneOf(k3).getEncoded(); - will(returnValue(key3)); } - oneOf(k3).erase(); // The recogniser should derive the tags for period 4 oneOf(crypto).deriveTagKey(secret4, false); will(returnValue(k4)); @@ -854,10 +734,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { oneOf(crypto).encodeTag(with(any(byte[].class)), with(k4), with((long) i)); will(new EncodeTagAction()); - oneOf(k4).getEncoded(); - will(returnValue(key4)); } - oneOf(k4).erase(); // Remove the listener and stop the timer oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(timer).cancel(); @@ -885,7 +762,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase { byte[] tag = (byte[]) invocation.getParameter(0); SecretKey key = (SecretKey) invocation.getParameter(1); long streamNumber = (Long) invocation.getParameter(2); - encodeTag(tag, key.getEncoded(), streamNumber); + encodeTag(tag, key.getBytes(), streamNumber); return null; } } diff --git a/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java b/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java deleted file mode 100644 index d9b7340335a1afb4fe960525e593d5104f44ff6e..0000000000000000000000000000000000000000 --- a/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.briarproject.transport; - -import static org.briarproject.api.transport.TransportConstants.AAD_LENGTH; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.IV_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; -import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; - -import java.io.ByteArrayOutputStream; -import java.util.Random; - -import org.briarproject.BriarTestCase; -import org.briarproject.TestLifecycleModule; -import org.briarproject.TestSystemModule; -import org.briarproject.api.crypto.AuthenticatedCipher; -import org.briarproject.api.crypto.CryptoComponent; -import org.briarproject.api.crypto.SecretKey; -import org.briarproject.crypto.CryptoModule; -import org.junit.Test; - -import com.google.inject.Guice; -import com.google.inject.Injector; - -public class OutgoingEncryptionLayerTest extends BriarTestCase { - - // FIXME: This is an integration test, not a unit test - - private static final int FRAME_LENGTH = 1024; - - private final CryptoComponent crypto; - private final AuthenticatedCipher frameCipher; - - public OutgoingEncryptionLayerTest() { - Injector i = Guice.createInjector(new CryptoModule(), - new TestLifecycleModule(), new TestSystemModule()); - crypto = i.getInstance(CryptoComponent.class); - frameCipher = crypto.getFrameCipher(); - } - - @Test - public void testEncryptionWithoutTag() throws Exception { - int payloadLength = 123; - byte[] iv = new byte[IV_LENGTH], aad = new byte[AAD_LENGTH]; - byte[] plaintext = new byte[FRAME_LENGTH - MAC_LENGTH]; - byte[] ciphertext = new byte[FRAME_LENGTH]; - SecretKey frameKey = crypto.generateSecretKey(); - // Calculate the expected ciphertext - FrameEncoder.encodeIv(iv, 0); - FrameEncoder.encodeAad(aad, 0, plaintext.length); - frameCipher.init(true, frameKey, iv, aad); - FrameEncoder.encodeHeader(plaintext, false, payloadLength); - frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0); - // Check that the actual ciphertext matches what's expected - ByteArrayOutputStream out = new ByteArrayOutputStream(); - OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - frameCipher, frameKey, FRAME_LENGTH, null); - o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], payloadLength, false); - byte[] actual = out.toByteArray(); - assertEquals(FRAME_LENGTH, actual.length); - for(int i = 0; i < FRAME_LENGTH; i++) - assertEquals(ciphertext[i], actual[i]); - } - - @Test - public void testEncryptionWithTag() throws Exception { - byte[] tag = new byte[TAG_LENGTH]; - new Random().nextBytes(tag); - int payloadLength = 123; - byte[] iv = new byte[IV_LENGTH], aad = new byte[AAD_LENGTH]; - byte[] plaintext = new byte[FRAME_LENGTH - MAC_LENGTH]; - byte[] ciphertext = new byte[FRAME_LENGTH]; - SecretKey frameKey = crypto.generateSecretKey(); - // Calculate the expected ciphertext - FrameEncoder.encodeIv(iv, 0); - FrameEncoder.encodeAad(aad, 0, plaintext.length); - frameCipher.init(true, frameKey, iv, aad); - FrameEncoder.encodeHeader(plaintext, false, payloadLength); - frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0); - // Check that the actual tag and ciphertext match what's expected - ByteArrayOutputStream out = new ByteArrayOutputStream(); - OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - frameCipher, frameKey, FRAME_LENGTH, tag); - o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], payloadLength, false); - byte[] actual = out.toByteArray(); - assertEquals(TAG_LENGTH + FRAME_LENGTH, actual.length); - for(int i = 0; i < TAG_LENGTH; i++) assertEquals(tag[i], actual[i]); - for(int i = 0; i < FRAME_LENGTH; i++) - assertEquals(ciphertext[i], actual[TAG_LENGTH + i]); - } - - @Test - public void testCloseConnectionWithoutWriting() throws Exception { - byte[] tag = new byte[TAG_LENGTH]; - new Random().nextBytes(tag); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - // Initiator's constructor - OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - frameCipher, crypto.generateSecretKey(), FRAME_LENGTH, tag); - // Write an empty final frame without having written any other frames - o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true); - // The tag and the empty frame should be written to the output stream - assertEquals(TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH, out.size()); - } -} diff --git a/briar-tests/src/org/briarproject/transport/StreamReaderImplTest.java b/briar-tests/src/org/briarproject/transport/StreamReaderImplTest.java index 67337a50ca4fca64e76f3ffa6b82fd54450e84ce..7e9e97bb2f92c773e0e2c7b36e56e058d32396cc 100644 --- a/briar-tests/src/org/briarproject/transport/StreamReaderImplTest.java +++ b/briar-tests/src/org/briarproject/transport/StreamReaderImplTest.java @@ -1,34 +1,30 @@ package org.briarproject.transport; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; import org.briarproject.BriarTestCase; +import org.briarproject.api.crypto.StreamDecrypter; import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.Test; public class StreamReaderImplTest extends BriarTestCase { - private static final int FRAME_LENGTH = 1024; - private static final int MAX_PAYLOAD_LENGTH = - FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; - @Test public void testEmptyFramesAreSkipped() throws Exception { Mockery context = new Mockery(); - final FrameReader reader = context.mock(FrameReader.class); + final StreamDecrypter decrypter = context.mock(StreamDecrypter.class); context.checking(new Expectations() {{ - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(0)); // Empty frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(2)); // Non-empty frame with two payload bytes - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(0)); // Empty frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(-1)); // No more frames }}); - StreamReaderImpl r = new StreamReaderImpl(reader, FRAME_LENGTH); + StreamReaderImpl r = new StreamReaderImpl(decrypter); assertEquals(0, r.read()); // Skip the first empty frame, read a byte assertEquals(0, r.read()); // Read another byte assertEquals(-1, r.read()); // Skip the second empty frame, reach EOF @@ -40,18 +36,18 @@ public class StreamReaderImplTest extends BriarTestCase { @Test public void testEmptyFramesAreSkippedWithBuffer() throws Exception { Mockery context = new Mockery(); - final FrameReader reader = context.mock(FrameReader.class); + final StreamDecrypter decrypter = context.mock(StreamDecrypter.class); context.checking(new Expectations() {{ - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(0)); // Empty frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(2)); // Non-empty frame with two payload bytes - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(0)); // Empty frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(-1)); // No more frames }}); - StreamReaderImpl r = new StreamReaderImpl(reader, FRAME_LENGTH); + StreamReaderImpl r = new StreamReaderImpl(decrypter); byte[] buf = new byte[MAX_PAYLOAD_LENGTH]; // Skip the first empty frame, read the two payload bytes assertEquals(2, r.read(buf)); @@ -66,14 +62,14 @@ public class StreamReaderImplTest extends BriarTestCase { @Test public void testMultipleReadsPerFrame() throws Exception { Mockery context = new Mockery(); - final FrameReader reader = context.mock(FrameReader.class); + final StreamDecrypter decrypter = context.mock(StreamDecrypter.class); context.checking(new Expectations() {{ - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(MAX_PAYLOAD_LENGTH)); // Nice long frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(-1)); // No more frames }}); - StreamReaderImpl r = new StreamReaderImpl(reader, FRAME_LENGTH); + StreamReaderImpl r = new StreamReaderImpl(decrypter); byte[] buf = new byte[MAX_PAYLOAD_LENGTH / 2]; // Read the first half of the payload assertEquals(MAX_PAYLOAD_LENGTH / 2, r.read(buf)); @@ -88,14 +84,14 @@ public class StreamReaderImplTest extends BriarTestCase { @Test public void testMultipleReadsPerFrameWithOffsets() throws Exception { Mockery context = new Mockery(); - final FrameReader reader = context.mock(FrameReader.class); + final StreamDecrypter decrypter = context.mock(StreamDecrypter.class); context.checking(new Expectations() {{ - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(MAX_PAYLOAD_LENGTH)); // Nice long frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(-1)); // No more frames }}); - StreamReaderImpl r = new StreamReaderImpl(reader, FRAME_LENGTH); + StreamReaderImpl r = new StreamReaderImpl(decrypter); byte[] buf = new byte[MAX_PAYLOAD_LENGTH]; // Read the first half of the payload assertEquals(MAX_PAYLOAD_LENGTH / 2, r.read(buf, MAX_PAYLOAD_LENGTH / 2, diff --git a/briar-tests/src/org/briarproject/transport/StreamWriterImplTest.java b/briar-tests/src/org/briarproject/transport/StreamWriterImplTest.java index e55e9092d031b6ad13e1b8f8dde0e98bba7218d9..37c979f2d1ed62f9da92212e9b43ce06f4976102 100644 --- a/briar-tests/src/org/briarproject/transport/StreamWriterImplTest.java +++ b/briar-tests/src/org/briarproject/transport/StreamWriterImplTest.java @@ -1,43 +1,43 @@ package org.briarproject.transport; -import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; import org.briarproject.BriarTestCase; +import org.briarproject.api.crypto.StreamEncrypter; import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.Test; public class StreamWriterImplTest extends BriarTestCase { - private static final int FRAME_LENGTH = 1024; - private static final int MAX_PAYLOAD_LENGTH = - FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; - @Test public void testCloseWithoutWritingWritesFinalFrame() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); context.checking(new Expectations() {{ // Write an empty final frame - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), - with(true)); + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), + with(0), with(true)); // Flush the stream - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + StreamWriterImpl w = new StreamWriterImpl(encrypter); w.close(); context.assertIsSatisfied(); } @Test - public void testFlushWithoutBufferedDataOnlyFlushes() throws Exception { + public void testFlushWithoutBufferedDataWritesFrameAndFlushes() + throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); + StreamWriterImpl w = new StreamWriterImpl(encrypter); context.checking(new Expectations() {{ + // Write a non-final frame with an empty payload + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), + with(0), with(false)); // Flush the stream - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); w.flush(); context.assertIsSatisfied(); @@ -45,9 +45,9 @@ public class StreamWriterImplTest extends BriarTestCase { // Clean up context.checking(new Expectations() {{ // Closing the writer writes a final frame and flushes again - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), - with(true)); - oneOf(writer).flush(); + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), + with(0), with(true)); + oneOf(encrypter).flush(); }}); w.close(); context.assertIsSatisfied(); @@ -57,14 +57,14 @@ public class StreamWriterImplTest extends BriarTestCase { public void testFlushWithBufferedDataWritesFrameAndFlushes() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); + StreamWriterImpl w = new StreamWriterImpl(encrypter); context.checking(new Expectations() {{ // Write a non-final frame with one payload byte - oneOf(writer).writeFrame(with(any(byte[].class)), with(1), - with(false)); + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(1), + with(0), with(false)); // Flush the stream - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); w.write(0); w.flush(); @@ -73,9 +73,9 @@ public class StreamWriterImplTest extends BriarTestCase { // Clean up context.checking(new Expectations() {{ // Closing the writer writes a final frame and flushes again - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), - with(true)); - oneOf(writer).flush(); + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), + with(0), with(true)); + oneOf(encrypter).flush(); }}); w.close(); context.assertIsSatisfied(); @@ -84,24 +84,22 @@ public class StreamWriterImplTest extends BriarTestCase { @Test public void testSingleByteWritesWriteFullFrame() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); + StreamWriterImpl w = new StreamWriterImpl(encrypter); context.checking(new Expectations() {{ // Write a full non-final frame - oneOf(writer).writeFrame(with(any(byte[].class)), - with(MAX_PAYLOAD_LENGTH), with(false)); + oneOf(encrypter).writeFrame(with(any(byte[].class)), + with(MAX_PAYLOAD_LENGTH), with(0), with(false)); }}); - for(int i = 0; i < MAX_PAYLOAD_LENGTH; i++) { - w.write(0); - } + for(int i = 0; i < MAX_PAYLOAD_LENGTH; i++) w.write(0); context.assertIsSatisfied(); // Clean up context.checking(new Expectations() {{ // Closing the writer writes a final frame and flushes again - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), - with(true)); - oneOf(writer).flush(); + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), + with(0), with(true)); + oneOf(encrypter).flush(); }}); w.close(); context.assertIsSatisfied(); @@ -110,12 +108,12 @@ public class StreamWriterImplTest extends BriarTestCase { @Test public void testMultiByteWritesWriteFullFrames() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); + StreamWriterImpl w = new StreamWriterImpl(encrypter); context.checking(new Expectations() {{ // Write two full non-final frames - exactly(2).of(writer).writeFrame(with(any(byte[].class)), - with(MAX_PAYLOAD_LENGTH), with(false)); + exactly(2).of(encrypter).writeFrame(with(any(byte[].class)), + with(MAX_PAYLOAD_LENGTH), with(0), with(false)); }}); // Sanity check assertEquals(0, MAX_PAYLOAD_LENGTH % 2); @@ -130,9 +128,9 @@ public class StreamWriterImplTest extends BriarTestCase { // Clean up context.checking(new Expectations() {{ // Closing the writer writes a final frame and flushes again - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), - with(true)); - oneOf(writer).flush(); + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), + with(0), with(true)); + oneOf(encrypter).flush(); }}); w.close(); context.assertIsSatisfied(); @@ -141,17 +139,17 @@ public class StreamWriterImplTest extends BriarTestCase { @Test public void testLargeMultiByteWriteWritesFullFrames() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); + StreamWriterImpl w = new StreamWriterImpl(encrypter); context.checking(new Expectations() {{ // Write two full non-final frames - exactly(2).of(writer).writeFrame(with(any(byte[].class)), - with(MAX_PAYLOAD_LENGTH), with(false)); + exactly(2).of(encrypter).writeFrame(with(any(byte[].class)), + with(MAX_PAYLOAD_LENGTH), with(0), with(false)); // Write a final frame with a one-byte payload - oneOf(writer).writeFrame(with(any(byte[].class)), with(1), - with(true)); + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(1), + with(0), with(true)); // Flush the stream - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); // Write two full payloads using one large multi-byte write byte[] b = new byte[MAX_PAYLOAD_LENGTH * 2 + 1]; diff --git a/briar-tests/src/org/briarproject/transport/TestStreamDecrypter.java b/briar-tests/src/org/briarproject/transport/TestStreamDecrypter.java new file mode 100644 index 0000000000000000000000000000000000000000..b901678a6beca92fbad25d2065abda5ac8710fec --- /dev/null +++ b/briar-tests/src/org/briarproject/transport/TestStreamDecrypter.java @@ -0,0 +1,45 @@ +package org.briarproject.transport; + +import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.briarproject.api.FormatException; +import org.briarproject.api.crypto.StreamDecrypter; +import org.briarproject.util.ByteUtils; + +class TestStreamDecrypter implements StreamDecrypter { + + private final InputStream in; + private final byte[] frame; + + TestStreamDecrypter(InputStream in) { + this.in = in; + frame = new byte[MAX_FRAME_LENGTH]; + } + + public int readFrame(byte[] payload) throws IOException { + int offset = 0; + while(offset < HEADER_LENGTH) { + int read = in.read(frame, offset, HEADER_LENGTH - offset); + if(read == -1) throw new EOFException(); + offset += read; + } + boolean finalFrame = (frame[0] & 0x80) == 0x80; + int payloadLength = ByteUtils.readUint16(frame, 0) & 0x7FFF; + while(offset < frame.length) { + int read = in.read(frame, offset, frame.length - offset); + if(read == -1) break; + offset += read; + } + if(!finalFrame && offset < frame.length) throw new EOFException(); + if(offset < HEADER_LENGTH + payloadLength + MAC_LENGTH) + throw new FormatException(); + System.arraycopy(frame, HEADER_LENGTH, payload, 0, payloadLength); + return payloadLength; + } +} diff --git a/briar-tests/src/org/briarproject/transport/TestStreamEncrypter.java b/briar-tests/src/org/briarproject/transport/TestStreamEncrypter.java new file mode 100644 index 0000000000000000000000000000000000000000..d78b4aa3c7570c4ce4643bc7933cac84eb0ce817 --- /dev/null +++ b/briar-tests/src/org/briarproject/transport/TestStreamEncrypter.java @@ -0,0 +1,45 @@ +package org.briarproject.transport; + +import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; + +import java.io.IOException; +import java.io.OutputStream; + +import org.briarproject.api.crypto.StreamEncrypter; +import org.briarproject.util.ByteUtils; + +class TestStreamEncrypter implements StreamEncrypter { + + private final OutputStream out; + private final byte[] tag, frame; + + private boolean writeTag = true; + + TestStreamEncrypter(OutputStream out, byte[] tag) { + this.out = out; + this.tag = tag; + frame = new byte[MAX_FRAME_LENGTH]; + } + + public void writeFrame(byte[] payload, int payloadLength, + int paddingLength, boolean finalFrame) throws IOException { + if(writeTag) { + out.write(tag); + writeTag = false; + } + ByteUtils.writeUint16(payloadLength, frame, 0); + if(finalFrame) frame[0] |= 0x80; + System.arraycopy(payload, 0, frame, HEADER_LENGTH, payloadLength); + for(int i = HEADER_LENGTH + payloadLength; i < frame.length; i++) + frame[i] = 0; + if(finalFrame) + out.write(frame, 0, HEADER_LENGTH + payloadLength + MAC_LENGTH); + else out.write(frame, 0, frame.length); + } + + public void flush() throws IOException { + out.flush(); + } +} diff --git a/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java b/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java index 19153db75a48546db0f4f12a6c827568ffd7511c..af006350ae623ecc622a2c582f5b896389727be8 100644 --- a/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java +++ b/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java @@ -1,5 +1,6 @@ package org.briarproject.transport; +import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import static org.junit.Assert.assertArrayEquals; @@ -11,48 +12,16 @@ import java.io.OutputStream; import java.util.Random; import org.briarproject.BriarTestCase; -import org.briarproject.TestLifecycleModule; -import org.briarproject.TestSystemModule; -import org.briarproject.api.crypto.AuthenticatedCipher; -import org.briarproject.api.crypto.CryptoComponent; -import org.briarproject.api.crypto.SecretKey; -import org.briarproject.api.transport.StreamWriterFactory; -import org.briarproject.crypto.CryptoModule; +import org.briarproject.api.crypto.StreamDecrypter; +import org.briarproject.api.crypto.StreamEncrypter; import org.junit.Test; -import com.google.inject.AbstractModule; -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.Module; - public class TransportIntegrationTest extends BriarTestCase { - private final int FRAME_LENGTH = 2048; - - private final CryptoComponent crypto; - private final AuthenticatedCipher frameCipher; private final Random random; - private final byte[] secret; - private final SecretKey tagKey, frameKey; public TransportIntegrationTest() { - Module testModule = new AbstractModule() { - @Override - public void configure() { - bind(StreamWriterFactory.class).to( - StreamWriterFactoryImpl.class); - } - }; - Injector i = Guice.createInjector(testModule, new CryptoModule(), - new TestLifecycleModule(), new TestSystemModule()); - crypto = i.getInstance(CryptoComponent.class); - frameCipher = crypto.getFrameCipher(); random = new Random(); - // Since we're sending frames to ourselves, we only need outgoing keys - secret = new byte[32]; - random.nextBytes(secret); - tagKey = crypto.deriveTagKey(secret, true); - frameKey = crypto.deriveFrameKey(secret, 0, true); } @Test @@ -66,46 +35,38 @@ public class TransportIntegrationTest extends BriarTestCase { } private void testWriteAndRead(boolean initiator) throws Exception { - // Encode the tag + // Generate a random tag byte[] tag = new byte[TAG_LENGTH]; - crypto.encodeTag(tag, tagKey, 0); - // Generate two random frames - byte[] frame = new byte[1234]; - random.nextBytes(frame); - byte[] frame1 = new byte[321]; - random.nextBytes(frame1); - // Copy the frame key - the copy will be erased - SecretKey frameCopy = frameKey.copy(); + random.nextBytes(tag); + // Generate two frames with random payloads + byte[] payload1 = new byte[123]; + random.nextBytes(payload1); + byte[] payload2 = new byte[321]; + random.nextBytes(payload2); // Write the tag and the frames ByteArrayOutputStream out = new ByteArrayOutputStream(); - FrameWriter frameWriter = new OutgoingEncryptionLayer(out, - frameCipher, frameCopy, FRAME_LENGTH, tag); - StreamWriterImpl streamWriter = new StreamWriterImpl(frameWriter, - FRAME_LENGTH); - OutputStream out1 = streamWriter.getOutputStream(); - out1.write(frame); - out1.flush(); - out1.write(frame1); - out1.flush(); + StreamEncrypter encrypter = new TestStreamEncrypter(out, tag); + OutputStream streamWriter = new StreamWriterImpl(encrypter); + streamWriter.write(payload1); + streamWriter.flush(); + streamWriter.write(payload2); + streamWriter.flush(); byte[] output = out.toByteArray(); - assertEquals(TAG_LENGTH + FRAME_LENGTH * 2, output.length); + assertEquals(TAG_LENGTH + MAX_FRAME_LENGTH * 2, output.length); // Read the tag back ByteArrayInputStream in = new ByteArrayInputStream(output); byte[] recoveredTag = new byte[tag.length]; read(in, recoveredTag); assertArrayEquals(tag, recoveredTag); // Read the frames back - FrameReader frameReader = new IncomingEncryptionLayer(in, frameCipher, - frameKey, FRAME_LENGTH); - StreamReaderImpl streamReader = new StreamReaderImpl(frameReader, - FRAME_LENGTH); - InputStream in1 = streamReader.getInputStream(); - byte[] recoveredFrame = new byte[frame.length]; - read(in1, recoveredFrame); - assertArrayEquals(frame, recoveredFrame); - byte[] recoveredFrame1 = new byte[frame1.length]; - read(in1, recoveredFrame1); - assertArrayEquals(frame1, recoveredFrame1); + StreamDecrypter decrypter = new TestStreamDecrypter(in); + InputStream streamReader = new StreamReaderImpl(decrypter); + byte[] recoveredPayload1 = new byte[payload1.length]; + read(streamReader, recoveredPayload1); + assertArrayEquals(payload1, recoveredPayload1); + byte[] recoveredPayload2 = new byte[payload2.length]; + read(streamReader, recoveredPayload2); + assertArrayEquals(payload2, recoveredPayload2); streamWriter.close(); streamReader.close(); } diff --git a/briar-tests/src/org/briarproject/transport/TransportTagRecogniserTest.java b/briar-tests/src/org/briarproject/transport/TransportTagRecogniserTest.java index ba1e84d48a13f583801e135f55255faf8df808a1..340745489de7a8c5a4d2babb0b52da75cc933bfe 100644 --- a/briar-tests/src/org/briarproject/transport/TransportTagRecogniserTest.java +++ b/briar-tests/src/org/briarproject/transport/TransportTagRecogniserTest.java @@ -25,6 +25,7 @@ public class TransportTagRecogniserTest extends BriarTestCase { private final ContactId contactId = new ContactId(234); private final TransportId transportId = new TransportId("id"); + private final SecretKey tagKey = new SecretKey(new byte[32]); @Test public void testAddAndRemoveSecret() { @@ -33,7 +34,6 @@ public class TransportTagRecogniserTest extends BriarTestCase { final byte[] secret = new byte[32]; new Random().nextBytes(secret); final boolean alice = false; - final SecretKey tagKey = context.mock(SecretKey.class); final DatabaseComponent db = context.mock(DatabaseComponent.class); context.checking(new Expectations() {{ // Add secret @@ -44,7 +44,6 @@ public class TransportTagRecogniserTest extends BriarTestCase { with((long) i)); will(new EncodeTagAction()); } - oneOf(tagKey).erase(); // Remove secret oneOf(crypto).deriveTagKey(secret, !alice); will(returnValue(tagKey)); @@ -53,7 +52,6 @@ public class TransportTagRecogniserTest extends BriarTestCase { with((long) i)); will(new EncodeTagAction()); } - oneOf(tagKey).erase(); }}); TemporarySecret s = new TemporarySecret(contactId, transportId, 123, alice, 0, secret, 0, 0, new byte[4]); @@ -71,7 +69,6 @@ public class TransportTagRecogniserTest extends BriarTestCase { final byte[] secret = new byte[32]; new Random().nextBytes(secret); final boolean alice = false; - final SecretKey tagKey = context.mock(SecretKey.class); final DatabaseComponent db = context.mock(DatabaseComponent.class); context.checking(new Expectations() {{ // Add secret @@ -82,7 +79,6 @@ public class TransportTagRecogniserTest extends BriarTestCase { with((long) i)); will(new EncodeTagAction()); } - oneOf(tagKey).erase(); // Recognise tag 0 oneOf(crypto).deriveTagKey(secret, !alice); will(returnValue(tagKey)); @@ -93,7 +89,6 @@ public class TransportTagRecogniserTest extends BriarTestCase { // The updated window should be stored oneOf(db).setReorderingWindow(contactId, transportId, 0, 1, new byte[] {0, 1, 0, 0}); - oneOf(tagKey).erase(); // Recognise tag again - no expectations }}); TemporarySecret s = new TemporarySecret(contactId, transportId, 123,