Don't try to erase secrets from memory.

1. The things we're really trying to protect - contact identities,
message contents, etc - can't be erased from memory because they're
encapsulated inside objects we don't control.

2. Long-term secrets can't be protected by erasing them from memory
because they're stored in the database and the database key has to be
held in memory whenever the app's running.

3. If the runtime uses a compacting garbage collector then we have no
way to ensure an object is erased from memory.

4. Trying to erase secrets from memory makes the code more complex.

Conclusion: Let's not try to protect secrets from an attacker who can
read arbitrary memory locations.
This commit is contained in:
akwizgran
2014-12-29 21:08:27 +00:00
parent f316d64afa
commit 358166bc12
28 changed files with 211 additions and 557 deletions

View File

@@ -133,10 +133,7 @@ public class PasswordActivity extends RoboActivity {
continueButton.setVisibility(GONE); continueButton.setVisibility(GONE);
progress.setVisibility(VISIBLE); progress.setVisibility(VISIBLE);
// Decrypt the database key in a background thread // Decrypt the database key in a background thread
int length = e.length(); final String password = e.toString();
final char[] password = new char[length];
e.getChars(0, length, password, 0);
e.delete(0, length);
cryptoExecutor.execute(new Runnable() { cryptoExecutor.execute(new Runnable() {
public void run() { public void run() {
byte[] key = crypto.decryptWithPassword(encrypted, password); byte[] key = crypto.decryptWithPassword(encrypted, password);

View File

@@ -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.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.api.crypto.PasswordStrengthEstimator.WEAK; import static org.briarproject.api.crypto.PasswordStrengthEstimator.WEAK;
import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -43,7 +42,6 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
@@ -187,18 +185,16 @@ OnEditorActionListener {
else strengthMeter.setVisibility(INVISIBLE); else strengthMeter.setVisibility(INVISIBLE);
String nickname = nicknameEntry.getText().toString(); String nickname = nicknameEntry.getText().toString();
int nicknameLength = StringUtils.toUtf8(nickname).length; int nicknameLength = StringUtils.toUtf8(nickname).length;
char[] firstPassword = getChars(passwordEntry.getText()); String firstPassword = passwordEntry.getText().toString();
char[] secondPassword = getChars(passwordConfirmation.getText()); String secondPassword = passwordConfirmation.getText().toString();
boolean passwordsMatch = Arrays.equals(firstPassword, secondPassword); boolean passwordsMatch = firstPassword.equals(secondPassword);
float strength = strengthEstimator.estimateStrength(firstPassword); 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); strengthMeter.setStrength(strength);
if(nicknameLength > MAX_AUTHOR_NAME_LENGTH) { if(nicknameLength > MAX_AUTHOR_NAME_LENGTH) {
feedback.setText(R.string.name_too_long); feedback.setText(R.string.name_too_long);
} else if(firstPassword.length == 0) { } else if(firstPassword.length() == 0) {
feedback.setText(""); feedback.setText("");
} else if(secondPassword.length == 0 || passwordsMatch) { } else if(secondPassword.length() == 0 || passwordsMatch) {
if(strength < PasswordStrengthEstimator.WEAK) if(strength < PasswordStrengthEstimator.WEAK)
feedback.setText(R.string.password_too_weak); feedback.setText(R.string.password_too_weak);
else feedback.setText(""); else feedback.setText("");
@@ -212,13 +208,6 @@ OnEditorActionListener {
&& passwordsMatch && strength >= WEAK); && 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) { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Hide the soft keyboard // Hide the soft keyboard
Object o = getSystemService(INPUT_METHOD_SERVICE); Object o = getSystemService(INPUT_METHOD_SERVICE);
@@ -231,18 +220,14 @@ OnEditorActionListener {
feedback.setVisibility(GONE); feedback.setVisibility(GONE);
continueButton.setVisibility(GONE); continueButton.setVisibility(GONE);
progress.setVisibility(VISIBLE); progress.setVisibility(VISIBLE);
// Copy the passwords and erase the originals
final String nickname = nicknameEntry.getText().toString(); final String nickname = nicknameEntry.getText().toString();
final char[] password = getChars(passwordEntry.getText()); final String password = passwordEntry.getText().toString();
delete(passwordEntry.getText());
delete(passwordConfirmation.getText());
// Store the DB key and create the identity in a background thread // Store the DB key and create the identity in a background thread
cryptoExecutor.execute(new Runnable() { cryptoExecutor.execute(new Runnable() {
public void run() { public void run() {
byte[] key = crypto.generateSecretKey().getEncoded(); byte[] key = crypto.generateSecretKey().getBytes();
databaseConfig.setEncryptionKey(key); databaseConfig.setEncryptionKey(key);
byte[] encrypted = encryptDatabaseKey(key, password); byte[] encrypted = encryptDatabaseKey(key, password);
for(int i = 0; i < password.length; i++) password[i] = 0;
storeEncryptedDatabaseKey(encrypted); storeEncryptedDatabaseKey(encrypted);
LocalAuthor localAuthor = createLocalAuthor(nickname); LocalAuthor localAuthor = createLocalAuthor(nickname);
showDashboard(referenceManager.putReference(localAuthor, 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) { private void storeEncryptedDatabaseKey(final byte[] encrypted) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
SharedPreferences prefs = getSharedPreferences("db", MODE_PRIVATE); SharedPreferences prefs = getSharedPreferences("db", MODE_PRIVATE);
@@ -266,7 +247,7 @@ OnEditorActionListener {
LOG.info("Key storage took " + duration + " ms"); 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(); long now = System.currentTimeMillis();
byte[] encrypted = crypto.encryptWithPassword(key, password); byte[] encrypted = crypto.encryptWithPassword(key, password);
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;

View File

@@ -89,7 +89,7 @@ public interface CryptoComponent {
* given password. The ciphertext will be decryptable using the same * given password. The ciphertext will be decryptable using the same
* password after the app restarts. * 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 * 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 * given password. Returns null if the ciphertext cannot be decrypted and
* authenticated (for example, if the password is wrong). * authenticated (for example, if the password is wrong).
*/ */
byte[] decryptWithPassword(byte[] ciphertext, char[] password); byte[] decryptWithPassword(byte[] ciphertext, String password);
} }

View File

@@ -16,9 +16,6 @@ public interface KeyManager extends Service {
*/ */
StreamContext getStreamContext(ContactId c, TransportId t); StreamContext getStreamContext(ContactId c, TransportId t);
/** /** Called whenever an endpoint has been added. */
* Called whenever an endpoint has been added. The initial secret is erased
* before returning.
*/
void endpointAdded(Endpoint ep, int maxLatency, byte[] initialSecret); void endpointAdded(Endpoint ep, int maxLatency, byte[] initialSecret);
} }

View File

@@ -12,5 +12,5 @@ public interface PasswordStrengthEstimator {
* Returns an estimate between 0 (weakest) and 1 (strongest), inclusive, * Returns an estimate between 0 (weakest) and 1 (strongest), inclusive,
* of the strength of the given password. * of the strength of the given password.
*/ */
float estimateStrength(char[] password); float estimateStrength(String password);
} }

View File

@@ -1,21 +1,15 @@
package org.briarproject.api.crypto; package org.briarproject.api.crypto;
/** A secret key used for encryption and/or authentication. */ /** A secret key used for encryption and/or authentication. */
public interface SecretKey { public class SecretKey {
/** Returns the encoded representation of this key. */ private final byte[] key;
byte[] getEncoded();
/** public SecretKey(byte[] key) {
* Returns a copy of this key - erasing this key will erase the copy and this.key = key;
* vice versa. }
*/
SecretKey copy();
/** public byte[] getBytes() {
* Erases this key from memory. Any copies derived from this key via the return key;
* {@link #copy()} method, and any keys from which this key was derived via }
* the {@link #copy()} method, are also erased.
*/
void erase();
} }

View File

@@ -38,7 +38,7 @@ class AuthenticatedCipherImpl implements AuthenticatedCipher {
public void init(boolean encrypt, SecretKey key, byte[] iv, byte[] aad) public void init(boolean encrypt, SecretKey key, byte[] iv, byte[] aad)
throws GeneralSecurityException { throws GeneralSecurityException {
KeyParameter k = new KeyParameter(key.getEncoded()); KeyParameter k = new KeyParameter(key.getBytes());
AEADParameters params = new AEADParameters(k, macLength * 8, iv, aad); AEADParameters params = new AEADParameters(k, macLength * 8, iv, aad);
try { try {
cipher.init(encrypt, params); cipher.init(encrypt, params);

View File

@@ -7,8 +7,6 @@ import static org.briarproject.crypto.EllipticCurveConstants.P;
import static org.briarproject.crypto.EllipticCurveConstants.PARAMETERS; import static org.briarproject.crypto.EllipticCurveConstants.PARAMETERS;
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED; 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.GeneralSecurityException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
@@ -31,6 +29,7 @@ import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.crypto.Signature; import org.briarproject.api.crypto.Signature;
import org.briarproject.api.system.SeedProvider; import org.briarproject.api.system.SeedProvider;
import org.briarproject.util.ByteUtils; import org.briarproject.util.ByteUtils;
import org.briarproject.util.StringUtils;
import org.spongycastle.crypto.AsymmetricCipherKeyPair; import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.BlockCipher; import org.spongycastle.crypto.BlockCipher;
import org.spongycastle.crypto.CipherParameters; import org.spongycastle.crypto.CipherParameters;
@@ -48,7 +47,6 @@ import org.spongycastle.crypto.params.ECKeyGenerationParameters;
import org.spongycastle.crypto.params.ECPrivateKeyParameters; import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.util.Strings;
class CryptoComponentImpl implements CryptoComponent { class CryptoComponentImpl implements CryptoComponent {
@@ -114,7 +112,7 @@ class CryptoComponentImpl implements CryptoComponent {
public SecretKey generateSecretKey() { public SecretKey generateSecretKey() {
byte[] b = new byte[CIPHER_KEY_BYTES]; byte[] b = new byte[CIPHER_KEY_BYTES];
secureRandom.nextBytes(b); secureRandom.nextBytes(b);
return new SecretKeyImpl(b); return new SecretKey(b);
} }
public MessageDigest getMessageDigest() { public MessageDigest getMessageDigest() {
@@ -188,8 +186,6 @@ class CryptoComponentImpl implements CryptoComponent {
int[] codes = new int[2]; int[] codes = new int[2];
codes[0] = ByteUtils.readUint(alice, CODE_BITS); codes[0] = ByteUtils.readUint(alice, CODE_BITS);
codes[1] = ByteUtils.readUint(bob, CODE_BITS); codes[1] = ByteUtils.readUint(bob, CODE_BITS);
ByteUtils.erase(alice);
ByteUtils.erase(bob);
return codes; return codes;
} }
@@ -223,9 +219,7 @@ class CryptoComponentImpl implements CryptoComponent {
byte[] raw = deriveSharedSecret(ourPriv, theirPub); byte[] raw = deriveSharedSecret(ourPriv, theirPub);
// Derive the cooked secret from the raw secret using the // Derive the cooked secret from the raw secret using the
// concatenation KDF // concatenation KDF
byte[] cooked = concatenationKdf(raw, MASTER, aliceInfo, bobInfo); return concatenationKdf(raw, MASTER, aliceInfo, bobInfo);
ByteUtils.erase(raw);
return cooked;
} }
// Package access for testing // Package access for testing
@@ -296,8 +290,7 @@ class CryptoComponentImpl implements CryptoComponent {
} }
private SecretKey deriveKey(byte[] secret, byte[] label, long context) { private SecretKey deriveKey(byte[] secret, byte[] label, long context) {
byte[] key = counterModeKdf(secret, label, context); return new SecretKey(counterModeKdf(secret, label, context));
return new SecretKeyImpl(key);
} }
public AuthenticatedCipher getFrameCipher() { public AuthenticatedCipher getFrameCipher() {
@@ -313,21 +306,19 @@ class CryptoComponentImpl implements CryptoComponent {
ByteUtils.writeUint32(streamNumber, tag, 0); ByteUtils.writeUint32(streamNumber, tag, 0);
BlockCipher cipher = new AESLightEngine(); BlockCipher cipher = new AESLightEngine();
assert cipher.getBlockSize() == TAG_LENGTH; assert cipher.getBlockSize() == TAG_LENGTH;
KeyParameter k = new KeyParameter(tagKey.getEncoded()); KeyParameter k = new KeyParameter(tagKey.getBytes());
cipher.init(true, k); cipher.init(true, k);
cipher.processBlock(tag, 0, tag, 0); 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 // Generate a random salt
byte[] salt = new byte[PBKDF_SALT_BYTES]; byte[] salt = new byte[PBKDF_SALT_BYTES];
secureRandom.nextBytes(salt); secureRandom.nextBytes(salt);
// Calibrate the KDF // Calibrate the KDF
int iterations = chooseIterationCount(PBKDF_TARGET_MILLIS); int iterations = chooseIterationCount(PBKDF_TARGET_MILLIS);
// Derive the key from the password // Derive the key from the password
byte[] keyBytes = pbkdf2(password, salt, iterations); SecretKey key = new SecretKey(pbkdf2(password, salt, iterations));
SecretKey key = new SecretKeyImpl(keyBytes);
// Generate a random IV // Generate a random IV
byte[] iv = new byte[STORAGE_IV_BYTES]; byte[] iv = new byte[STORAGE_IV_BYTES];
secureRandom.nextBytes(iv); secureRandom.nextBytes(iv);
@@ -348,12 +339,10 @@ class CryptoComponentImpl implements CryptoComponent {
return output; return output;
} catch(GeneralSecurityException e) { } catch(GeneralSecurityException e) {
throw new RuntimeException(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 // The input contains the salt, iterations, IV, ciphertext and MAC
if(input.length < PBKDF_SALT_BYTES + 4 + STORAGE_IV_BYTES + MAC_BYTES) if(input.length < PBKDF_SALT_BYTES + 4 + STORAGE_IV_BYTES + MAC_BYTES)
return null; // Invalid return null; // Invalid
@@ -365,8 +354,7 @@ class CryptoComponentImpl implements CryptoComponent {
byte[] iv = new byte[STORAGE_IV_BYTES]; byte[] iv = new byte[STORAGE_IV_BYTES];
System.arraycopy(input, salt.length + 4, iv, 0, iv.length); System.arraycopy(input, salt.length + 4, iv, 0, iv.length);
// Derive the key from the password // Derive the key from the password
byte[] keyBytes = pbkdf2(password, salt, (int) iterations); SecretKey key = new SecretKey(pbkdf2(password, salt, (int) iterations));
SecretKey key = new SecretKeyImpl(keyBytes);
// Initialise the cipher // Initialise the cipher
AuthenticatedCipher cipher; AuthenticatedCipher cipher;
try { try {
@@ -374,7 +362,6 @@ class CryptoComponentImpl implements CryptoComponent {
cipher = new AuthenticatedCipherImpl(a, MAC_BYTES); cipher = new AuthenticatedCipherImpl(a, MAC_BYTES);
cipher.init(false, key, iv, null); cipher.init(false, key, iv, null);
} catch(GeneralSecurityException e) { } catch(GeneralSecurityException e) {
key.erase();
throw new RuntimeException(e); throw new RuntimeException(e);
} }
// Try to decrypt the ciphertext (may be invalid) // Try to decrypt the ciphertext (may be invalid)
@@ -385,9 +372,7 @@ class CryptoComponentImpl implements CryptoComponent {
cipher.doFinal(input, inputOff, inputLen, output, 0); cipher.doFinal(input, inputOff, inputLen, output, 0);
return output; return output;
} catch(GeneralSecurityException e) { } catch(GeneralSecurityException e) {
return null; // Invalid return null; // Invalid ciphertext
} finally {
key.erase();
} }
} }
@@ -417,7 +402,6 @@ class CryptoComponentImpl implements CryptoComponent {
// The secret is the first CIPHER_KEY_BYTES bytes of the hash // The secret is the first CIPHER_KEY_BYTES bytes of the hash
byte[] output = new byte[CIPHER_KEY_BYTES]; byte[] output = new byte[CIPHER_KEY_BYTES];
System.arraycopy(hash, 0, output, 0, output.length); System.arraycopy(hash, 0, output, 0, output.length);
ByteUtils.erase(hash);
return output; return output;
} }
@@ -447,20 +431,17 @@ class CryptoComponentImpl implements CryptoComponent {
prf.update((byte) CIPHER_KEY_BYTES); // Output length prf.update((byte) CIPHER_KEY_BYTES); // Output length
prf.doFinal(mac, 0); prf.doFinal(mac, 0);
System.arraycopy(mac, 0, output, 0, output.length); System.arraycopy(mac, 0, output, 0, output.length);
ByteUtils.erase(mac);
ByteUtils.erase(k.getKey());
return output; return output;
} }
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2 // Password-based key derivation function - see PKCS#5 v2.1, section 5.2
private byte[] pbkdf2(char[] password, byte[] salt, int iterations) { private byte[] pbkdf2(String password, byte[] salt, int iterations) {
byte[] utf8 = toUtf8ByteArray(password); byte[] utf8 = StringUtils.toUtf8(password);
Digest digest = new SHA384Digest(); Digest digest = new SHA384Digest();
PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest); PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest);
gen.init(utf8, salt, iterations); gen.init(utf8, salt, iterations);
int keyLengthInBits = CIPHER_KEY_BYTES * 8; int keyLengthInBits = CIPHER_KEY_BYTES * 8;
CipherParameters p = gen.generateDerivedParameters(keyLengthInBits); CipherParameters p = gen.generateDerivedParameters(keyLengthInBits);
ByteUtils.erase(utf8);
return ((KeyParameter) p).getKey(); return ((KeyParameter) p).getKey();
} }
@@ -512,18 +493,4 @@ class CryptoComponentImpl implements CryptoComponent {
if(size % 2 == 1) return list.get(size / 2); if(size % 2 == 1) return list.get(size / 2);
return list.get(size / 2 - 1) + list.get(size / 2) / 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);
}
}
} }

View File

@@ -13,9 +13,10 @@ class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator {
private static final double STRONG = Math.log(Math.pow(LOWER + UPPER + private static final double STRONG = Math.log(Math.pow(LOWER + UPPER +
DIGIT + OTHER, 10)); DIGIT + OTHER, 10));
public float estimateStrength(char[] password) { public float estimateStrength(String password) {
HashSet<Character> unique = new HashSet<Character>(); 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; boolean lower = false, upper = false, digit = false, other = false;
for(char c : unique) { for(char c : unique) {
if(Character.isLowerCase(c)) lower = true; if(Character.isLowerCase(c)) lower = true;

View File

@@ -1,30 +0,0 @@
package org.briarproject.crypto;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.util.ByteUtils;
class SecretKeyImpl implements SecretKey {
private final byte[] key;
private boolean erased = false; // Locking: this
SecretKeyImpl(byte[] key) {
this.key = key;
}
public synchronized byte[] getEncoded() {
if(erased) throw new IllegalStateException();
return key;
}
public SecretKey copy() {
return new SecretKeyImpl(key.clone());
}
public synchronized void erase() {
if(erased) throw new IllegalStateException();
ByteUtils.erase(key);
erased = true;
}
}

View File

@@ -44,16 +44,11 @@ class StreamDecrypterImpl implements StreamDecrypter {
if(finalFrame) return -1; if(finalFrame) return -1;
// Read the frame // Read the frame
int ciphertextLength = 0; int ciphertextLength = 0;
try { while(ciphertextLength < frameLength) {
while(ciphertextLength < frameLength) { int read = in.read(ciphertext, ciphertextLength,
int read = in.read(ciphertext, ciphertextLength, frameLength - ciphertextLength);
frameLength - ciphertextLength); if(read == -1) break; // We'll check the length later
if(read == -1) break; // We'll check the length later ciphertextLength += read;
ciphertextLength += read;
}
} catch(IOException e) {
frameKey.erase();
throw e;
} }
int plaintextLength = ciphertextLength - MAC_LENGTH; int plaintextLength = ciphertextLength - MAC_LENGTH;
if(plaintextLength < HEADER_LENGTH) throw new EOFException(); if(plaintextLength < HEADER_LENGTH) throw new EOFException();

View File

@@ -30,7 +30,6 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
SecretKey tagKey = crypto.deriveTagKey(secret, alice); SecretKey tagKey = crypto.deriveTagKey(secret, alice);
crypto.encodeTag(tag, tagKey, streamNumber); crypto.encodeTag(tag, tagKey, streamNumber);
tagKey.erase();
// Derive the frame key // Derive the frame key
SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice);
// Create the encrypter // Create the encrypter

View File

@@ -45,12 +45,7 @@ class StreamEncrypterImpl implements StreamEncrypter {
if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
// Write the tag if required // Write the tag if required
if(writeTag) { if(writeTag) {
try { out.write(tag, 0, tag.length);
out.write(tag, 0, tag.length);
} catch(IOException e) {
frameKey.erase();
throw e;
}
writeTag = false; writeTag = false;
} }
// Don't pad the final frame // Don't pad the final frame
@@ -81,24 +76,14 @@ class StreamEncrypterImpl implements StreamEncrypter {
throw new RuntimeException(badCipher); throw new RuntimeException(badCipher);
} }
// Write the frame // Write the frame
try { out.write(ciphertext, 0, ciphertextLength);
out.write(ciphertext, 0, ciphertextLength);
} catch(IOException e) {
frameKey.erase();
throw e;
}
frameNumber++; frameNumber++;
} }
public void flush() throws IOException { public void flush() throws IOException {
// Write the tag if required // Write the tag if required
if(writeTag) { if(writeTag) {
try { out.write(tag, 0, tag.length);
out.write(tag, 0, tag.length);
} catch(IOException e) {
frameKey.erase();
throw e;
}
writeTag = false; writeTag = false;
} }
out.flush(); out.flush();

View File

@@ -81,34 +81,18 @@ class H2Database extends JdbcDatabase {
} }
} }
@Override
protected Connection createConnection() throws SQLException { protected Connection createConnection() throws SQLException {
byte[] key = config.getEncryptionKey(); byte[] key = config.getEncryptionKey();
if(key == null) throw new IllegalStateException(); if(key == null) throw new IllegalStateException();
char[] password = encodePassword(key);
Properties props = new Properties(); Properties props = new Properties();
props.setProperty("user", "user"); props.setProperty("user", "user");
props.put("password", password); // Separate the file password from the user password with a space
try { props.put("password", StringUtils.toHexString(key) + " password");
return DriverManager.getConnection(url, props); 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;
} }
@Override
protected void flushBuffersToDisk(Statement s) throws SQLException { protected void flushBuffersToDisk(Statement s) throws SQLException {
// FIXME: Remove this after implementing BTPv2? // FIXME: Remove this after implementing BTPv2?
s.execute("CHECKPOINT SYNC"); s.execute("CHECKPOINT SYNC");

View File

@@ -28,7 +28,6 @@ import org.briarproject.api.transport.StreamContext;
import org.briarproject.api.transport.StreamReaderFactory; import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.api.transport.StreamWriterFactory;
import org.briarproject.api.transport.TagRecogniser; import org.briarproject.api.transport.TagRecogniser;
import org.briarproject.util.ByteUtils;
class ConnectionManagerImpl implements ConnectionManager { class ConnectionManagerImpl implements ConnectionManager {
@@ -95,40 +94,28 @@ class ConnectionManagerImpl implements ConnectionManager {
private MessagingSession createIncomingSession(StreamContext ctx, private MessagingSession createIncomingSession(StreamContext ctx,
TransportConnectionReader r) throws IOException { TransportConnectionReader r) throws IOException {
try { InputStream streamReader = streamReaderFactory.createStreamReader(
InputStream streamReader = streamReaderFactory.createStreamReader( r.getInputStream(), r.getMaxFrameLength(), ctx);
r.getInputStream(), r.getMaxFrameLength(), ctx); return messagingSessionFactory.createIncomingSession(
return messagingSessionFactory.createIncomingSession( ctx.getContactId(), ctx.getTransportId(), streamReader);
ctx.getContactId(), ctx.getTransportId(), streamReader);
} finally {
ByteUtils.erase(ctx.getSecret());
}
} }
private MessagingSession createSimplexOutgoingSession(StreamContext ctx, private MessagingSession createSimplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException { TransportConnectionWriter w) throws IOException {
try { OutputStream streamWriter = streamWriterFactory.createStreamWriter(
OutputStream streamWriter = streamWriterFactory.createStreamWriter( w.getOutputStream(), w.getMaxFrameLength(), ctx);
w.getOutputStream(), w.getMaxFrameLength(), ctx); return messagingSessionFactory.createSimplexOutgoingSession(
return messagingSessionFactory.createSimplexOutgoingSession( ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(),
ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(), streamWriter);
streamWriter);
} finally {
ByteUtils.erase(ctx.getSecret());
}
} }
private MessagingSession createDuplexOutgoingSession(StreamContext ctx, private MessagingSession createDuplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException { TransportConnectionWriter w) throws IOException {
try { OutputStream streamWriter = streamWriterFactory.createStreamWriter(
OutputStream streamWriter = streamWriterFactory.createStreamWriter( w.getOutputStream(), w.getMaxFrameLength(), ctx);
w.getOutputStream(), w.getMaxFrameLength(), ctx); return messagingSessionFactory.createDuplexOutgoingSession(
return messagingSessionFactory.createDuplexOutgoingSession( ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(),
ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(), w.getMaxIdleTime(), streamWriter);
w.getMaxIdleTime(), streamWriter);
} finally {
ByteUtils.erase(ctx.getSecret());
}
} }
private class ManageIncomingSimplexConnection implements Runnable { private class ManageIncomingSimplexConnection implements Runnable {

View File

@@ -33,7 +33,6 @@ import org.briarproject.api.transport.Endpoint;
import org.briarproject.api.transport.StreamContext; import org.briarproject.api.transport.StreamContext;
import org.briarproject.api.transport.TagRecogniser; import org.briarproject.api.transport.TagRecogniser;
import org.briarproject.api.transport.TemporarySecret; import org.briarproject.api.transport.TemporarySecret;
import org.briarproject.util.ByteUtils;
// FIXME: Don't make alien calls with a lock held // FIXME: Don't make alien calls with a lock held
class KeyManagerImpl extends TimerTask implements KeyManager, EventListener { class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
@@ -119,7 +118,6 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
Integer maxLatency = maxLatencies.get(s.getTransportId()); Integer maxLatency = maxLatencies.get(s.getTransportId());
if(maxLatency == null) { if(maxLatency == null) {
LOG.info("Discarding obsolete secret"); LOG.info("Discarding obsolete secret");
ByteUtils.erase(s.getSecret());
continue; continue;
} }
long rotation = maxLatency + MAX_CLOCK_DIFFERENCE; long rotation = maxLatency + MAX_CLOCK_DIFFERENCE;
@@ -143,7 +141,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
return dead; return dead;
} }
// Replaces and erases the given secrets and returns any secrets created // Replaces the given secrets and returns any secrets created
// Locking: this // Locking: this
private Collection<TemporarySecret> replaceDeadSecrets(long now, private Collection<TemporarySecret> replaceDeadSecrets(long now,
Collection<TemporarySecret> dead) { Collection<TemporarySecret> dead) {
@@ -157,12 +155,10 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
// There's no other secret for this endpoint // There's no other secret for this endpoint
newest.put(k, s); newest.put(k, s);
} else if(exists.getPeriod() < s.getPeriod()) { } else if(exists.getPeriod() < s.getPeriod()) {
// There's an older secret - erase it and use this one instead // There's an older secret - use this one instead
ByteUtils.erase(exists.getSecret());
newest.put(k, s); newest.put(k, s);
} else { } else {
// There's a newer secret - erase this one // There's a newer secret - keep using it
ByteUtils.erase(s.getSecret());
} }
} }
Collection<TemporarySecret> created = new ArrayList<TemporarySecret>(); Collection<TemporarySecret> created = new ArrayList<TemporarySecret>();
@@ -179,34 +175,23 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
throw new IllegalStateException(); throw new IllegalStateException();
// Derive the old, current and new secrets // Derive the old, current and new secrets
byte[] b1 = s.getSecret(); byte[] b1 = s.getSecret();
for(long p = s.getPeriod() + 1; p < period; p++) { for(long p = s.getPeriod() + 1; p < period; p++)
byte[] temp = crypto.deriveNextSecret(b1, p); b1 = crypto.deriveNextSecret(b1, p);
ByteUtils.erase(b1);
b1 = temp;
}
byte[] b2 = crypto.deriveNextSecret(b1, period); byte[] b2 = crypto.deriveNextSecret(b1, period);
byte[] b3 = crypto.deriveNextSecret(b2, period + 1); byte[] b3 = crypto.deriveNextSecret(b2, period + 1);
// Add the secrets to their respective maps - copies may already // Add the secrets to their respective maps if not already present
// exist, in which case erase the new copies (the old copies are
// referenced by the connection recogniser)
EndpointKey k = e.getKey(); EndpointKey k = e.getKey();
if(oldSecrets.containsKey(k)) { if(!oldSecrets.containsKey(k)) {
ByteUtils.erase(b1);
} else {
TemporarySecret s1 = new TemporarySecret(s, period - 1, b1); TemporarySecret s1 = new TemporarySecret(s, period - 1, b1);
oldSecrets.put(k, s1); oldSecrets.put(k, s1);
created.add(s1); created.add(s1);
} }
if(currentSecrets.containsKey(k)) { if(!currentSecrets.containsKey(k)) {
ByteUtils.erase(b2);
} else {
TemporarySecret s2 = new TemporarySecret(s, period, b2); TemporarySecret s2 = new TemporarySecret(s, period, b2);
currentSecrets.put(k, s2); currentSecrets.put(k, s2);
created.add(s2); created.add(s2);
} }
if(newSecrets.containsKey(k)) { if(!newSecrets.containsKey(k)) {
ByteUtils.erase(b3);
} else {
TemporarySecret s3 = new TemporarySecret(s, period + 1, b3); TemporarySecret s3 = new TemporarySecret(s, period + 1, b3);
newSecrets.put(k, s3); newSecrets.put(k, s3);
created.add(s3); created.add(s3);
@@ -220,18 +205,12 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
timer.cancel(); timer.cancel();
tagRecogniser.removeSecrets(); tagRecogniser.removeSecrets();
maxLatencies.clear(); maxLatencies.clear();
removeAndEraseSecrets(oldSecrets); oldSecrets.clear();
removeAndEraseSecrets(currentSecrets); currentSecrets.clear();
removeAndEraseSecrets(newSecrets); newSecrets.clear();
return true; return true;
} }
// Locking: this
private void removeAndEraseSecrets(Map<?, TemporarySecret> m) {
for(TemporarySecret s : m.values()) ByteUtils.erase(s.getSecret());
m.clear();
}
public synchronized StreamContext getStreamContext(ContactId c, public synchronized StreamContext getStreamContext(ContactId c,
TransportId t) { TransportId t) {
TemporarySecret s = currentSecrets.get(new EndpointKey(c, t)); TemporarySecret s = currentSecrets.get(new EndpointKey(c, t));
@@ -250,8 +229,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null; return null;
} }
// Clone the secret - the original will be erased byte[] secret = s.getSecret();
byte[] secret = s.getSecret().clone();
return new StreamContext(c, t, secret, streamNumber, s.getAlice()); return new StreamContext(c, t, secret, streamNumber, s.getAlice());
} }
@@ -265,11 +243,8 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
if(period < 1) throw new IllegalStateException(); if(period < 1) throw new IllegalStateException();
// Derive the old, current and new secrets // Derive the old, current and new secrets
byte[] b1 = initialSecret; byte[] b1 = initialSecret;
for(long p = 0; p < period; p++) { for(long p = 0; p < period; p++)
byte[] temp = crypto.deriveNextSecret(b1, p); b1 = crypto.deriveNextSecret(b1, p);
ByteUtils.erase(b1);
b1 = temp;
}
byte[] b2 = crypto.deriveNextSecret(b1, period); byte[] b2 = crypto.deriveNextSecret(b1, period);
byte[] b3 = crypto.deriveNextSecret(b2, period + 1); byte[] b3 = crypto.deriveNextSecret(b2, period + 1);
TemporarySecret s1 = new TemporarySecret(ep, period - 1, b1); TemporarySecret s1 = new TemporarySecret(ep, period - 1, b1);
@@ -341,28 +316,17 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
} }
// Locking: this // Locking: this
private void removeAndEraseSecrets(ContactId c, Map<?, TemporarySecret> m) { private void removeSecrets(ContactId c, Map<?, TemporarySecret> m) {
Iterator<TemporarySecret> it = m.values().iterator(); Iterator<TemporarySecret> it = m.values().iterator();
while(it.hasNext()) { while(it.hasNext())
TemporarySecret s = it.next(); if(it.next().getContactId().equals(c)) it.remove();
if(s.getContactId().equals(c)) {
ByteUtils.erase(s.getSecret());
it.remove();
}
}
} }
// Locking: this // Locking: this
private void removeAndEraseSecrets(TransportId t, private void removeSecrets(TransportId t, Map<?, TemporarySecret> m) {
Map<?, TemporarySecret> m) {
Iterator<TemporarySecret> it = m.values().iterator(); Iterator<TemporarySecret> it = m.values().iterator();
while(it.hasNext()) { while(it.hasNext())
TemporarySecret s = it.next(); if(it.next().getTransportId().equals(t)) it.remove();
if(s.getTransportId().equals(t)) {
ByteUtils.erase(s.getSecret());
it.remove();
}
}
} }
private static class EndpointKey { private static class EndpointKey {
@@ -408,9 +372,9 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
ContactId c = event.getContactId(); ContactId c = event.getContactId();
tagRecogniser.removeSecrets(c); tagRecogniser.removeSecrets(c);
synchronized(KeyManagerImpl.this) { synchronized(KeyManagerImpl.this) {
removeAndEraseSecrets(c, oldSecrets); removeSecrets(c, oldSecrets);
removeAndEraseSecrets(c, currentSecrets); removeSecrets(c, currentSecrets);
removeAndEraseSecrets(c, newSecrets); removeSecrets(c, newSecrets);
} }
} }
} }
@@ -445,9 +409,9 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
tagRecogniser.removeSecrets(t); tagRecogniser.removeSecrets(t);
synchronized(KeyManagerImpl.this) { synchronized(KeyManagerImpl.this) {
maxLatencies.remove(t); maxLatencies.remove(t);
removeAndEraseSecrets(t, oldSecrets); removeSecrets(t, oldSecrets);
removeAndEraseSecrets(t, currentSecrets); removeSecrets(t, currentSecrets);
removeAndEraseSecrets(t, newSecrets); removeSecrets(t, newSecrets);
} }
} }
} }

View File

@@ -56,13 +56,10 @@ class TransportTagRecogniser {
assert duplicate == null; assert duplicate == null;
} }
} }
key.erase();
// Store the updated reordering window in the DB // Store the updated reordering window in the DB
db.setReorderingWindow(t.contactId, transportId, t.period, db.setReorderingWindow(t.contactId, transportId, t.period,
t.window.getCentre(), t.window.getBitmap()); t.window.getCentre(), t.window.getBitmap());
// Clone the secret - the key manager will erase the original return new StreamContext(t.contactId, transportId, t.secret,
byte[] secret = t.secret.clone();
return new StreamContext(t.contactId, transportId, secret,
t.streamNumber, t.alice); t.streamNumber, t.alice);
} }
@@ -84,7 +81,6 @@ class TransportTagRecogniser {
TagContext duplicate = tagMap.put(new Bytes(tag), added); TagContext duplicate = tagMap.put(new Bytes(tag), added);
assert duplicate == null; assert duplicate == null;
} }
key.erase();
// Create a removal context to remove the window and the tags later // Create a removal context to remove the window and the tags later
RemovalContext r = new RemovalContext(window, secret, alice); RemovalContext r = new RemovalContext(window, secret, alice);
removalMap.put(new RemovalKey(contactId, period), r); removalMap.put(new RemovalKey(contactId, period), r);
@@ -107,7 +103,6 @@ class TransportTagRecogniser {
TagContext removed = tagMap.remove(new Bytes(tag)); TagContext removed = tagMap.remove(new Bytes(tag));
assert removed != null; assert removed != null;
} }
key.erase();
} }
synchronized void removeSecrets(ContactId c) { synchronized void removeSecrets(ContactId c) {

View File

@@ -48,10 +48,6 @@ public class ByteUtils {
| ((b[offset + 2] & 0xFFL) << 8) | (b[offset + 3] & 0xFFL); | ((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) { public static int readUint(byte[] b, int bits) {
if(b.length << 3 < bits) throw new IllegalArgumentException(); if(b.length << 3 < bits) throw new IllegalArgumentException();
int result = 0; int result = 0;

View File

@@ -100,8 +100,7 @@
<test name='org.briarproject.crypto.KeyDerivationTest'/> <test name='org.briarproject.crypto.KeyDerivationTest'/>
<test name='org.briarproject.crypto.KeyEncodingAndParsingTest'/> <test name='org.briarproject.crypto.KeyEncodingAndParsingTest'/>
<test name="org.briarproject.crypto.PasswordBasedKdfTest"/> <test name="org.briarproject.crypto.PasswordBasedKdfTest"/>
<test name="org.briarproject.crypto.PasswordStrengthEstimatorTest"/> <test name="org.briarproject.crypto.PasswordStrengthEstimatorImplTest"/>
<test name='org.briarproject.crypto.SecretKeyImplTest'/>
<test name='org.briarproject.crypto.StreamDecrypterImplTest'/> <test name='org.briarproject.crypto.StreamDecrypterImplTest'/>
<test name='org.briarproject.crypto.StreamEncrypterImplTest'/> <test name='org.briarproject.crypto.StreamEncrypterImplTest'/>
<test name='org.briarproject.db.BasicH2Test'/> <test name='org.briarproject.db.BasicH2Test'/>

View File

@@ -116,8 +116,8 @@ public class ProtocolIntegrationTest extends BriarTestCase {
private byte[] write() throws Exception { private byte[] write() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamContext ctx = new StreamContext(contactId, transportId, StreamContext ctx = new StreamContext(contactId, transportId, secret,
secret.clone(), 0, true); 0, true);
OutputStream streamWriter = streamWriterFactory.createStreamWriter(out, OutputStream streamWriter = streamWriterFactory.createStreamWriter(out,
MAX_FRAME_LENGTH, ctx); MAX_FRAME_LENGTH, ctx);
PacketWriter packetWriter = packetWriterFactory.createPacketWriter( PacketWriter packetWriter = packetWriterFactory.createPacketWriter(
@@ -148,8 +148,8 @@ public class ProtocolIntegrationTest extends BriarTestCase {
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH)); assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH));
// FIXME: Check that the expected tag was received // FIXME: Check that the expected tag was received
StreamContext ctx = new StreamContext(contactId, transportId, StreamContext ctx = new StreamContext(contactId, transportId, secret,
secret.clone(), 0, false); 0, false);
InputStream streamReader = streamReaderFactory.createStreamReader(in, InputStream streamReader = streamReaderFactory.createStreamReader(in,
MAX_FRAME_LENGTH, ctx); MAX_FRAME_LENGTH, ctx);
PacketReader packetReader = packetReaderFactory.createPacketReader( PacketReader packetReader = packetReaderFactory.createPacketReader(

View File

@@ -30,9 +30,9 @@ public class KeyDerivationTest extends BriarTestCase {
keys.add(crypto.deriveTagKey(secret, true)); keys.add(crypto.deriveTagKey(secret, true));
keys.add(crypto.deriveTagKey(secret, false)); keys.add(crypto.deriveTagKey(secret, false));
for(int i = 0; i < 4; i++) { 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++) { 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)); assertEquals(i == j, Arrays.equals(keyI, keyJ));
} }
} }
@@ -59,9 +59,8 @@ public class KeyDerivationTest extends BriarTestCase {
@Test @Test
public void testStreamNumberAffectsDerivation() { public void testStreamNumberAffectsDerivation() {
List<byte[]> secrets = new ArrayList<byte[]>(); List<byte[]> secrets = new ArrayList<byte[]>();
for(int i = 0; i < 20; i++) { for(int i = 0; i < 20; i++)
secrets.add(crypto.deriveNextSecret(secret.clone(), i)); secrets.add(crypto.deriveNextSecret(secret, i));
}
for(int i = 0; i < 20; i++) { for(int i = 0; i < 20; i++) {
byte[] secretI = secrets.get(i); byte[] secretI = secrets.get(i);
for(int j = 0; j < 20; j++) { for(int j = 0; j < 20; j++) {

View File

@@ -18,7 +18,7 @@ public class PasswordBasedKdfTest extends BriarTestCase {
Random random = new Random(); Random random = new Random();
byte[] input = new byte[1234]; byte[] input = new byte[1234];
random.nextBytes(input); random.nextBytes(input);
char[] password = "password".toCharArray(); String password = "password";
byte[] ciphertext = crypto.encryptWithPassword(input, password); byte[] ciphertext = crypto.encryptWithPassword(input, password);
byte[] output = crypto.decryptWithPassword(ciphertext, password); byte[] output = crypto.decryptWithPassword(ciphertext, password);
assertArrayEquals(input, output); assertArrayEquals(input, output);
@@ -29,7 +29,7 @@ public class PasswordBasedKdfTest extends BriarTestCase {
Random random = new Random(); Random random = new Random();
byte[] input = new byte[1234]; byte[] input = new byte[1234];
random.nextBytes(input); random.nextBytes(input);
char[] password = "password".toCharArray(); String password = "password";
byte[] ciphertext = crypto.encryptWithPassword(input, password); byte[] ciphertext = crypto.encryptWithPassword(input, password);
// Modify the ciphertext // Modify the ciphertext
int position = random.nextInt(ciphertext.length); int position = random.nextInt(ciphertext.length);

View File

@@ -6,24 +6,23 @@ import org.briarproject.BriarTestCase;
import org.briarproject.api.crypto.PasswordStrengthEstimator; import org.briarproject.api.crypto.PasswordStrengthEstimator;
import org.junit.Test; import org.junit.Test;
public class PasswordStrengthEstimatorTest extends BriarTestCase { public class PasswordStrengthEstimatorImplTest extends BriarTestCase {
@Test @Test
public void testWeakPasswords() { public void testWeakPasswords() {
PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl(); PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl();
assertTrue(e.estimateStrength("".toCharArray()) < QUITE_STRONG); assertTrue(e.estimateStrength("") < QUITE_STRONG);
assertTrue(e.estimateStrength("password".toCharArray()) < QUITE_STRONG); assertTrue(e.estimateStrength("password") < QUITE_STRONG);
assertTrue(e.estimateStrength("letmein".toCharArray()) < QUITE_STRONG); assertTrue(e.estimateStrength("letmein") < QUITE_STRONG);
assertTrue(e.estimateStrength("123456".toCharArray()) < QUITE_STRONG); assertTrue(e.estimateStrength("123456") < QUITE_STRONG);
} }
@Test @Test
public void testStrongPasswords() { public void testStrongPasswords() {
PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl(); PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl();
// Industry standard // Industry standard
assertTrue(e.estimateStrength("Tr0ub4dor&3".toCharArray()) assertTrue(e.estimateStrength("Tr0ub4dor&3") > QUITE_STRONG);
> QUITE_STRONG); assertTrue(e.estimateStrength("correcthorsebatterystaple")
assertTrue(e.estimateStrength("correcthorsebatterystaple".toCharArray())
> QUITE_STRONG); > QUITE_STRONG);
} }
} }

View File

@@ -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);
}
}

View File

@@ -127,7 +127,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
db.addTransport(transportId, MAX_LATENCY); db.addTransport(transportId, MAX_LATENCY);
Endpoint ep = new Endpoint(contactId, transportId, epoch, true); Endpoint ep = new Endpoint(contactId, transportId, epoch, true);
db.addEndpoint(ep); db.addEndpoint(ep);
keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret);
// Send Bob a message // Send Bob a message
String contentType = "text/plain"; String contentType = "text/plain";
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
@@ -190,7 +190,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
db.addTransport(transportId, MAX_LATENCY); db.addTransport(transportId, MAX_LATENCY);
Endpoint ep = new Endpoint(contactId, transportId, epoch, false); Endpoint ep = new Endpoint(contactId, transportId, epoch, false);
db.addEndpoint(ep); db.addEndpoint(ep);
keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret);
// Set up an event listener // Set up an event listener
MessageListener listener = new MessageListener(); MessageListener listener = new MessageListener();
bob.getInstance(EventBus.class).addListener(listener); bob.getInstance(EventBus.class).addListener(listener);

View File

@@ -103,9 +103,9 @@ public class KeyManagerImplTest extends BriarTestCase {
// The secrets for periods 0 - 2 should be derived // The secrets for periods 0 - 2 should be derived
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -123,11 +123,11 @@ public class KeyManagerImplTest extends BriarTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(EPOCH)); will(returnValue(EPOCH));
oneOf(crypto).deriveNextSecret(initialSecret, 0); oneOf(crypto).deriveNextSecret(initialSecret, 0);
will(returnValue(secret0.clone())); will(returnValue(secret0));
oneOf(crypto).deriveNextSecret(secret0, 1); oneOf(crypto).deriveNextSecret(secret0, 1);
will(returnValue(secret1.clone())); will(returnValue(secret1));
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(db).addSecrets(Arrays.asList(s0, s1, s2)); oneOf(db).addSecrets(Arrays.asList(s0, s1, s2));
// The secrets for periods 0 - 2 should be added to the recogniser // The secrets for periods 0 - 2 should be added to the recogniser
oneOf(tagRecogniser).addSecret(s0); oneOf(tagRecogniser).addSecret(s0);
@@ -140,7 +140,7 @@ public class KeyManagerImplTest extends BriarTestCase {
}}); }});
assertTrue(keyManager.start()); assertTrue(keyManager.start());
keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret);
keyManager.stop(); keyManager.stop();
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -161,9 +161,9 @@ public class KeyManagerImplTest extends BriarTestCase {
// The secrets for periods 0 - 2 should be derived // The secrets for periods 0 - 2 should be derived
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -181,11 +181,11 @@ public class KeyManagerImplTest extends BriarTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(EPOCH)); will(returnValue(EPOCH));
oneOf(crypto).deriveNextSecret(initialSecret, 0); oneOf(crypto).deriveNextSecret(initialSecret, 0);
will(returnValue(secret0.clone())); will(returnValue(secret0));
oneOf(crypto).deriveNextSecret(secret0, 1); oneOf(crypto).deriveNextSecret(secret0, 1);
will(returnValue(secret1.clone())); will(returnValue(secret1));
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(db).addSecrets(Arrays.asList(s0, s1, s2)); oneOf(db).addSecrets(Arrays.asList(s0, s1, s2));
// The secrets for periods 0 - 2 should be added to the recogniser // The secrets for periods 0 - 2 should be added to the recogniser
oneOf(tagRecogniser).addSecret(s0); oneOf(tagRecogniser).addSecret(s0);
@@ -201,7 +201,7 @@ public class KeyManagerImplTest extends BriarTestCase {
}}); }});
assertTrue(keyManager.start()); assertTrue(keyManager.start());
keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret);
StreamContext ctx = StreamContext ctx =
keyManager.getStreamContext(contactId, transportId); keyManager.getStreamContext(contactId, transportId);
assertNotNull(ctx); assertNotNull(ctx);
@@ -230,9 +230,9 @@ public class KeyManagerImplTest extends BriarTestCase {
// The DB contains the secrets for periods 0 - 2 // The DB contains the secrets for periods 0 - 2
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -278,11 +278,11 @@ public class KeyManagerImplTest extends BriarTestCase {
// The DB contains the secrets for periods 0 - 2 // The DB contains the secrets for periods 0 - 2
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
// The secret for period 3 should be derived and stored // 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() {{ context.checking(new Expectations() {{
// start() // start()
@@ -297,11 +297,11 @@ public class KeyManagerImplTest extends BriarTestCase {
will(returnValue(EPOCH + ROTATION_PERIOD)); will(returnValue(EPOCH + ROTATION_PERIOD));
// The secret for period 3 should be derived and stored // The secret for period 3 should be derived and stored
oneOf(crypto).deriveNextSecret(secret0, 1); oneOf(crypto).deriveNextSecret(secret0, 1);
will(returnValue(secret1.clone())); will(returnValue(secret1));
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(crypto).deriveNextSecret(secret2, 3); oneOf(crypto).deriveNextSecret(secret2, 3);
will(returnValue(secret3.clone())); will(returnValue(secret3));
oneOf(db).addSecrets(Arrays.asList(s3)); oneOf(db).addSecrets(Arrays.asList(s3));
// The secrets for periods 1 - 3 should be added to the recogniser // The secrets for periods 1 - 3 should be added to the recogniser
oneOf(tagRecogniser).addSecret(s1); oneOf(tagRecogniser).addSecret(s1);
@@ -336,12 +336,12 @@ public class KeyManagerImplTest extends BriarTestCase {
// The DB contains the secrets for periods 0 - 2 // The DB contains the secrets for periods 0 - 2
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
// The secrets for periods 3 and 4 should be derived and stored // The secrets for periods 3 and 4 should be derived and stored
final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone()); final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3);
final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4.clone()); final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -356,11 +356,11 @@ public class KeyManagerImplTest extends BriarTestCase {
will(returnValue(EPOCH + 3 * ROTATION_PERIOD - 1)); will(returnValue(EPOCH + 3 * ROTATION_PERIOD - 1));
// The secrets for periods 3 and 4 should be derived from secret 1 // The secrets for periods 3 and 4 should be derived from secret 1
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(crypto).deriveNextSecret(secret2, 3); oneOf(crypto).deriveNextSecret(secret2, 3);
will(returnValue(secret3.clone())); will(returnValue(secret3));
oneOf(crypto).deriveNextSecret(secret3, 4); oneOf(crypto).deriveNextSecret(secret3, 4);
will(returnValue(secret4.clone())); will(returnValue(secret4));
// The new secrets should be stored // The new secrets should be stored
oneOf(db).addSecrets(Arrays.asList(s3, s4)); oneOf(db).addSecrets(Arrays.asList(s3, s4));
// The secrets for periods 2 - 4 should be added to the recogniser // 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 // The DB contains the secrets for periods 0 - 2
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -459,11 +459,11 @@ public class KeyManagerImplTest extends BriarTestCase {
// The DB contains the secrets for periods 0 - 2 // The DB contains the secrets for periods 0 - 2
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
// The secret for period 3 should be derived and stored // 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() {{ context.checking(new Expectations() {{
// start() // start()
@@ -486,11 +486,11 @@ public class KeyManagerImplTest extends BriarTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(EPOCH + ROTATION_PERIOD + 1)); will(returnValue(EPOCH + ROTATION_PERIOD + 1));
oneOf(crypto).deriveNextSecret(secret0, 1); oneOf(crypto).deriveNextSecret(secret0, 1);
will(returnValue(secret1.clone())); will(returnValue(secret1));
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(crypto).deriveNextSecret(secret2, 3); oneOf(crypto).deriveNextSecret(secret2, 3);
will(returnValue(secret3.clone())); will(returnValue(secret3));
oneOf(tagRecogniser).removeSecret(contactId, transportId, 0); oneOf(tagRecogniser).removeSecret(contactId, transportId, 0);
oneOf(db).addSecrets(Arrays.asList(s3)); oneOf(db).addSecrets(Arrays.asList(s3));
oneOf(tagRecogniser).addSecret(s3); oneOf(tagRecogniser).addSecret(s3);
@@ -533,12 +533,12 @@ public class KeyManagerImplTest extends BriarTestCase {
// The DB contains the secrets for periods 0 - 2 // The DB contains the secrets for periods 0 - 2
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
// The secrets for periods 3 and 4 should be derived and stored // The secrets for periods 3 and 4 should be derived and stored
final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone()); final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3);
final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4.clone()); final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -561,11 +561,11 @@ public class KeyManagerImplTest extends BriarTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(EPOCH + 2 * ROTATION_PERIOD + 1)); will(returnValue(EPOCH + 2 * ROTATION_PERIOD + 1));
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(crypto).deriveNextSecret(secret2, 3); oneOf(crypto).deriveNextSecret(secret2, 3);
will(returnValue(secret3.clone())); will(returnValue(secret3));
oneOf(crypto).deriveNextSecret(secret3, 4); oneOf(crypto).deriveNextSecret(secret3, 4);
will(returnValue(secret4.clone())); will(returnValue(secret4));
oneOf(tagRecogniser).removeSecret(contactId, transportId, 0); oneOf(tagRecogniser).removeSecret(contactId, transportId, 0);
oneOf(tagRecogniser).removeSecret(contactId, transportId, 1); oneOf(tagRecogniser).removeSecret(contactId, transportId, 1);
oneOf(db).addSecrets(Arrays.asList(s3, s4)); oneOf(db).addSecrets(Arrays.asList(s3, s4));

View File

@@ -40,6 +40,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
private final TransportId transportId; private final TransportId transportId;
private final byte[] secret0, secret1, secret2, secret3, secret4; private final byte[] secret0, secret1, secret2, secret3, secret4;
private final byte[] key0, key1, key2, key3, key4; private final byte[] key0, key1, key2, key3, key4;
private final SecretKey k0, k1, k2, k3, k4;
private final byte[] initialSecret; private final byte[] initialSecret;
public KeyRotationIntegrationTest() { public KeyRotationIntegrationTest() {
@@ -60,6 +61,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
key2 = new byte[32]; key2 = new byte[32];
key3 = new byte[32]; key3 = new byte[32];
key4 = 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 < key0.length; i++) key0[i] = 1;
for(int i = 0; i < key1.length; i++) key1[i] = 2; for(int i = 0; i < key1.length; i++) key1[i] = 2;
for(int i = 0; i < key2.length; i++) key2[i] = 3; 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 EventBus eventBus = context.mock(EventBus.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Timer timer = context.mock(Timer.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 TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db);
final KeyManagerImpl keyManager = new KeyManagerImpl(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 // The secrets for periods 0 - 2 should be derived
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -142,11 +145,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(EPOCH)); will(returnValue(EPOCH));
oneOf(crypto).deriveNextSecret(initialSecret, 0); oneOf(crypto).deriveNextSecret(initialSecret, 0);
will(returnValue(secret0.clone())); will(returnValue(secret0));
oneOf(crypto).deriveNextSecret(secret0, 1); oneOf(crypto).deriveNextSecret(secret0, 1);
will(returnValue(secret1.clone())); will(returnValue(secret1));
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(db).addSecrets(Arrays.asList(s0, s1, s2)); oneOf(db).addSecrets(Arrays.asList(s0, s1, s2));
// The recogniser should derive the tags for period 0 // The recogniser should derive the tags for period 0
oneOf(crypto).deriveTagKey(secret0, false); oneOf(crypto).deriveTagKey(secret0, false);
@@ -155,10 +158,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k0).getEncoded();
will(returnValue(key0));
} }
oneOf(k0).erase();
// The recogniser should derive the tags for period 1 // The recogniser should derive the tags for period 1
oneOf(crypto).deriveTagKey(secret1, false); oneOf(crypto).deriveTagKey(secret1, false);
will(returnValue(k1)); will(returnValue(k1));
@@ -166,10 +166,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k1).getEncoded();
will(returnValue(key1));
} }
oneOf(k1).erase();
// The recogniser should derive the tags for period 2 // The recogniser should derive the tags for period 2
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
@@ -177,10 +174,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// stop() // stop()
// The recogniser should derive the tags for period 0 // The recogniser should derive the tags for period 0
oneOf(crypto).deriveTagKey(secret0, false); oneOf(crypto).deriveTagKey(secret0, false);
@@ -189,10 +183,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k0).getEncoded();
will(returnValue(key0));
} }
oneOf(k0).erase();
// The recogniser should derive the tags for period 1 // The recogniser should derive the tags for period 1
oneOf(crypto).deriveTagKey(secret1, false); oneOf(crypto).deriveTagKey(secret1, false);
will(returnValue(k1)); will(returnValue(k1));
@@ -200,10 +191,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k1).getEncoded();
will(returnValue(key1));
} }
oneOf(k1).erase();
// The recogniser should derive the tags for period 2 // The recogniser should derive the tags for period 2
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
@@ -211,17 +199,14 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// Remove the listener and stop the timer // Remove the listener and stop the timer
oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(eventBus).removeListener(with(any(EventListener.class)));
oneOf(timer).cancel(); oneOf(timer).cancel();
}}); }});
assertTrue(keyManager.start()); assertTrue(keyManager.start());
keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret);
keyManager.stop(); keyManager.stop();
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -235,9 +220,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
final EventBus eventBus = context.mock(EventBus.class); final EventBus eventBus = context.mock(EventBus.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Timer timer = context.mock(Timer.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 TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db);
final KeyManagerImpl keyManager = new KeyManagerImpl(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 // The secrets for periods 0 - 2 should be derived
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -265,11 +247,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(EPOCH)); will(returnValue(EPOCH));
oneOf(crypto).deriveNextSecret(initialSecret, 0); oneOf(crypto).deriveNextSecret(initialSecret, 0);
will(returnValue(secret0.clone())); will(returnValue(secret0));
oneOf(crypto).deriveNextSecret(secret0, 1); oneOf(crypto).deriveNextSecret(secret0, 1);
will(returnValue(secret1.clone())); will(returnValue(secret1));
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(db).addSecrets(Arrays.asList(s0, s1, s2)); oneOf(db).addSecrets(Arrays.asList(s0, s1, s2));
// The recogniser should derive the tags for period 0 // The recogniser should derive the tags for period 0
oneOf(crypto).deriveTagKey(secret0, false); oneOf(crypto).deriveTagKey(secret0, false);
@@ -278,10 +260,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k0).getEncoded();
will(returnValue(key0));
} }
oneOf(k0).erase();
// The recogniser should derive the tags for period 1 // The recogniser should derive the tags for period 1
oneOf(crypto).deriveTagKey(secret1, false); oneOf(crypto).deriveTagKey(secret1, false);
will(returnValue(k1)); will(returnValue(k1));
@@ -289,10 +268,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k1).getEncoded();
will(returnValue(key1));
} }
oneOf(k1).erase();
// The recogniser should derive the tags for period 2 // The recogniser should derive the tags for period 2
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
@@ -300,10 +276,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// getConnectionContext() // getConnectionContext()
oneOf(db).incrementStreamCounter(contactId, transportId, 1); oneOf(db).incrementStreamCounter(contactId, transportId, 1);
will(returnValue(0L)); will(returnValue(0L));
@@ -315,10 +288,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k0).getEncoded();
will(returnValue(key0));
} }
oneOf(k0).erase();
// The recogniser should derive the tags for period 1 // The recogniser should derive the tags for period 1
oneOf(crypto).deriveTagKey(secret1, false); oneOf(crypto).deriveTagKey(secret1, false);
will(returnValue(k1)); will(returnValue(k1));
@@ -326,10 +296,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k1).getEncoded();
will(returnValue(key1));
} }
oneOf(k1).erase();
// The recogniser should derive the tags for period 2 // The recogniser should derive the tags for period 2
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
@@ -337,17 +304,14 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// Remove the listener and stop the timer // Remove the listener and stop the timer
oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(eventBus).removeListener(with(any(EventListener.class)));
oneOf(timer).cancel(); oneOf(timer).cancel();
}}); }});
assertTrue(keyManager.start()); assertTrue(keyManager.start());
keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret.clone()); keyManager.endpointAdded(ep, MAX_LATENCY, initialSecret);
StreamContext ctx = StreamContext ctx =
keyManager.getStreamContext(contactId, transportId); keyManager.getStreamContext(contactId, transportId);
assertNotNull(ctx); assertNotNull(ctx);
@@ -369,9 +333,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
final EventBus eventBus = context.mock(EventBus.class); final EventBus eventBus = context.mock(EventBus.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Timer timer = context.mock(Timer.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 TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db);
final KeyManagerImpl keyManager = new KeyManagerImpl(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 // The secrets for periods 0 - 2 should be derived
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -399,11 +360,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(EPOCH)); will(returnValue(EPOCH));
oneOf(crypto).deriveNextSecret(initialSecret, 0); oneOf(crypto).deriveNextSecret(initialSecret, 0);
will(returnValue(secret0.clone())); will(returnValue(secret0));
oneOf(crypto).deriveNextSecret(secret0, 1); oneOf(crypto).deriveNextSecret(secret0, 1);
will(returnValue(secret1.clone())); will(returnValue(secret1));
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(db).addSecrets(Arrays.asList(s0, s1, s2)); oneOf(db).addSecrets(Arrays.asList(s0, s1, s2));
// The recogniser should derive the tags for period 0 // The recogniser should derive the tags for period 0
oneOf(crypto).deriveTagKey(secret0, false); oneOf(crypto).deriveTagKey(secret0, false);
@@ -412,10 +373,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k0).getEncoded();
will(returnValue(key0));
} }
oneOf(k0).erase();
// The recogniser should derive the tags for period 1 // The recogniser should derive the tags for period 1
oneOf(crypto).deriveTagKey(secret1, false); oneOf(crypto).deriveTagKey(secret1, false);
will(returnValue(k1)); will(returnValue(k1));
@@ -423,10 +381,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k1).getEncoded();
will(returnValue(key1));
} }
oneOf(k1).erase();
// The recogniser should derive the tags for period 2 // The recogniser should derive the tags for period 2
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
@@ -434,21 +389,15 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// acceptConnection() // acceptConnection()
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
oneOf(crypto).encodeTag(with(any(byte[].class)), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with(k2), with(16L)); with(16L));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
oneOf(db).setReorderingWindow(contactId, transportId, 2, 1, oneOf(db).setReorderingWindow(contactId, transportId, 2, 1,
new byte[] {0, 1, 0, 0}); new byte[] {0, 1, 0, 0});
oneOf(k2).erase();
// stop() // stop()
// The recogniser should derive the tags for period 0 // The recogniser should derive the tags for period 0
oneOf(crypto).deriveTagKey(secret0, false); oneOf(crypto).deriveTagKey(secret0, false);
@@ -457,10 +406,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k0).getEncoded();
will(returnValue(key0));
} }
oneOf(k0).erase();
// The recogniser should derive the tags for period 1 // The recogniser should derive the tags for period 1
oneOf(crypto).deriveTagKey(secret1, false); oneOf(crypto).deriveTagKey(secret1, false);
will(returnValue(k1)); will(returnValue(k1));
@@ -468,10 +414,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k1).getEncoded();
will(returnValue(key1));
} }
oneOf(k1).erase();
// The recogniser should derive the updated tags for period 2 // The recogniser should derive the updated tags for period 2
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
@@ -479,17 +422,14 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// Remove the listener and stop the timer // Remove the listener and stop the timer
oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(eventBus).removeListener(with(any(EventListener.class)));
oneOf(timer).cancel(); oneOf(timer).cancel();
}}); }});
assertTrue(keyManager.start()); 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 // Recognise the tag for connection 0 in period 2
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
encodeTag(tag, key2, 0); encodeTag(tag, key2, 0);
@@ -513,9 +453,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
final EventBus eventBus = context.mock(EventBus.class); final EventBus eventBus = context.mock(EventBus.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Timer timer = context.mock(Timer.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 TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db);
final KeyManagerImpl keyManager = new KeyManagerImpl(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 // The DB contains the secrets for periods 0 - 2
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -545,10 +482,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k0).getEncoded();
will(returnValue(key0));
} }
oneOf(k0).erase();
// The recogniser should derive the tags for period 1 // The recogniser should derive the tags for period 1
oneOf(crypto).deriveTagKey(secret1, false); oneOf(crypto).deriveTagKey(secret1, false);
will(returnValue(k1)); will(returnValue(k1));
@@ -556,10 +490,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k1).getEncoded();
will(returnValue(key1));
} }
oneOf(k1).erase();
// The recogniser should derive the tags for period 2 // The recogniser should derive the tags for period 2
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
@@ -567,10 +498,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// Start the timer // Start the timer
oneOf(timer).scheduleAtFixedRate(with(keyManager), oneOf(timer).scheduleAtFixedRate(with(keyManager),
with(any(long.class)), with(any(long.class))); 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), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k0),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k0).getEncoded();
will(returnValue(key0));
} }
oneOf(k0).erase();
// The recogniser should derive the tags for period 1 // The recogniser should derive the tags for period 1
oneOf(crypto).deriveTagKey(secret1, false); oneOf(crypto).deriveTagKey(secret1, false);
will(returnValue(k1)); will(returnValue(k1));
@@ -593,10 +518,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k1).getEncoded();
will(returnValue(key1));
} }
oneOf(k1).erase();
// The recogniser should derive the tags for period 2 // The recogniser should derive the tags for period 2
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
@@ -604,10 +526,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// Remove the listener and stop the timer // Remove the listener and stop the timer
oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(eventBus).removeListener(with(any(EventListener.class)));
oneOf(timer).cancel(); oneOf(timer).cancel();
@@ -627,9 +546,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
final EventBus eventBus = context.mock(EventBus.class); final EventBus eventBus = context.mock(EventBus.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Timer timer = context.mock(Timer.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 TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db);
final KeyManagerImpl keyManager = new KeyManagerImpl(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 // The DB contains the secrets for periods 0 - 2
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
// The secret for period 3 should be derived and stored // 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() {{ context.checking(new Expectations() {{
// start() // start()
@@ -656,11 +572,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
will(returnValue(EPOCH + ROTATION_PERIOD)); will(returnValue(EPOCH + ROTATION_PERIOD));
// The secret for period 3 should be derived and stored // The secret for period 3 should be derived and stored
oneOf(crypto).deriveNextSecret(secret0, 1); oneOf(crypto).deriveNextSecret(secret0, 1);
will(returnValue(secret1.clone())); will(returnValue(secret1));
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(crypto).deriveNextSecret(secret2, 3); oneOf(crypto).deriveNextSecret(secret2, 3);
will(returnValue(secret3.clone())); will(returnValue(secret3));
oneOf(db).addSecrets(Arrays.asList(s3)); oneOf(db).addSecrets(Arrays.asList(s3));
// The recogniser should derive the tags for period 1 // The recogniser should derive the tags for period 1
oneOf(crypto).deriveTagKey(secret1, false); oneOf(crypto).deriveTagKey(secret1, false);
@@ -669,10 +585,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k1).getEncoded();
will(returnValue(key1));
} }
oneOf(k1).erase();
// The recogniser should derive the tags for period 2 // The recogniser should derive the tags for period 2
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
@@ -680,10 +593,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// The recogniser should derive the tags for period 3 // The recogniser should derive the tags for period 3
oneOf(crypto).deriveTagKey(secret3, false); oneOf(crypto).deriveTagKey(secret3, false);
will(returnValue(k3)); will(returnValue(k3));
@@ -691,10 +601,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k3).getEncoded();
will(returnValue(key3));
} }
oneOf(k3).erase();
// Start the timer // Start the timer
oneOf(timer).scheduleAtFixedRate(with(keyManager), oneOf(timer).scheduleAtFixedRate(with(keyManager),
with(any(long.class)), with(any(long.class))); 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), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k1),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k1).getEncoded();
will(returnValue(key1));
} }
oneOf(k1).erase();
// The recogniser should derive the tags for period 2 // The recogniser should derive the tags for period 2
oneOf(crypto).deriveTagKey(secret2, false); oneOf(crypto).deriveTagKey(secret2, false);
will(returnValue(k2)); will(returnValue(k2));
@@ -717,10 +621,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// The recogniser should remove the tags for period 3 // The recogniser should remove the tags for period 3
oneOf(crypto).deriveTagKey(secret3, false); oneOf(crypto).deriveTagKey(secret3, false);
will(returnValue(k3)); will(returnValue(k3));
@@ -728,10 +629,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k3).getEncoded();
will(returnValue(key3));
} }
oneOf(k3).erase();
// Remove the listener and stop the timer // Remove the listener and stop the timer
oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(eventBus).removeListener(with(any(EventListener.class)));
oneOf(timer).cancel(); oneOf(timer).cancel();
@@ -751,9 +649,6 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
final EventBus eventBus = context.mock(EventBus.class); final EventBus eventBus = context.mock(EventBus.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Timer timer = context.mock(Timer.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 TagRecogniser tagRecogniser = new TagRecogniserImpl(crypto, db);
final KeyManagerImpl keyManager = new KeyManagerImpl(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 // The DB contains the secrets for periods 0 - 2
Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true); Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone()); final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone()); final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone()); final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
// The secrets for periods 3 and 4 should be derived and stored // The secrets for periods 3 and 4 should be derived and stored
final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone()); final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3);
final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4.clone()); final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
@@ -781,11 +676,11 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
will(returnValue(EPOCH + 3 * ROTATION_PERIOD - 1)); will(returnValue(EPOCH + 3 * ROTATION_PERIOD - 1));
// The secrets for periods 3 and 4 should be derived from secret 1 // The secrets for periods 3 and 4 should be derived from secret 1
oneOf(crypto).deriveNextSecret(secret1, 2); oneOf(crypto).deriveNextSecret(secret1, 2);
will(returnValue(secret2.clone())); will(returnValue(secret2));
oneOf(crypto).deriveNextSecret(secret2, 3); oneOf(crypto).deriveNextSecret(secret2, 3);
will(returnValue(secret3.clone())); will(returnValue(secret3));
oneOf(crypto).deriveNextSecret(secret3, 4); oneOf(crypto).deriveNextSecret(secret3, 4);
will(returnValue(secret4.clone())); will(returnValue(secret4));
// The new secrets should be stored // The new secrets should be stored
oneOf(db).addSecrets(Arrays.asList(s3, s4)); oneOf(db).addSecrets(Arrays.asList(s3, s4));
// The recogniser should derive the tags for period 2 // 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), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// The recogniser should derive the tags for period 3 // The recogniser should derive the tags for period 3
oneOf(crypto).deriveTagKey(secret3, false); oneOf(crypto).deriveTagKey(secret3, false);
will(returnValue(k3)); will(returnValue(k3));
@@ -806,10 +698,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k3).getEncoded();
will(returnValue(key3));
} }
oneOf(k3).erase();
// The recogniser should derive the tags for period 4 // The recogniser should derive the tags for period 4
oneOf(crypto).deriveTagKey(secret4, false); oneOf(crypto).deriveTagKey(secret4, false);
will(returnValue(k4)); will(returnValue(k4));
@@ -817,10 +706,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k4), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k4),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k4).getEncoded();
will(returnValue(key4));
} }
oneOf(k4).erase();
// Start the timer // Start the timer
oneOf(timer).scheduleAtFixedRate(with(keyManager), oneOf(timer).scheduleAtFixedRate(with(keyManager),
with(any(long.class)), with(any(long.class))); 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), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k2),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k2).getEncoded();
will(returnValue(key2));
} }
oneOf(k2).erase();
// The recogniser should remove the tags for period 3 // The recogniser should remove the tags for period 3
oneOf(crypto).deriveTagKey(secret3, false); oneOf(crypto).deriveTagKey(secret3, false);
will(returnValue(k3)); will(returnValue(k3));
@@ -843,10 +726,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k3),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k3).getEncoded();
will(returnValue(key3));
} }
oneOf(k3).erase();
// The recogniser should derive the tags for period 4 // The recogniser should derive the tags for period 4
oneOf(crypto).deriveTagKey(secret4, false); oneOf(crypto).deriveTagKey(secret4, false);
will(returnValue(k4)); will(returnValue(k4));
@@ -854,10 +734,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
oneOf(crypto).encodeTag(with(any(byte[].class)), with(k4), oneOf(crypto).encodeTag(with(any(byte[].class)), with(k4),
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
oneOf(k4).getEncoded();
will(returnValue(key4));
} }
oneOf(k4).erase();
// Remove the listener and stop the timer // Remove the listener and stop the timer
oneOf(eventBus).removeListener(with(any(EventListener.class))); oneOf(eventBus).removeListener(with(any(EventListener.class)));
oneOf(timer).cancel(); oneOf(timer).cancel();
@@ -885,7 +762,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
byte[] tag = (byte[]) invocation.getParameter(0); byte[] tag = (byte[]) invocation.getParameter(0);
SecretKey key = (SecretKey) invocation.getParameter(1); SecretKey key = (SecretKey) invocation.getParameter(1);
long streamNumber = (Long) invocation.getParameter(2); long streamNumber = (Long) invocation.getParameter(2);
encodeTag(tag, key.getEncoded(), streamNumber); encodeTag(tag, key.getBytes(), streamNumber);
return null; return null;
} }
} }

View File

@@ -25,6 +25,7 @@ public class TransportTagRecogniserTest extends BriarTestCase {
private final ContactId contactId = new ContactId(234); private final ContactId contactId = new ContactId(234);
private final TransportId transportId = new TransportId("id"); private final TransportId transportId = new TransportId("id");
private final SecretKey tagKey = new SecretKey(new byte[32]);
@Test @Test
public void testAddAndRemoveSecret() { public void testAddAndRemoveSecret() {
@@ -33,7 +34,6 @@ public class TransportTagRecogniserTest extends BriarTestCase {
final byte[] secret = new byte[32]; final byte[] secret = new byte[32];
new Random().nextBytes(secret); new Random().nextBytes(secret);
final boolean alice = false; final boolean alice = false;
final SecretKey tagKey = context.mock(SecretKey.class);
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Add secret // Add secret
@@ -44,7 +44,6 @@ public class TransportTagRecogniserTest extends BriarTestCase {
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
oneOf(tagKey).erase();
// Remove secret // Remove secret
oneOf(crypto).deriveTagKey(secret, !alice); oneOf(crypto).deriveTagKey(secret, !alice);
will(returnValue(tagKey)); will(returnValue(tagKey));
@@ -53,7 +52,6 @@ public class TransportTagRecogniserTest extends BriarTestCase {
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
oneOf(tagKey).erase();
}}); }});
TemporarySecret s = new TemporarySecret(contactId, transportId, 123, TemporarySecret s = new TemporarySecret(contactId, transportId, 123,
alice, 0, secret, 0, 0, new byte[4]); alice, 0, secret, 0, 0, new byte[4]);
@@ -71,7 +69,6 @@ public class TransportTagRecogniserTest extends BriarTestCase {
final byte[] secret = new byte[32]; final byte[] secret = new byte[32];
new Random().nextBytes(secret); new Random().nextBytes(secret);
final boolean alice = false; final boolean alice = false;
final SecretKey tagKey = context.mock(SecretKey.class);
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Add secret // Add secret
@@ -82,7 +79,6 @@ public class TransportTagRecogniserTest extends BriarTestCase {
with((long) i)); with((long) i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
oneOf(tagKey).erase();
// Recognise tag 0 // Recognise tag 0
oneOf(crypto).deriveTagKey(secret, !alice); oneOf(crypto).deriveTagKey(secret, !alice);
will(returnValue(tagKey)); will(returnValue(tagKey));
@@ -93,7 +89,6 @@ public class TransportTagRecogniserTest extends BriarTestCase {
// The updated window should be stored // The updated window should be stored
oneOf(db).setReorderingWindow(contactId, transportId, 0, 1, oneOf(db).setReorderingWindow(contactId, transportId, 0, 1,
new byte[] {0, 1, 0, 0}); new byte[] {0, 1, 0, 0});
oneOf(tagKey).erase();
// Recognise tag again - no expectations // Recognise tag again - no expectations
}}); }});
TemporarySecret s = new TemporarySecret(contactId, transportId, 123, TemporarySecret s = new TemporarySecret(contactId, transportId, 123,