Compare commits

..

1 Commits

Author SHA1 Message Date
akwizgran
4fcce7116c Decouple poller from plugin manager. 2019-05-14 17:52:44 +01:00
210 changed files with 3193 additions and 5052 deletions

View File

@@ -11,8 +11,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 26 targetSdkVersion 26
versionCode 10107 versionCode 10106
versionName "1.1.7" versionName "1.1.6"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

View File

@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.util.IoUtils;
import java.io.File; import java.io.File;
import java.util.HashSet; import java.util.HashSet;
@@ -19,9 +20,6 @@ import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
class AndroidAccountManager extends AccountManagerImpl class AndroidAccountManager extends AccountManagerImpl
implements AccountManager { implements AccountManager {
@@ -43,16 +41,6 @@ class AndroidAccountManager extends AccountManagerImpl
appContext = app.getApplicationContext(); appContext = app.getApplicationContext();
} }
@Override
public boolean accountExists() {
boolean exists = super.accountExists();
if (!exists && LOG.isLoggable(INFO)) {
LOG.info("Account does not exist. Contents of account directory:");
logFileOrDir(LOG, INFO, getDataDir());
}
return exists;
}
// Locking: stateChangeLock // Locking: stateChangeLock
@Override @Override
@Nullable @Nullable
@@ -86,17 +74,9 @@ class AndroidAccountManager extends AccountManagerImpl
@Override @Override
public void deleteAccount() { public void deleteAccount() {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
if (LOG.isLoggable(INFO)) {
LOG.info("Contents of account directory before deleting:");
logFileOrDir(LOG, INFO, getDataDir());
}
super.deleteAccount(); super.deleteAccount();
SharedPreferences defaultPrefs = getDefaultSharedPreferences(); SharedPreferences defaultPrefs = getDefaultSharedPreferences();
deleteAppData(prefs, defaultPrefs); deleteAppData(prefs, defaultPrefs);
if (LOG.isLoggable(INFO)) {
LOG.info("Contents of account directory after deleting:");
logFileOrDir(LOG, INFO, getDataDir());
}
} }
} }
@@ -114,7 +94,7 @@ class AndroidAccountManager extends AccountManagerImpl
} }
// Delete files, except lib and shared_prefs directories // Delete files, except lib and shared_prefs directories
Set<File> files = new HashSet<>(); Set<File> files = new HashSet<>();
File dataDir = getDataDir(); File dataDir = new File(appContext.getApplicationInfo().dataDir);
@Nullable @Nullable
File[] fileArray = dataDir.listFiles(); File[] fileArray = dataDir.listFiles();
if (fileArray == null) { if (fileArray == null) {
@@ -141,17 +121,13 @@ class AndroidAccountManager extends AccountManagerImpl
} }
} }
for (File file : files) { for (File file : files) {
deleteFileOrDir(file); IoUtils.deleteFileOrDir(file);
} }
// Recreate the cache dir as some OpenGL drivers expect it to exist // Recreate the cache dir as some OpenGL drivers expect it to exist
if (!new File(dataDir, "cache").mkdirs()) if (!new File(dataDir, "cache").mkdirs())
LOG.warning("Could not recreate cache dir"); LOG.warning("Could not recreate cache dir");
} }
private File getDataDir() {
return new File(appContext.getApplicationInfo().dataDir);
}
private void addIfNotNull(Set<File> files, @Nullable File file) { private void addIfNotNull(Set<File> files, @Nullable File file) {
if (file != null) files.add(file); if (file != null) files.add(file);
} }

View File

@@ -128,7 +128,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
will(returnValue(editor)); will(returnValue(editor));
oneOf(editor).commit(); oneOf(editor).commit();
will(returnValue(true)); will(returnValue(true));
allowing(app).getApplicationInfo(); oneOf(app).getApplicationInfo();
will(returnValue(applicationInfo)); will(returnValue(applicationInfo));
oneOf(app).getFilesDir(); oneOf(app).getFilesDir();
will(returnValue(filesDir)); will(returnValue(filesDir));

View File

@@ -6,7 +6,6 @@ import org.briarproject.bramble.util.StringUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
/** /**
@@ -39,7 +38,7 @@ public class Bytes implements Comparable<Bytes> {
} }
@Override @Override
public boolean equals(@Nullable Object o) { public boolean equals(Object o) {
return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes); return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes);
} }

View File

@@ -1,8 +1,6 @@
package org.briarproject.bramble.api.client; package org.briarproject.bramble.api.client;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -98,18 +96,14 @@ public interface ClientHelper {
BdfList toList(Author a); BdfList toList(Author a);
byte[] sign(String label, BdfList toSign, PrivateKey privateKey) byte[] sign(String label, BdfList toSign, byte[] privateKey)
throws FormatException, GeneralSecurityException; throws FormatException, GeneralSecurityException;
void verifySignature(byte[] signature, String label, BdfList signed, void verifySignature(byte[] signature, String label, BdfList signed,
PublicKey publicKey) byte[] publicKey) throws FormatException, GeneralSecurityException;
throws FormatException, GeneralSecurityException;
Author parseAndValidateAuthor(BdfList author) throws FormatException; Author parseAndValidateAuthor(BdfList author) throws FormatException;
PublicKey parseAndValidateAgreementPublicKey(byte[] publicKeyBytes)
throws FormatException;
TransportProperties parseAndValidateTransportProperties( TransportProperties parseAndValidateTransportProperties(
BdfDictionary properties) throws FormatException; BdfDictionary properties) throws FormatException;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -9,6 +8,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.util.StringUtils.toUtf8; import static org.briarproject.bramble.util.StringUtils.toUtf8;
@Immutable @Immutable
@@ -21,17 +21,21 @@ public class Contact {
@Nullable @Nullable
private final String alias; private final String alias;
@Nullable @Nullable
private final PublicKey handshakePublicKey; private final byte[] handshakePublicKey;
private final boolean verified; private final boolean verified;
public Contact(ContactId id, Author author, AuthorId localAuthorId, public Contact(ContactId id, Author author, AuthorId localAuthorId,
@Nullable String alias, @Nullable PublicKey handshakePublicKey, @Nullable String alias, @Nullable byte[] handshakePublicKey,
boolean verified) { boolean verified) {
if (alias != null) { if (alias != null) {
int aliasLength = toUtf8(alias).length; int aliasLength = toUtf8(alias).length;
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH) if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
if (handshakePublicKey != null && (handshakePublicKey.length == 0 ||
handshakePublicKey.length > MAX_PUBLIC_KEY_LENGTH)) {
throw new IllegalArgumentException();
}
this.id = id; this.id = id;
this.author = author; this.author = author;
this.localAuthorId = localAuthorId; this.localAuthorId = localAuthorId;
@@ -58,7 +62,7 @@ public class Contact {
} }
@Nullable @Nullable
public PublicKey getHandshakePublicKey() { public byte[] getHandshakePublicKey() {
return handshakePublicKey; return handshakePublicKey;
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
@@ -29,7 +28,7 @@ public class ContactId {
} }
@Override @Override
public boolean equals(@Nullable Object o) { public boolean equals(Object o) {
return o instanceof ContactId && id == ((ContactId) o).id; return o instanceof ContactId && id == ((ContactId) o).id;
} }
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@@ -10,12 +9,12 @@ import javax.annotation.concurrent.Immutable;
public class PendingContact { public class PendingContact {
private final PendingContactId id; private final PendingContactId id;
private final PublicKey publicKey; private final byte[] publicKey;
private final String alias; private final String alias;
private final PendingContactState state; private final PendingContactState state;
private final long timestamp; private final long timestamp;
public PendingContact(PendingContactId id, PublicKey publicKey, public PendingContact(PendingContactId id, byte[] publicKey,
String alias, PendingContactState state, long timestamp) { String alias, PendingContactState state, long timestamp) {
this.id = id; this.id = id;
this.publicKey = publicKey; this.publicKey = publicKey;
@@ -28,7 +27,7 @@ public class PendingContact {
return id; return id;
} }
public PublicKey getPublicKey() { public byte[] getPublicKey() {
return publicKey; return publicKey;
} }

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.UniqueId; import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
/** /**
@@ -18,8 +17,9 @@ public class PendingContactId extends UniqueId {
super(id); super(id);
} }
@Override @Override
public boolean equals(@Nullable Object o) { public boolean equals(Object o) {
return o instanceof PendingContactId && super.equals(o); return o instanceof PendingContactId && super.equals(o);
} }
} }

View File

@@ -1,30 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
/**
* Type-safe wrapper for a private key used for key agreement.
*/
@Immutable
@NotNullByDefault
public class AgreementPrivateKey extends Bytes implements PrivateKey {
public AgreementPrivateKey(byte[] encoded) {
super(encoded);
}
@Override
public String getKeyType() {
return KEY_TYPE_AGREEMENT;
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -1,35 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
/**
* Type-safe wrapper for a public key used for key agreement.
*/
@Immutable
@NotNullByDefault
public class AgreementPublicKey extends Bytes implements PublicKey {
public AgreementPublicKey(byte[] encoded) {
super(encoded);
if (encoded.length == 0 ||
encoded.length > MAX_AGREEMENT_PUBLIC_KEY_BYTES) {
throw new IllegalArgumentException();
}
}
@Override
public String getKeyType() {
return KEY_TYPE_AGREEMENT;
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -55,7 +55,7 @@ public interface CryptoComponent {
* signature, to prevent it from being repurposed or colliding with a * signature, to prevent it from being repurposed or colliding with a
* signature created for another purpose * signature created for another purpose
*/ */
byte[] sign(String label, byte[] toSign, PrivateKey privateKey) byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException; throws GeneralSecurityException;
/** /**
@@ -68,7 +68,7 @@ public interface CryptoComponent {
* @return true if the signature was valid, false otherwise. * @return true if the signature was valid, false otherwise.
*/ */
boolean verifySignature(byte[] signature, String label, byte[] signed, boolean verifySignature(byte[] signature, String label, byte[] signed,
PublicKey publicKey) throws GeneralSecurityException; byte[] publicKey) throws GeneralSecurityException;
/** /**
* Returns the hash of the given inputs. The inputs are unambiguously * Returns the hash of the given inputs. The inputs are unambiguously

View File

@@ -7,21 +7,11 @@ public interface CryptoConstants {
*/ */
int MAX_AGREEMENT_PUBLIC_KEY_BYTES = 32; int MAX_AGREEMENT_PUBLIC_KEY_BYTES = 32;
/**
* The key type for agreement key pairs.
*/
String KEY_TYPE_AGREEMENT = "Curve25519";
/** /**
* The maximum length of a signature public key in bytes. * The maximum length of a signature public key in bytes.
*/ */
int MAX_SIGNATURE_PUBLIC_KEY_BYTES = 32; int MAX_SIGNATURE_PUBLIC_KEY_BYTES = 32;
/**
* The key type for signature key pairs.
*/
String KEY_TYPE_SIGNATURE = "Ed25519";
/** /**
* The maximum length of a signature in bytes. * The maximum length of a signature in bytes.
*/ */

View File

@@ -15,8 +15,6 @@ public class KeyPair {
private final PrivateKey privateKey; private final PrivateKey privateKey;
public KeyPair(PublicKey publicKey, PrivateKey privateKey) { public KeyPair(PublicKey publicKey, PrivateKey privateKey) {
if (!publicKey.getKeyType().equals(privateKey.getKeyType()))
throw new IllegalArgumentException();
this.publicKey = publicKey; this.publicKey = publicKey;
this.privateKey = privateKey; this.privateKey = privateKey;
} }

View File

@@ -8,11 +8,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface PrivateKey { public interface PrivateKey {
/**
* Returns the type of this key pair.
*/
String getKeyType();
/** /**
* Returns the encoded representation of this key. * Returns the encoded representation of this key.
*/ */

View File

@@ -8,11 +8,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface PublicKey { public interface PublicKey {
/**
* Returns the type of this key pair.
*/
String getKeyType();
/** /**
* Returns the encoded representation of this key. * Returns the encoded representation of this key.
*/ */

View File

@@ -1,30 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
/**
* Type-safe wrapper for a public key used for signing.
*/
@Immutable
@NotNullByDefault
public class SignaturePrivateKey extends Bytes implements PrivateKey {
public SignaturePrivateKey(byte[] bytes) {
super(bytes);
}
@Override
public String getKeyType() {
return KEY_TYPE_SIGNATURE;
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -1,35 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
/**
* Type-safe wrapper for a public key used for verifying signatures.
*/
@Immutable
@NotNullByDefault
public class SignaturePublicKey extends Bytes implements PublicKey {
public SignaturePublicKey(byte[] encoded) {
super(encoded);
if (encoded.length == 0 ||
encoded.length > MAX_SIGNATURE_PUBLIC_KEY_BYTES) {
throw new IllegalArgumentException();
}
}
@Override
public String getKeyType() {
return KEY_TYPE_SIGNATURE;
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api.crypto; package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
/** /**
@@ -10,29 +11,35 @@ import org.briarproject.bramble.api.transport.TransportKeys;
public interface TransportCrypto { public interface TransportCrypto {
/** /**
* Derives initial rotation mode transport keys for the given transport in * Derives initial transport keys for the given transport in the given
* the given time period from the given root key. * time period from the given root key.
* *
* @param alice Whether the keys are for use by Alice or Bob * @param alice whether the keys are for use by Alice or Bob.
* @param active Whether the keys are usable for outgoing streams * @param active whether the keys are usable for outgoing streams.
*/ */
TransportKeys deriveRotationKeys(TransportId t, SecretKey rootKey, TransportKeys deriveTransportKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean alice, boolean active); long timePeriod, boolean alice, boolean active);
/**
* Rotates the given transport keys to the given time period. If the keys
* are for the given period or any later period they are not rotated.
*/
TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod);
/** /**
* Derives handshake keys for the given transport in the given time period * Derives handshake keys for the given transport in the given time period
* from the given root key. * from the given root key.
* *
* @param alice Whether the keys are for use by Alice or Bob * @param alice whether the keys are for use by Alice or Bob.
*/ */
TransportKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey, HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean alice); long timePeriod, boolean alice);
/** /**
* Updates the given transport keys to the given time period. If the keys * Updates the given handshake keys to the given time period. If the keys
* are for the given period or any later period they are not updated. * are for the given period or any later period they are not updated.
*/ */
TransportKeys updateTransportKeys(TransportKeys k, long timePeriod); HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod);
/** /**
* Encodes the pseudo-random tag that is used to recognise a stream. * Encodes the pseudo-random tag that is used to recognise a stream.

View File

@@ -4,8 +4,6 @@ import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -24,8 +22,11 @@ import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
@@ -112,6 +113,20 @@ public interface DatabaseComponent {
*/ */
void addGroup(Transaction txn, Group g) throws DbException; void addGroup(Transaction txn, Group g) throws DbException;
/**
* Stores the given handshake keys for the given contact and returns a
* key set ID.
*/
HandshakeKeySetId addHandshakeKeys(Transaction txn, ContactId c,
HandshakeKeys k) throws DbException;
/**
* Stores the given handshake keys for the given pending contact and
* returns a key set ID.
*/
HandshakeKeySetId addHandshakeKeys(Transaction txn, PendingContactId p,
HandshakeKeys k) throws DbException;
/** /**
* Stores an identity. * Stores an identity.
*/ */
@@ -139,14 +154,7 @@ public interface DatabaseComponent {
* Stores the given transport keys for the given contact and returns a * Stores the given transport keys for the given contact and returns a
* key set ID. * key set ID.
*/ */
KeySetId addTransportKeys(Transaction txn, ContactId c, TransportKeys k) TransportKeySetId addTransportKeys(Transaction txn, ContactId c,
throws DbException;
/**
* Stores the given transport keys for the given pending contact and
* returns a key set ID.
*/
KeySetId addTransportKeys(Transaction txn, PendingContactId p,
TransportKeys k) throws DbException; TransportKeys k) throws DbException;
/** /**
@@ -266,7 +274,7 @@ public interface DatabaseComponent {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<ContactId> getContacts(Transaction txn, AuthorId local) Collection<ContactId> getContacts(Transaction txn, AuthorId a)
throws DbException; throws DbException;
/** /**
@@ -300,6 +308,14 @@ public interface DatabaseComponent {
Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g) Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g)
throws DbException; throws DbException;
/**
* Returns all handshake keys for the given transport.
* <p/>
* Read-only.
*/
Collection<HandshakeKeySet> getHandshakeKeys(Transaction txn, TransportId t)
throws DbException;
/** /**
* Returns the identity for the local pseudonym with the given ID. * Returns the identity for the local pseudonym with the given ID.
* <p/> * <p/>
@@ -471,11 +487,17 @@ public interface DatabaseComponent {
Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t) Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t)
throws DbException; throws DbException;
/**
* Increments the outgoing stream counter for the given handshake keys.
*/
void incrementStreamCounter(Transaction txn, TransportId t,
HandshakeKeySetId k) throws DbException;
/** /**
* Increments the outgoing stream counter for the given transport keys. * Increments the outgoing stream counter for the given transport keys.
*/ */
void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k) void incrementStreamCounter(Transaction txn, TransportId t,
throws DbException; TransportKeySetId k) throws DbException;
/** /**
* Merges the given metadata with the existing metadata for the given * Merges the given metadata with the existing metadata for the given
@@ -530,6 +552,12 @@ public interface DatabaseComponent {
*/ */
void removeGroup(Transaction txn, Group g) throws DbException; void removeGroup(Transaction txn, Group g) throws DbException;
/**
* Removes the given handshake keys from the database.
*/
void removeHandshakeKeys(Transaction txn, TransportId t,
HandshakeKeySetId k) throws DbException;
/** /**
* Removes an identity (and all associated state) from the database. * Removes an identity (and all associated state) from the database.
*/ */
@@ -554,8 +582,8 @@ public interface DatabaseComponent {
/** /**
* Removes the given transport keys from the database. * Removes the given transport keys from the database.
*/ */
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k) void removeTransportKeys(Transaction txn, TransportId t,
throws DbException; TransportKeySetId k) throws DbException;
/** /**
* Marks the given contact as verified. * Marks the given contact as verified.
@@ -594,20 +622,35 @@ public interface DatabaseComponent {
/** /**
* Sets the handshake key pair for the identity with the given ID. * Sets the handshake key pair for the identity with the given ID.
*/ */
void setHandshakeKeyPair(Transaction txn, AuthorId local, void setHandshakeKeyPair(Transaction txn, AuthorId local, byte[] publicKey,
PublicKey publicKey, PrivateKey privateKey) throws DbException; byte[] privateKey) throws DbException;
/** /**
* Sets the reordering window for the given transport keys in the given * Sets the reordering window for the given transport key set in the given
* time period. * time period.
*/ */
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t, void setReorderingWindow(Transaction txn, TransportKeySetId k,
long timePeriod, long base, byte[] bitmap) throws DbException; TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException;
/**
* Sets the reordering window for the given handshake key set in the given
* time period.
*/
void setReorderingWindow(Transaction txn, HandshakeKeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k) void setTransportKeysActive(Transaction txn, TransportId t,
TransportKeySetId k) throws DbException;
/**
* Stores the given handshake keys, deleting any keys they have replaced.
*/
void updateHandshakeKeys(Transaction txn, Collection<HandshakeKeySet> keys)
throws DbException; throws DbException;
/** /**

View File

@@ -1,14 +1,13 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.Nameable; import org.briarproject.bramble.api.Nameable;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.util.StringUtils.toUtf8; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
/** /**
* A pseudonym for a user. * A pseudonym for a user.
@@ -25,14 +24,14 @@ public class Author implements Nameable {
private final AuthorId id; private final AuthorId id;
private final int formatVersion; private final int formatVersion;
private final String name; private final String name;
private final PublicKey publicKey; private final byte[] publicKey;
public Author(AuthorId id, int formatVersion, String name, public Author(AuthorId id, int formatVersion, String name,
PublicKey publicKey) { byte[] publicKey) {
int nameLength = toUtf8(name).length; int nameLength = StringUtils.toUtf8(name).length;
if (nameLength == 0 || nameLength > MAX_AUTHOR_NAME_LENGTH) if (nameLength == 0 || nameLength > MAX_AUTHOR_NAME_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if (!publicKey.getKeyType().equals(KEY_TYPE_SIGNATURE)) if (publicKey.length == 0 || publicKey.length > MAX_PUBLIC_KEY_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
this.id = id; this.id = id;
this.formatVersion = formatVersion; this.formatVersion = formatVersion;
@@ -64,7 +63,7 @@ public class Author implements Nameable {
/** /**
* Returns the public key used to verify the pseudonym's signatures. * Returns the public key used to verify the pseudonym's signatures.
*/ */
public PublicKey getPublicKey() { public byte[] getPublicKey() {
return publicKey; return publicKey;
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
@@ -10,12 +9,12 @@ public interface AuthorFactory {
* Creates an author with the current format version and the given name and * Creates an author with the current format version and the given name and
* public key. * public key.
*/ */
Author createAuthor(String name, PublicKey publicKey); Author createAuthor(String name, byte[] publicKey);
/** /**
* Creates an author with the given format version, name and public key. * Creates an author with the given format version, name and public key.
*/ */
Author createAuthor(int formatVersion, String name, PublicKey publicKey); Author createAuthor(int formatVersion, String name, byte[] publicKey);
/** /**
* Creates a local author with the current format version and the given * Creates a local author with the current format version and the given

View File

@@ -1,13 +1,13 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Arrays;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT; import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -15,24 +15,15 @@ public class Identity {
private final LocalAuthor localAuthor; private final LocalAuthor localAuthor;
@Nullable @Nullable
private final PublicKey handshakePublicKey; private final byte[] handshakePublicKey, handshakePrivateKey;
@Nullable
private final PrivateKey handshakePrivateKey;
private final long created; private final long created;
public Identity(LocalAuthor localAuthor, public Identity(LocalAuthor localAuthor,
@Nullable PublicKey handshakePublicKey, @Nullable byte[] handshakePublicKey,
@Nullable PrivateKey handshakePrivateKey, long created) { @Nullable byte[] handshakePrivateKey, long created) {
if (handshakePublicKey != null) { if (handshakePublicKey != null) {
if (handshakePrivateKey == null) int keyLength = handshakePublicKey.length;
throw new IllegalArgumentException(); if (keyLength == 0 || keyLength > MAX_AGREEMENT_PUBLIC_KEY_BYTES)
if (!handshakePublicKey.getKeyType().equals(KEY_TYPE_AGREEMENT))
throw new IllegalArgumentException();
}
if (handshakePrivateKey != null) {
if (handshakePublicKey == null)
throw new IllegalArgumentException();
if (!handshakePrivateKey.getKeyType().equals(KEY_TYPE_AGREEMENT))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
this.localAuthor = localAuthor; this.localAuthor = localAuthor;
@@ -66,7 +57,7 @@ public class Identity {
* Returns the public key used for handshaking, or null if no key exists. * Returns the public key used for handshaking, or null if no key exists.
*/ */
@Nullable @Nullable
public PublicKey getHandshakePublicKey() { public byte[] getHandshakePublicKey() {
return handshakePublicKey; return handshakePublicKey;
} }
@@ -74,7 +65,7 @@ public class Identity {
* Returns the private key used for handshaking, or null if no key exists. * Returns the private key used for handshaking, or null if no key exists.
*/ */
@Nullable @Nullable
public PrivateKey getHandshakePrivateKey() { public byte[] getHandshakePrivateKey() {
return handshakePrivateKey; return handshakePrivateKey;
} }
@@ -85,4 +76,21 @@ public class Identity {
public long getTimeCreated() { public long getTimeCreated() {
return created; return created;
} }
@Override
public int hashCode() {
return localAuthor.getId().hashCode();
}
@Override
public boolean equals(Object o) {
if (o instanceof Identity) {
Identity i = (Identity) o;
return created == i.created &&
localAuthor.equals(i.localAuthor) &&
Arrays.equals(handshakePublicKey, i.handshakePublicKey) &&
Arrays.equals(handshakePrivateKey, i.handshakePrivateKey);
}
return false;
}
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.CryptoExecutor; import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
@@ -42,6 +41,9 @@ public interface IdentityManager {
* Returns the cached handshake keys or loads them from the database. * Returns the cached handshake keys or loads them from the database.
* <p/> * <p/>
* Read-only. * Read-only.
*
* @return A two-element array containing the public key in the first
* element and the private key in the second
*/ */
KeyPair getHandshakeKeys(Transaction txn) throws DbException; byte[][] getHandshakeKeys(Transaction txn) throws DbException;
} }

View File

@@ -1,13 +1,9 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
/** /**
* A pseudonym for the local user. * A pseudonym for the local user.
*/ */
@@ -15,20 +11,18 @@ import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNA
@NotNullByDefault @NotNullByDefault
public class LocalAuthor extends Author { public class LocalAuthor extends Author {
private final PrivateKey privateKey; private final byte[] privateKey;
public LocalAuthor(AuthorId id, int formatVersion, String name, public LocalAuthor(AuthorId id, int formatVersion, String name,
PublicKey publicKey, PrivateKey privateKey) { byte[] publicKey, byte[] privateKey) {
super(id, formatVersion, name, publicKey); super(id, formatVersion, name, publicKey);
if (!privateKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
throw new IllegalArgumentException();
this.privateKey = privateKey; this.privateKey = privateKey;
} }
/** /**
* Returns the private key used to generate the pseudonym's signatures. * Returns the private key used to generate the pseudonym's signatures.
*/ */
public PrivateKey getPrivateKey() { public byte[] getPrivateKey() {
return privateKey; return privateKey;
} }
} }

View File

@@ -6,20 +6,10 @@ import javax.annotation.Nullable;
public class NullSafety { public class NullSafety {
/** /**
* Stand-in for {@code Objects.requireNonNull()}. * Stand-in for `Objects.requireNonNull()`.
*/ */
public static <T> T requireNonNull(@Nullable T t) { public static <T> T requireNonNull(@Nullable T t) {
if (t == null) throw new NullPointerException(); if (t == null) throw new NullPointerException();
return t; return t;
} }
/**
* Checks that exactly one of the arguments is null.
*
* @throws AssertionError If both or neither of the arguments are null
*/
public static void requireExactlyOneNull(@Nullable Object a,
@Nullable Object b) {
if ((a == null) == (b == null)) throw new AssertionError();
}
} }

View File

@@ -0,0 +1,57 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* Abstract superclass for {@link TransportKeys} and {@link HandshakeKeys}.
*/
@Immutable
@NotNullByDefault
public abstract class AbstractTransportKeys {
private final TransportId transportId;
private final IncomingKeys inPrev, inCurr, inNext;
private final OutgoingKeys outCurr;
AbstractTransportKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
if (inPrev.getTimePeriod() != outCurr.getTimePeriod() - 1)
throw new IllegalArgumentException();
if (inCurr.getTimePeriod() != outCurr.getTimePeriod())
throw new IllegalArgumentException();
if (inNext.getTimePeriod() != outCurr.getTimePeriod() + 1)
throw new IllegalArgumentException();
this.transportId = transportId;
this.inPrev = inPrev;
this.inCurr = inCurr;
this.inNext = inNext;
this.outCurr = outCurr;
}
public TransportId getTransportId() {
return transportId;
}
public IncomingKeys getPreviousIncomingKeys() {
return inPrev;
}
public IncomingKeys getCurrentIncomingKeys() {
return inCurr;
}
public IncomingKeys getNextIncomingKeys() {
return inNext;
}
public OutgoingKeys getCurrentOutgoingKeys() {
return outCurr;
}
public long getTimePeriod() {
return outCurr.getTimePeriod();
}
}

View File

@@ -0,0 +1,70 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A set of keys for handshaking with a given contact or pending contact over a
* given transport. Unlike a {@link TransportKeySet} these keys do not provide
* forward secrecy.
*/
@Immutable
@NotNullByDefault
public class HandshakeKeySet {
private final HandshakeKeySetId keySetId;
@Nullable
private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final HandshakeKeys keys;
public HandshakeKeySet(HandshakeKeySetId keySetId, ContactId contactId,
HandshakeKeys keys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.keys = keys;
pendingContactId = null;
}
public HandshakeKeySet(HandshakeKeySetId keySetId,
PendingContactId pendingContactId, HandshakeKeys keys) {
this.keySetId = keySetId;
this.pendingContactId = pendingContactId;
this.keys = keys;
contactId = null;
}
public HandshakeKeySetId getKeySetId() {
return keySetId;
}
@Nullable
public ContactId getContactId() {
return contactId;
}
@Nullable
public PendingContactId getPendingContactId() {
return pendingContactId;
}
public HandshakeKeys getKeys() {
return keys;
}
@Override
public int hashCode() {
return keySetId.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof HandshakeKeySet &&
keySetId.equals(((HandshakeKeySet) o).keySetId);
}
}

View File

@@ -5,16 +5,17 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* Type-safe wrapper for an integer that uniquely identifies a set of * Type-safe wrapper for an integer that uniquely identifies a
* {@link TransportKeySet transport keys} within the scope of the local device. * {@link HandshakeKeySet set of handshake keys} within the scope of the local
* device.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class KeySetId { public class HandshakeKeySetId {
private final int id; private final int id;
public KeySetId(int id) { public HandshakeKeySetId(int id) {
this.id = id; this.id = id;
} }
@@ -29,6 +30,7 @@ public class KeySetId {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return o instanceof KeySetId && id == ((KeySetId) o).id; return o instanceof HandshakeKeySetId &&
id == ((HandshakeKeySetId) o).id;
} }
} }

View File

@@ -0,0 +1,36 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* Keys for handshaking with a given contact or pending contact over a given
* transport. Unlike {@link TransportKeys} these keys do not provide forward
* secrecy.
*/
@Immutable
@NotNullByDefault
public class HandshakeKeys extends AbstractTransportKeys {
private final SecretKey rootKey;
private final boolean alice;
public HandshakeKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr,
SecretKey rootKey, boolean alice) {
super(transportId, inPrev, inCurr, inNext, outCurr);
this.rootKey = rootKey;
this.alice = alice;
}
public SecretKey getRootKey() {
return rootKey;
}
public boolean isAlice() {
return alice;
}
}

View File

@@ -8,8 +8,8 @@ import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE; import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
/** /**
* Contains transport keys for receiving streams from a given contact or * Contains transport keys for receiving streams from a given contact over a
* pending contact over a given transport in a given time period. * given transport in a given time period.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
@@ -19,51 +18,23 @@ public interface KeyManager {
/** /**
* Informs the key manager that a new contact has been added. Derives and * Informs the key manager that a new contact has been added. Derives and
* stores a set of rotation mode transport keys for communicating with the * stores a set of transport keys for communicating with the contact over
* contact over each transport and returns the key set IDs. * each transport and returns the key set IDs.
* <p/> * <p/>
* {@link StreamContext StreamContexts} for the contact can be created * {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned. * after this method has returned.
* *
* @param alice True if the local party is Alice * @param alice true if the local party is Alice
* @param active Whether the derived keys can be used for outgoing streams * @param active whether the derived keys can be used for outgoing streams
*/ */
Map<TransportId, KeySetId> addContactWithRotationKeys(Transaction txn, Map<TransportId, TransportKeySetId> addContact(Transaction txn, ContactId c,
ContactId c, SecretKey rootKey, long timestamp, boolean alice, SecretKey rootKey, long timestamp, boolean alice, boolean active)
boolean active) throws DbException;
/**
* Informs the key manager that a new contact has been added. Derives and
* stores a set of handshake mode transport keys for communicating with the
* contact over each transport and returns the key set IDs.
* <p/>
* {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned.
*
* @param alice True if the local party is Alice
*/
Map<TransportId, KeySetId> addContactWithHandshakeKeys(Transaction txn,
ContactId c, SecretKey rootKey, boolean alice) throws DbException;
/**
* Informs the key manager that a new pending contact has been added.
* Derives and stores a set of handshake mode transport keys for
* communicating with the pending contact over each transport and returns
* the key set IDs.
* <p/>
* {@link StreamContext StreamContexts} for the pending contact can be
* created after this method has returned.
*
* @param alice True if the local party is Alice
*/
Map<TransportId, KeySetId> addPendingContact(Transaction txn,
PendingContactId p, SecretKey rootKey, boolean alice)
throws DbException; throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys) void activateKeys(Transaction txn, Map<TransportId, TransportKeySetId> keys)
throws DbException; throws DbException;
/** /**
@@ -72,28 +43,15 @@ public interface KeyManager {
*/ */
boolean canSendOutgoingStreams(ContactId c, TransportId t); boolean canSendOutgoingStreams(ContactId c, TransportId t);
/**
* Returns true if we have keys that can be used for outgoing streams to
* the given pending contact over the given transport.
*/
boolean canSendOutgoingStreams(PendingContactId p, TransportId t);
/** /**
* Returns a {@link StreamContext} for sending a stream to the given * Returns a {@link StreamContext} for sending a stream to the given
* contact over the given transport, or null if an error occurs. * contact over the given transport, or null if an error occurs or the
* contact does not support the transport.
*/ */
@Nullable @Nullable
StreamContext getStreamContext(ContactId c, TransportId t) StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException; throws DbException;
/**
* Returns a {@link StreamContext} for sending a stream to the given
* pending contact over the given transport, or null if an error occurs.
*/
@Nullable
StreamContext getStreamContext(PendingContactId p, TransportId t)
throws DbException;
/** /**
* Looks up the given tag and returns a {@link StreamContext} for reading * Looks up the given tag and returns a {@link StreamContext} for reading
* from the corresponding stream, or null if an error occurs or the tag was * from the corresponding stream, or null if an error occurs or the tag was

View File

@@ -6,8 +6,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* Contains transport keys for sending streams to a given contact or pending * Contains transport keys for sending streams to a given contact over a given
* contact over a given transport in a given time period. * transport in a given time period.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault

View File

@@ -1,53 +1,34 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class StreamContext { public class StreamContext {
@Nullable
private final ContactId contactId; private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final TransportId transportId; private final TransportId transportId;
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long streamNumber; private final long streamNumber;
private final boolean handshakeMode;
public StreamContext(@Nullable ContactId contactId, public StreamContext(ContactId contactId, TransportId transportId,
@Nullable PendingContactId pendingContactId, SecretKey tagKey, SecretKey headerKey, long streamNumber) {
TransportId transportId, SecretKey tagKey, SecretKey headerKey,
long streamNumber, boolean handshakeMode) {
requireExactlyOneNull(contactId, pendingContactId);
this.contactId = contactId; this.contactId = contactId;
this.pendingContactId = pendingContactId;
this.transportId = transportId; this.transportId = transportId;
this.tagKey = tagKey; this.tagKey = tagKey;
this.headerKey = headerKey; this.headerKey = headerKey;
this.streamNumber = streamNumber; this.streamNumber = streamNumber;
this.handshakeMode = handshakeMode;
} }
@Nullable
public ContactId getContactId() { public ContactId getContactId() {
return contactId; return contactId;
} }
@Nullable
public PendingContactId getPendingContactId() {
return pendingContactId;
}
public TransportId getTransportId() { public TransportId getTransportId() {
return transportId; return transportId;
} }
@@ -63,8 +44,4 @@ public class StreamContext {
public long getStreamNumber() { public long getStreamNumber() {
return streamNumber; return streamNumber;
} }
public boolean isHandshakeMode() {
return handshakeMode;
}
} }

View File

@@ -1,52 +1,37 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
/** /**
* A set of keys for communicating with a given contact or pending contact * A set of keys for communicating with a given contact over a given transport.
* over a given transport. * Unlike a {@link HandshakeKeySet} these keys provide forward secrecy.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class TransportKeySet { public class TransportKeySet {
private final KeySetId keySetId; private final TransportKeySetId keySetId;
@Nullable
private final ContactId contactId; private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final TransportKeys keys; private final TransportKeys keys;
public TransportKeySet(KeySetId keySetId, @Nullable ContactId contactId, public TransportKeySet(TransportKeySetId keySetId, ContactId contactId,
@Nullable PendingContactId pendingContactId, TransportKeys keys) { TransportKeys keys) {
requireExactlyOneNull(contactId, pendingContactId);
this.keySetId = keySetId; this.keySetId = keySetId;
this.contactId = contactId; this.contactId = contactId;
this.pendingContactId = pendingContactId;
this.keys = keys; this.keys = keys;
} }
public KeySetId getKeySetId() { public TransportKeySetId getKeySetId() {
return keySetId; return keySetId;
} }
@Nullable
public ContactId getContactId() { public ContactId getContactId() {
return contactId; return contactId;
} }
@Nullable
public PendingContactId getPendingContactId() {
return pendingContactId;
}
public TransportKeys getKeys() { public TransportKeys getKeys() {
return keys; return keys;
} }

View File

@@ -0,0 +1,38 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* Type-safe wrapper for an integer that uniquely identifies a
* {@link TransportKeySet set of transport keys} within the scope of the local
* device.
* <p/>
* Key sets created on a given device must have increasing identifiers.
*/
@Immutable
@NotNullByDefault
public class TransportKeySetId {
private final int id;
public TransportKeySetId(int id) {
this.id = id;
}
public int getInt() {
return id;
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object o) {
return o instanceof TransportKeySetId &&
id == ((TransportKeySetId) o).id;
}
}

View File

@@ -1,108 +1,20 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* Keys for communicating with a given contact or pending contact over a given * Keys for communicating with a given contact over a given transport. Unlike
* transport. * {@link HandshakeKeys} these keys provide forward secrecy.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class TransportKeys { public class TransportKeys extends AbstractTransportKeys {
private final TransportId transportId;
private final IncomingKeys inPrev, inCurr, inNext;
private final OutgoingKeys outCurr;
@Nullable
private final SecretKey rootKey;
private final boolean alice;
/**
* Constructor for rotation mode.
*/
public TransportKeys(TransportId transportId, IncomingKeys inPrev, public TransportKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) { IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
this(transportId, inPrev, inCurr, inNext, outCurr, null, false); super(transportId, inPrev, inCurr, inNext, outCurr);
}
/**
* Constructor for handshake mode.
*/
public TransportKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr,
@Nullable SecretKey rootKey, boolean alice) {
if (inPrev.getTimePeriod() != outCurr.getTimePeriod() - 1)
throw new IllegalArgumentException();
if (inCurr.getTimePeriod() != outCurr.getTimePeriod())
throw new IllegalArgumentException();
if (inNext.getTimePeriod() != outCurr.getTimePeriod() + 1)
throw new IllegalArgumentException();
this.transportId = transportId;
this.inPrev = inPrev;
this.inCurr = inCurr;
this.inNext = inNext;
this.outCurr = outCurr;
this.rootKey = rootKey;
this.alice = alice;
}
public TransportId getTransportId() {
return transportId;
}
public IncomingKeys getPreviousIncomingKeys() {
return inPrev;
}
public IncomingKeys getCurrentIncomingKeys() {
return inCurr;
}
public IncomingKeys getNextIncomingKeys() {
return inNext;
}
public OutgoingKeys getCurrentOutgoingKeys() {
return outCurr;
}
public long getTimePeriod() {
return outCurr.getTimePeriod();
}
/**
* Returns true if these keys are for use in handshake mode or false if
* they're for use in rotation mode.
*/
public boolean isHandshakeMode() {
return rootKey != null;
}
/**
* If these keys are for use in handshake mode, returns the root key.
*
* @throws UnsupportedOperationException If these keys are for use in
* rotation mode
*/
public SecretKey getRootKey() {
if (rootKey == null) throw new UnsupportedOperationException();
return rootKey;
}
/**
* If these keys are for use in handshake mode, returns true if the local
* party is Alice.
*
* @throws UnsupportedOperationException If these keys are for use in
* rotation mode
*/
public boolean isAlice() {
if (rootKey == null) throw new UnsupportedOperationException();
return alice;
} }
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.util; package org.briarproject.bramble.util;
import java.io.File;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -20,7 +19,6 @@ public class LogUtils {
/** /**
* Logs the duration of a task. * Logs the duration of a task.
*
* @param logger the logger to use * @param logger the logger to use
* @param task a description of the task * @param task a description of the task
* @param start the start time of the task, as returned by {@link #now()} * @param start the start time of the task, as returned by {@link #now()}
@@ -35,26 +33,4 @@ public class LogUtils {
public static void logException(Logger logger, Level level, Throwable t) { public static void logException(Logger logger, Level level, Throwable t) {
if (logger.isLoggable(level)) logger.log(level, t.toString(), t); if (logger.isLoggable(level)) logger.log(level, t.toString(), t);
} }
public static void logFileOrDir(Logger logger, Level level, File f) {
if (logger.isLoggable(level)) {
if (f.isFile()) {
logWithType(logger, level, f, "F");
} else if (f.isDirectory()) {
logWithType(logger, level, f, "D");
File[] children = f.listFiles();
if (children != null) {
for (File child : children)
logFileOrDir(logger, level, child);
}
} else if (f.exists()) {
logWithType(logger, level, f, "?");
}
}
}
private static void logWithType(Logger logger, Level level, File f,
String type) {
logger.log(level, type + " " + f.getAbsolutePath() + " " + f.length());
}
} }

View File

@@ -5,14 +5,7 @@ import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.Identity;
@@ -37,10 +30,11 @@ import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES; import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION; import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH; import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH; import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
@@ -107,28 +101,10 @@ public class TestUtils {
return new SecretKey(getRandomBytes(SecretKey.LENGTH)); return new SecretKey(getRandomBytes(SecretKey.LENGTH));
} }
public static PublicKey getSignaturePublicKey() {
byte[] key = getRandomBytes(MAX_SIGNATURE_PUBLIC_KEY_BYTES);
return new SignaturePublicKey(key);
}
public static PrivateKey getSignaturePrivateKey() {
return new SignaturePrivateKey(getRandomBytes(123));
}
public static PublicKey getAgreementPublicKey() {
byte[] key = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
return new AgreementPublicKey(key);
}
public static PrivateKey getAgreementPrivateKey() {
return new AgreementPrivateKey(getRandomBytes(123));
}
public static Identity getIdentity() { public static Identity getIdentity() {
LocalAuthor localAuthor = getLocalAuthor(); LocalAuthor localAuthor = getLocalAuthor();
PublicKey handshakePub = getAgreementPublicKey(); byte[] handshakePub = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
PrivateKey handshakePriv = getAgreementPrivateKey(); byte[] handshakePriv = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
return new Identity(localAuthor, handshakePub, handshakePriv, return new Identity(localAuthor, handshakePub, handshakePriv,
timestamp); timestamp);
} }
@@ -137,8 +113,8 @@ public class TestUtils {
AuthorId id = new AuthorId(getRandomId()); AuthorId id = new AuthorId(getRandomId());
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH); int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
String name = getRandomString(nameLength); String name = getRandomString(nameLength);
PublicKey publicKey = getSignaturePublicKey(); byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
PrivateKey privateKey = getSignaturePrivateKey(); byte[] privateKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey); return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
} }
@@ -146,7 +122,7 @@ public class TestUtils {
AuthorId id = new AuthorId(getRandomId()); AuthorId id = new AuthorId(getRandomId());
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH); int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
String name = getRandomString(nameLength); String name = getRandomString(nameLength);
PublicKey publicKey = getSignaturePublicKey(); byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
return new Author(id, FORMAT_VERSION, name, publicKey); return new Author(id, FORMAT_VERSION, name, publicKey);
} }
@@ -179,12 +155,10 @@ public class TestUtils {
public static PendingContact getPendingContact(int nameLength) { public static PendingContact getPendingContact(int nameLength) {
PendingContactId id = new PendingContactId(getRandomId()); PendingContactId id = new PendingContactId(getRandomId());
PublicKey publicKey = getAgreementPublicKey(); byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
String alias = getRandomString(nameLength); String alias = getRandomString(nameLength);
int stateIndex = return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
random.nextInt(PendingContactState.values().length - 1); timestamp);
PendingContactState state = PendingContactState.values()[stateIndex];
return new PendingContact(id, publicKey, alias, state, timestamp);
} }
public static ContactId getContactId() { public static ContactId getContactId() {
@@ -205,7 +179,7 @@ public class TestUtils {
boolean verified) { boolean verified) {
return new Contact(c, remote, local, return new Contact(c, remote, local,
getRandomString(MAX_AUTHOR_NAME_LENGTH), getRandomString(MAX_AUTHOR_NAME_LENGTH),
getAgreementPublicKey(), verified); getRandomBytes(MAX_PUBLIC_KEY_LENGTH), verified);
} }
public static double getMedian(Collection<? extends Number> samples) { public static double getMedian(Collection<? extends Number> samples) {

View File

@@ -3,9 +3,6 @@ package org.briarproject.bramble.client;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader; import org.briarproject.bramble.api.data.BdfReader;
@@ -308,15 +305,14 @@ class ClientHelperImpl implements ClientHelper {
} }
@Override @Override
public byte[] sign(String label, BdfList toSign, PrivateKey privateKey) public byte[] sign(String label, BdfList toSign, byte[] privateKey)
throws FormatException, GeneralSecurityException { throws FormatException, GeneralSecurityException {
return crypto.sign(label, toByteArray(toSign), privateKey); return crypto.sign(label, toByteArray(toSign), privateKey);
} }
@Override @Override
public void verifySignature(byte[] signature, String label, BdfList signed, public void verifySignature(byte[] signature, String label, BdfList signed,
PublicKey publicKey) byte[] publicKey) throws FormatException, GeneralSecurityException {
throws FormatException, GeneralSecurityException {
if (!crypto.verifySignature(signature, label, toByteArray(signed), if (!crypto.verifySignature(signature, label, toByteArray(signed),
publicKey)) { publicKey)) {
throw new GeneralSecurityException("Invalid signature"); throw new GeneralSecurityException("Invalid signature");
@@ -331,29 +327,11 @@ class ClientHelperImpl implements ClientHelper {
if (formatVersion != FORMAT_VERSION) throw new FormatException(); if (formatVersion != FORMAT_VERSION) throw new FormatException();
String name = author.getString(1); String name = author.getString(1);
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH); checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
byte[] publicKeyBytes = author.getRaw(2); byte[] publicKey = author.getRaw(2);
checkLength(publicKeyBytes, 1, MAX_PUBLIC_KEY_LENGTH); checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH);
KeyParser parser = crypto.getSignatureKeyParser();
PublicKey publicKey;
try {
publicKey = parser.parsePublicKey(publicKeyBytes);
} catch (GeneralSecurityException e) {
throw new FormatException();
}
return authorFactory.createAuthor(formatVersion, name, publicKey); return authorFactory.createAuthor(formatVersion, name, publicKey);
} }
@Override
public PublicKey parseAndValidateAgreementPublicKey(byte[] publicKeyBytes)
throws FormatException {
KeyParser parser = crypto.getAgreementKeyParser();
try {
return parser.parsePublicKey(publicKeyBytes);
} catch (GeneralSecurityException e) {
throw new FormatException();
}
}
@Override @Override
public TransportProperties parseAndValidateTransportProperties( public TransportProperties parseAndValidateTransportProperties(
BdfDictionary properties) throws FormatException { BdfDictionary properties) throws FormatException {

View File

@@ -70,8 +70,7 @@ class ContactManagerImpl implements ContactManager {
SecretKey rootKey, long timestamp, boolean alice, boolean verified, SecretKey rootKey, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException { boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified); ContactId c = db.addContact(txn, remote, local, verified);
keyManager.addContactWithRotationKeys(txn, c, rootKey, timestamp, keyManager.addContact(txn, c, rootKey, timestamp, alice, active);
alice, active);
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact); for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c; return c;

View File

@@ -39,8 +39,8 @@ class PendingContactFactoryImpl implements PendingContactFactory {
PublicKey publicKey = parseHandshakeLink(link); PublicKey publicKey = parseHandshakeLink(link);
PendingContactId id = getPendingContactId(publicKey); PendingContactId id = getPendingContactId(publicKey);
long timestamp = clock.currentTimeMillis(); long timestamp = clock.currentTimeMillis();
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION, return new PendingContact(id, publicKey.getEncoded(), alias,
timestamp); WAITING_FOR_CONNECTION, timestamp);
} }
private PublicKey parseHandshakeLink(String link) throws FormatException { private PublicKey parseHandshakeLink(String link) throws FormatException {

View File

@@ -4,16 +4,12 @@ import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey; import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.KeyPairGenerator; import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.util.ByteUtils; import org.briarproject.bramble.util.ByteUtils;
@@ -35,8 +31,6 @@ import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@@ -86,8 +80,8 @@ class CryptoComponentImpl implements CryptoComponent {
signatureKeyPairGenerator = new KeyPairGenerator(); signatureKeyPairGenerator = new KeyPairGenerator();
signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS, signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS,
secureRandom); secureRandom);
agreementKeyParser = new AgreementKeyParser(); agreementKeyParser = new Curve25519KeyParser();
signatureKeyParser = new SignatureKeyParser(); signatureKeyParser = new EdKeyParser();
messageEncrypter = new MessageEncrypter(secureRandom); messageEncrypter = new MessageEncrypter(secureRandom);
} }
@@ -131,9 +125,9 @@ class CryptoComponentImpl implements CryptoComponent {
// Package access for testing // Package access for testing
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub) byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
throws GeneralSecurityException { throws GeneralSecurityException {
if (!priv.getKeyType().equals(KEY_TYPE_AGREEMENT)) if (!(priv instanceof Curve25519PrivateKey))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if (!pub.getKeyType().equals(KEY_TYPE_AGREEMENT)) if (!(pub instanceof Curve25519PublicKey))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
long start = now(); long start = now();
byte[] secret = curve25519.calculateAgreement(pub.getEncoded(), byte[] secret = curve25519.calculateAgreement(pub.getEncoded(),
@@ -149,8 +143,8 @@ class CryptoComponentImpl implements CryptoComponent {
@Override @Override
public KeyPair generateAgreementKeyPair() { public KeyPair generateAgreementKeyPair() {
Curve25519KeyPair keyPair = curve25519.generateKeyPair(); Curve25519KeyPair keyPair = curve25519.generateKeyPair();
PublicKey pub = new AgreementPublicKey(keyPair.getPublicKey()); PublicKey pub = new Curve25519PublicKey(keyPair.getPublicKey());
PrivateKey priv = new AgreementPrivateKey(keyPair.getPrivateKey()); PrivateKey priv = new Curve25519PrivateKey(keyPair.getPrivateKey());
return new KeyPair(pub, priv); return new KeyPair(pub, priv);
} }
@@ -164,9 +158,9 @@ class CryptoComponentImpl implements CryptoComponent {
java.security.KeyPair keyPair = java.security.KeyPair keyPair =
signatureKeyPairGenerator.generateKeyPair(); signatureKeyPairGenerator.generateKeyPair();
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic(); EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
PublicKey publicKey = new SignaturePublicKey(edPublicKey.getAbyte()); PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate(); EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
PrivateKey privateKey = new SignaturePrivateKey(edPrivateKey.getSeed()); PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed());
return new KeyPair(publicKey, privateKey); return new KeyPair(publicKey, privateKey);
} }
@@ -201,22 +195,21 @@ class CryptoComponentImpl implements CryptoComponent {
} }
@Override @Override
public byte[] sign(String label, byte[] toSign, PrivateKey privateKey) public byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException { throws GeneralSecurityException {
PrivateKey key = signatureKeyParser.parsePrivateKey(privateKey);
Signature sig = new EdSignature(); Signature sig = new EdSignature();
sig.initSign(privateKey); sig.initSign(key);
updateSignature(sig, label, toSign); updateSignature(sig, label, toSign);
return sig.sign(); return sig.sign();
} }
@Override @Override
public boolean verifySignature(byte[] signature, String label, public boolean verifySignature(byte[] signature, String label,
byte[] signed, PublicKey publicKey) byte[] signed, byte[] publicKey) throws GeneralSecurityException {
throws GeneralSecurityException { PublicKey key = signatureKeyParser.parsePublicKey(publicKey);
if (!publicKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
throw new IllegalArgumentException();
Signature sig = new EdSignature(); Signature sig = new EdSignature();
sig.initVerify(publicKey); sig.initVerify(key);
updateSignature(sig, label, signed); updateSignature(sig, label, signed);
return sig.verify(signature); return sig.verify(signature);
} }

View File

@@ -1,7 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
@@ -9,24 +7,21 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault @NotNullByDefault
class AgreementKeyParser implements KeyParser { class Curve25519KeyParser implements KeyParser {
@Override @Override
public PublicKey parsePublicKey(byte[] encodedKey) public PublicKey parsePublicKey(byte[] encodedKey)
throws GeneralSecurityException { throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException(); if (encodedKey.length != 32) throw new GeneralSecurityException();
return new AgreementPublicKey(encodedKey); return new Curve25519PublicKey(encodedKey);
} }
@Override @Override
public PrivateKey parsePrivateKey(byte[] encodedKey) public PrivateKey parsePrivateKey(byte[] encodedKey)
throws GeneralSecurityException { throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException(); if (encodedKey.length != 32) throw new GeneralSecurityException();
return new AgreementPrivateKey(clamp(encodedKey)); return new Curve25519PrivateKey(clamp(encodedKey));
} }
static byte[] clamp(byte[] b) { static byte[] clamp(byte[] b) {

View File

@@ -0,0 +1,18 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class Curve25519PrivateKey extends Bytes implements PrivateKey {
Curve25519PrivateKey(byte[] bytes) {
super(bytes);
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -0,0 +1,18 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class Curve25519PublicKey extends Bytes implements PublicKey {
Curve25519PublicKey(byte[] bytes) {
super(bytes);
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -3,29 +3,24 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault @NotNullByDefault
class SignatureKeyParser implements KeyParser { class EdKeyParser implements KeyParser {
@Override @Override
public PublicKey parsePublicKey(byte[] encodedKey) public PublicKey parsePublicKey(byte[] encodedKey)
throws GeneralSecurityException { throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException(); if (encodedKey.length != 32) throw new GeneralSecurityException();
return new SignaturePublicKey(encodedKey); return new EdPublicKey(encodedKey);
} }
@Override @Override
public PrivateKey parsePrivateKey(byte[] encodedKey) public PrivateKey parsePrivateKey(byte[] encodedKey)
throws GeneralSecurityException { throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException(); if (encodedKey.length != 32) throw new GeneralSecurityException();
return new SignaturePrivateKey(encodedKey); return new EdPrivateKey(encodedKey);
} }
} }

View File

@@ -0,0 +1,18 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class EdPrivateKey extends Bytes implements PrivateKey {
EdPrivateKey(byte[] bytes) {
super(bytes);
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -0,0 +1,18 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class EdPublicKey extends Bytes implements PublicKey {
EdPublicKey(byte[] bytes) {
super(bytes);
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -17,7 +17,6 @@ import java.security.NoSuchAlgorithmException;
import java.security.Provider; import java.security.Provider;
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM; import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
@NotNullByDefault @NotNullByDefault
class EdSignature implements Signature { class EdSignature implements Signature {
@@ -40,7 +39,7 @@ class EdSignature implements Signature {
@Override @Override
public void initSign(PrivateKey k) throws GeneralSecurityException { public void initSign(PrivateKey k) throws GeneralSecurityException {
if (!k.getKeyType().equals(KEY_TYPE_SIGNATURE)) if (!(k instanceof EdPrivateKey))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
EdDSAPrivateKey privateKey = new EdDSAPrivateKey( EdDSAPrivateKey privateKey = new EdDSAPrivateKey(
new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC)); new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC));
@@ -49,7 +48,7 @@ class EdSignature implements Signature {
@Override @Override
public void initVerify(PublicKey k) throws GeneralSecurityException { public void initVerify(PublicKey k) throws GeneralSecurityException {
if (!k.getKeyType().equals(KEY_TYPE_SIGNATURE)) if (!(k instanceof EdPublicKey))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
EdDSAPublicKey publicKey = new EdDSAPublicKey( EdDSAPublicKey publicKey = new EdDSAPublicKey(
new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC)); new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC));

View File

@@ -49,7 +49,6 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
public class MessageEncrypter { public class MessageEncrypter {
private static final String KEY_TYPE = "SEC1_brainpoolp512r1";
private static final ECDomainParameters PARAMETERS; private static final ECDomainParameters PARAMETERS;
private static final int MESSAGE_KEY_BITS = 512; private static final int MESSAGE_KEY_BITS = 512;
private static final int MAC_KEY_BITS = 256; private static final int MAC_KEY_BITS = 256;
@@ -70,7 +69,7 @@ public class MessageEncrypter {
MessageEncrypter(SecureRandom random) { MessageEncrypter(SecureRandom random) {
generator = new ECKeyPairGenerator(); generator = new ECKeyPairGenerator();
generator.init(new ECKeyGenerationParameters(PARAMETERS, random)); generator.init(new ECKeyGenerationParameters(PARAMETERS, random));
parser = new Sec1KeyParser(KEY_TYPE, PARAMETERS, MESSAGE_KEY_BITS); parser = new Sec1KeyParser(PARAMETERS, MESSAGE_KEY_BITS);
KeyEncoder encoder = new PublicKeyEncoder(); KeyEncoder encoder = new PublicKeyEncoder();
ephemeralGenerator = new EphemeralKeyPairGenerator(generator, encoder); ephemeralGenerator = new EphemeralKeyPairGenerator(generator, encoder);
ephemeralParser = new PublicKeyParser(PARAMETERS); ephemeralParser = new PublicKeyParser(PARAMETERS);
@@ -81,11 +80,11 @@ public class MessageEncrypter {
// Return a wrapper that uses the SEC 1 encoding // Return a wrapper that uses the SEC 1 encoding
ECPublicKeyParameters ecPublicKey = ECPublicKeyParameters ecPublicKey =
(ECPublicKeyParameters) keyPair.getPublic(); (ECPublicKeyParameters) keyPair.getPublic();
PublicKey publicKey = new Sec1PublicKey(KEY_TYPE, ecPublicKey); PublicKey publicKey = new Sec1PublicKey(ecPublicKey);
ECPrivateKeyParameters ecPrivateKey = ECPrivateKeyParameters ecPrivateKey =
(ECPrivateKeyParameters) keyPair.getPrivate(); (ECPrivateKeyParameters) keyPair.getPrivate();
PrivateKey privateKey = PrivateKey privateKey =
new Sec1PrivateKey(KEY_TYPE, ecPrivateKey, MESSAGE_KEY_BITS); new Sec1PrivateKey(ecPrivateKey, MESSAGE_KEY_BITS);
return new KeyPair(publicKey, privateKey); return new KeyPair(publicKey, privateKey);
} }

View File

@@ -31,13 +31,11 @@ class Sec1KeyParser implements KeyParser {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(Sec1KeyParser.class.getName()); Logger.getLogger(Sec1KeyParser.class.getName());
private final String keyType;
private final ECDomainParameters params; private final ECDomainParameters params;
private final BigInteger modulus; private final BigInteger modulus;
private final int keyBits, bytesPerInt, publicKeyBytes, privateKeyBytes; private final int keyBits, bytesPerInt, publicKeyBytes, privateKeyBytes;
Sec1KeyParser(String keyType, ECDomainParameters params, int keyBits) { Sec1KeyParser(ECDomainParameters params, int keyBits) {
this.keyType = keyType;
this.params = params; this.params = params;
this.keyBits = keyBits; this.keyBits = keyBits;
modulus = ((ECCurve.Fp) params.getCurve()).getQ(); modulus = ((ECCurve.Fp) params.getCurve()).getQ();
@@ -82,7 +80,7 @@ class Sec1KeyParser implements KeyParser {
throw new GeneralSecurityException(); throw new GeneralSecurityException();
// Construct a public key from the point (x, y) and the params // Construct a public key from the point (x, y) and the params
ECPublicKeyParameters k = new ECPublicKeyParameters(pub, params); ECPublicKeyParameters k = new ECPublicKeyParameters(pub, params);
PublicKey p = new Sec1PublicKey(keyType, k); PublicKey p = new Sec1PublicKey(k);
logDuration(LOG, "Parsing public key", start); logDuration(LOG, "Parsing public key", start);
return p; return p;
} }
@@ -99,7 +97,7 @@ class Sec1KeyParser implements KeyParser {
throw new GeneralSecurityException(); throw new GeneralSecurityException();
// Construct a private key from the private value and the params // Construct a private key from the private value and the params
ECPrivateKeyParameters k = new ECPrivateKeyParameters(d, params); ECPrivateKeyParameters k = new ECPrivateKeyParameters(d, params);
PrivateKey p = new Sec1PrivateKey(keyType, k, keyBits); PrivateKey p = new Sec1PrivateKey(k, keyBits);
logDuration(LOG, "Parsing private key", start); logDuration(LOG, "Parsing private key", start);
return p; return p;
} }

View File

@@ -10,21 +10,14 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
class Sec1PrivateKey implements PrivateKey { class Sec1PrivateKey implements PrivateKey {
private final String keyType;
private final ECPrivateKeyParameters key; private final ECPrivateKeyParameters key;
private final int bytesPerInt; private final int bytesPerInt;
Sec1PrivateKey(String keyType, ECPrivateKeyParameters key, int keyBits) { Sec1PrivateKey(ECPrivateKeyParameters key, int keyBits) {
this.keyType = keyType;
this.key = key; this.key = key;
bytesPerInt = (keyBits + 7) / 8; bytesPerInt = (keyBits + 7) / 8;
} }
@Override
public String getKeyType() {
return keyType;
}
@Override @Override
public byte[] getEncoded() { public byte[] getEncoded() {
byte[] encodedKey = new byte[bytesPerInt]; byte[] encodedKey = new byte[bytesPerInt];

View File

@@ -15,19 +15,12 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
class Sec1PublicKey implements PublicKey { class Sec1PublicKey implements PublicKey {
private final String keyType;
private final ECPublicKeyParameters key; private final ECPublicKeyParameters key;
Sec1PublicKey(String keyType, ECPublicKeyParameters key) { Sec1PublicKey(ECPublicKeyParameters key) {
this.keyType = keyType;
this.key = key; this.key = key;
} }
@Override
public String getKeyType() {
return keyType;
}
@Override @Override
public byte[] getEncoded() { public byte[] getEncoded() {
return key.getQ().getEncoded(false); return key.getQ().getEncoded(false);

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
@@ -41,7 +42,7 @@ class TransportCryptoImpl implements TransportCrypto {
} }
@Override @Override
public TransportKeys deriveRotationKeys(TransportId t, public TransportKeys deriveTransportKeys(TransportId t,
SecretKey rootKey, long timePeriod, boolean weAreAlice, SecretKey rootKey, long timePeriod, boolean weAreAlice,
boolean active) { boolean active) {
// Keys for the previous period are derived from the root key // Keys for the previous period are derived from the root key
@@ -69,6 +70,31 @@ class TransportCryptoImpl implements TransportCrypto {
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr); return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
} }
@Override
public TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod) {
if (k.getTimePeriod() >= timePeriod) return k;
IncomingKeys inPrev = k.getPreviousIncomingKeys();
IncomingKeys inCurr = k.getCurrentIncomingKeys();
IncomingKeys inNext = k.getNextIncomingKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
long startPeriod = outCurr.getTimePeriod();
boolean active = outCurr.isActive();
// Rotate the keys
for (long p = startPeriod + 1; p <= timePeriod; p++) {
inPrev = inCurr;
inCurr = inNext;
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p, active);
}
// Collect and return the keys
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
outCurr);
}
private SecretKey rotateKey(SecretKey k, long timePeriod) { private SecretKey rotateKey(SecretKey k, long timePeriod) {
byte[] period = new byte[INT_64_BYTES]; byte[] period = new byte[INT_64_BYTES];
writeUint64(timePeriod, period, 0); writeUint64(timePeriod, period, 0);
@@ -91,7 +117,7 @@ class TransportCryptoImpl implements TransportCrypto {
} }
@Override @Override
public TransportKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey, public HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean weAreAlice) { long timePeriod, boolean weAreAlice) {
if (timePeriod < 1) throw new IllegalArgumentException(); if (timePeriod < 1) throw new IllegalArgumentException();
IncomingKeys inPrev = deriveIncomingHandshakeKeys(t, rootKey, IncomingKeys inPrev = deriveIncomingHandshakeKeys(t, rootKey,
@@ -102,7 +128,7 @@ class TransportCryptoImpl implements TransportCrypto {
weAreAlice, timePeriod + 1); weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey, OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod); weAreAlice, timePeriod);
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr, rootKey, return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr, rootKey,
weAreAlice); weAreAlice);
} }
@@ -145,13 +171,7 @@ class TransportCryptoImpl implements TransportCrypto {
} }
@Override @Override
public TransportKeys updateTransportKeys(TransportKeys k, long timePeriod) { public HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod) {
if (k.isHandshakeMode()) return updateHandshakeKeys(k, timePeriod);
else return updateRotationKeys(k, timePeriod);
}
private TransportKeys updateHandshakeKeys(TransportKeys k,
long timePeriod) {
long elapsed = timePeriod - k.getTimePeriod(); long elapsed = timePeriod - k.getTimePeriod();
TransportId t = k.getTransportId(); TransportId t = k.getTransportId();
SecretKey rootKey = k.getRootKey(); SecretKey rootKey = k.getRootKey();
@@ -168,7 +188,7 @@ class TransportCryptoImpl implements TransportCrypto {
weAreAlice, timePeriod + 1); weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey, OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod); weAreAlice, timePeriod);
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr, return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
rootKey, weAreAlice); rootKey, weAreAlice);
} else if (elapsed == 2) { } else if (elapsed == 2) {
// The keys are two periods old - shift by two periods, keeping // The keys are two periods old - shift by two periods, keeping
@@ -180,7 +200,7 @@ class TransportCryptoImpl implements TransportCrypto {
weAreAlice, timePeriod + 1); weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey, OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod); weAreAlice, timePeriod);
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr, return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
rootKey, weAreAlice); rootKey, weAreAlice);
} else { } else {
// The keys are more than two periods old - derive fresh keys // The keys are more than two periods old - derive fresh keys
@@ -188,30 +208,6 @@ class TransportCryptoImpl implements TransportCrypto {
} }
} }
private TransportKeys updateRotationKeys(TransportKeys k, long timePeriod) {
if (k.getTimePeriod() >= timePeriod) return k;
IncomingKeys inPrev = k.getPreviousIncomingKeys();
IncomingKeys inCurr = k.getCurrentIncomingKeys();
IncomingKeys inNext = k.getNextIncomingKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
long startPeriod = outCurr.getTimePeriod();
boolean active = outCurr.isActive();
// Rotate the keys
for (long p = startPeriod + 1; p <= timePeriod; p++) {
inPrev = inCurr;
inCurr = inNext;
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p, active);
}
// Collect and return the keys
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
outCurr);
}
@Override @Override
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion, public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber) { long streamNumber) {

View File

@@ -5,8 +5,6 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DataTooNewException; import org.briarproject.bramble.api.db.DataTooNewException;
import org.briarproject.bramble.api.db.DataTooOldException; import org.briarproject.bramble.api.db.DataTooOldException;
@@ -29,8 +27,11 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
@@ -104,6 +105,20 @@ interface Database<T> {
void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared) void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
throws DbException; throws DbException;
/**
* Stores the given handshake keys for the given contact and returns a
* key set ID.
*/
HandshakeKeySetId addHandshakeKeys(T txn, ContactId c, HandshakeKeys k)
throws DbException;
/**
* Stores the given handshake keys for the given pending contact and
* returns a key set ID.
*/
HandshakeKeySetId addHandshakeKeys(T txn, PendingContactId p,
HandshakeKeys k) throws DbException;
/** /**
* Stores an identity. * Stores an identity.
*/ */
@@ -145,14 +160,7 @@ interface Database<T> {
* Stores the given transport keys for the given contact and returns a * Stores the given transport keys for the given contact and returns a
* key set ID. * key set ID.
*/ */
KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k) TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
throws DbException;
/**
* Stores the given transport keys for the given pending contact and
* returns a key set ID.
*/
KeySetId addTransportKeys(T txn, PendingContactId p, TransportKeys k)
throws DbException; throws DbException;
/** /**
@@ -265,7 +273,7 @@ interface Database<T> {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<ContactId> getContacts(T txn, AuthorId local) throws DbException; Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
@@ -307,6 +315,14 @@ interface Database<T> {
Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g) Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
throws DbException; throws DbException;
/**
* Returns all handshake keys for the given transport.
* <p/>
* Read-only.
*/
Collection<HandshakeKeySet> getHandshakeKeys(T txn, TransportId t)
throws DbException;
/** /**
* Returns the identity for local pseudonym with the given ID. * Returns the identity for local pseudonym with the given ID.
* <p/> * <p/>
@@ -529,10 +545,16 @@ interface Database<T> {
Collection<TransportKeySet> getTransportKeys(T txn, TransportId t) Collection<TransportKeySet> getTransportKeys(T txn, TransportId t)
throws DbException; throws DbException;
/**
* Increments the outgoing stream counter for the given handshake keys.
*/
void incrementStreamCounter(T txn, TransportId t, HandshakeKeySetId k)
throws DbException;
/** /**
* Increments the outgoing stream counter for the given transport keys. * Increments the outgoing stream counter for the given transport keys.
*/ */
void incrementStreamCounter(T txn, TransportId t, KeySetId k) void incrementStreamCounter(T txn, TransportId t, TransportKeySetId k)
throws DbException; throws DbException;
/** /**
@@ -601,6 +623,12 @@ interface Database<T> {
void removeGroupVisibility(T txn, ContactId c, GroupId g) void removeGroupVisibility(T txn, ContactId c, GroupId g)
throws DbException; throws DbException;
/**
* Removes the given handshake keys from the database.
*/
void removeHandshakeKeys(T txn, TransportId t, HandshakeKeySetId k)
throws DbException;
/** /**
* Removes an identity (and all associated state) from the database. * Removes an identity (and all associated state) from the database.
*/ */
@@ -631,7 +659,8 @@ interface Database<T> {
/** /**
* Removes the given transport keys from the database. * Removes the given transport keys from the database.
*/ */
void removeTransportKeys(T txn, TransportId t, KeySetId k) throws DbException; void removeTransportKeys(T txn, TransportId t, TransportKeySetId k)
throws DbException;
/** /**
* Resets the transmission count and expiry time of the given message with * Resets the transmission count and expiry time of the given message with
@@ -660,8 +689,8 @@ interface Database<T> {
/** /**
* Sets the handshake key pair for the identity with the given ID. * Sets the handshake key pair for the identity with the given ID.
*/ */
void setHandshakeKeyPair(T txn, AuthorId local, PublicKey publicKey, void setHandshakeKeyPair(T txn, AuthorId local, byte[] publicKey,
PrivateKey privateKey) throws DbException; byte[] privateKey) throws DbException;
/** /**
* Marks the given message as shared. * Marks the given message as shared.
@@ -681,16 +710,23 @@ interface Database<T> {
PendingContactState state) throws DbException; PendingContactState state) throws DbException;
/** /**
* Sets the reordering window for the given transport keys in the given * Sets the reordering window for the given transport key set in the given
* time period. * time period.
*/ */
void setReorderingWindow(T txn, KeySetId k, TransportId t, void setReorderingWindow(T txn, TransportKeySetId k, TransportId t,
long timePeriod, long base, byte[] bitmap) throws DbException;
/**
* Sets the reordering window for the given handshake key set in the given
* time period.
*/
void setReorderingWindow(T txn, HandshakeKeySetId k, TransportId t,
long timePeriod, long base, byte[] bitmap) throws DbException; long timePeriod, long base, byte[] bitmap) throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void setTransportKeysActive(T txn, TransportId t, KeySetId k) void setTransportKeysActive(T txn, TransportId t, TransportKeySetId k)
throws DbException; throws DbException;
/** /**
@@ -702,7 +738,12 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Stores the given transport keys, deleting any keys they have replaced. * Updates the given handshake keys.
*/
void updateHandshakeKeys(T txn, HandshakeKeySet ks) throws DbException;
/**
* Updates the given transport keys following key rotation.
*/ */
void updateTransportKeys(T txn, TransportKeySet ks) throws DbException; void updateTransportKeys(T txn, TransportKeySet ks) throws DbException;
} }

View File

@@ -7,10 +7,6 @@ import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent; import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.CommitAction; import org.briarproject.bramble.api.db.CommitAction;
import org.briarproject.bramble.api.db.CommitAction.Visitor; import org.briarproject.bramble.api.db.CommitAction.Visitor;
@@ -66,8 +62,11 @@ import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent; import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.ArrayList; import java.util.ArrayList;
@@ -259,6 +258,30 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
} }
@Override
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
ContactId c, HandshakeKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addHandshakeKeys(txn, c, k);
}
@Override
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
PendingContactId p, HandshakeKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addHandshakeKeys(txn, p, k);
}
@Override @Override
public void addIdentity(Transaction transaction, Identity i) public void addIdentity(Transaction transaction, Identity i)
throws DbException { throws DbException {
@@ -295,8 +318,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (db.containsPendingContact(txn, p.getId())) if (db.containsPendingContact(txn, p.getId()))
throw new PendingContactExistsException(); throw new PendingContactExistsException();
db.addPendingContact(txn, p); db.addPendingContact(txn, p);
transaction.attach(new PendingContactStateChangedEvent(p.getId(),
p.getState()));
} }
@Override @Override
@@ -309,8 +330,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public KeySetId addTransportKeys(Transaction transaction, ContactId c, public TransportKeySetId addTransportKeys(Transaction transaction,
TransportKeys k) throws DbException { ContactId c, TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
@@ -320,18 +341,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.addTransportKeys(txn, c, k); return db.addTransportKeys(txn, c, k);
} }
@Override
public KeySetId addTransportKeys(Transaction transaction,
PendingContactId p, TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addTransportKeys(txn, p, k);
}
@Override @Override
public boolean containsContact(Transaction transaction, AuthorId remote, public boolean containsContact(Transaction transaction, AuthorId remote,
AuthorId local) throws DbException { AuthorId local) throws DbException {
@@ -494,11 +503,11 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public Collection<ContactId> getContacts(Transaction transaction, public Collection<ContactId> getContacts(Transaction transaction,
AuthorId local) throws DbException { AuthorId a) throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsIdentity(txn, local)) if (!db.containsIdentity(txn, a))
throw new NoSuchIdentityException(); throw new NoSuchIdentityException();
return db.getContacts(txn, local); return db.getContacts(txn, a);
} }
@Override @Override
@@ -535,6 +544,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getGroupVisibility(txn, c, g); return db.getGroupVisibility(txn, c, g);
} }
@Override
public Collection<HandshakeKeySet> getHandshakeKeys(Transaction transaction,
TransportId t) throws DbException {
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
return db.getHandshakeKeys(txn, t);
}
@Override @Override
public Identity getIdentity(Transaction transaction, AuthorId a) public Identity getIdentity(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
@@ -717,7 +735,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void incrementStreamCounter(Transaction transaction, TransportId t, public void incrementStreamCounter(Transaction transaction, TransportId t,
KeySetId k) throws DbException { HandshakeKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
db.incrementStreamCounter(txn, t, k);
}
@Override
public void incrementStreamCounter(Transaction transaction, TransportId t,
TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -866,6 +894,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new GroupVisibilityUpdatedEvent(affected)); transaction.attach(new GroupVisibilityUpdatedEvent(affected));
} }
@Override
public void removeHandshakeKeys(Transaction transaction,
TransportId t, HandshakeKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
db.removeHandshakeKeys(txn, t, k);
}
@Override @Override
public void removeIdentity(Transaction transaction, AuthorId a) public void removeIdentity(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
@@ -896,7 +934,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (!db.containsPendingContact(txn, p)) if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException(); throw new NoSuchPendingContactException();
db.removePendingContact(txn, p); db.removePendingContact(txn, p);
transaction.attach(new PendingContactRemovedEvent(p));
} }
@Override @Override
@@ -910,8 +947,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void removeTransportKeys(Transaction transaction, TransportId t, public void removeTransportKeys(Transaction transaction,
KeySetId k) throws DbException { TransportId t, TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -1000,7 +1037,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void setHandshakeKeyPair(Transaction transaction, AuthorId local, public void setHandshakeKeyPair(Transaction transaction, AuthorId local,
PublicKey publicKey, PrivateKey privateKey) throws DbException { byte[] publicKey, byte[] privateKey) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsIdentity(txn, local)) if (!db.containsIdentity(txn, local))
@@ -1009,9 +1046,20 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void setReorderingWindow(Transaction transaction, KeySetId k, public void setReorderingWindow(Transaction transaction,
TransportId t, long timePeriod, long base, byte[] bitmap) TransportKeySetId k, TransportId t, long timePeriod, long base,
throws DbException { byte[] bitmap) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
db.setReorderingWindow(txn, k, t, timePeriod, base, bitmap);
}
@Override
public void setReorderingWindow(Transaction transaction,
HandshakeKeySetId k, TransportId t, long timePeriod, long base,
byte[] bitmap) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -1021,7 +1069,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void setTransportKeysActive(Transaction transaction, TransportId t, public void setTransportKeysActive(Transaction transaction, TransportId t,
KeySetId k) throws DbException { TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -1029,6 +1077,18 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.setTransportKeysActive(txn, t, k); db.setTransportKeysActive(txn, t, k);
} }
@Override
public void updateHandshakeKeys(Transaction transaction,
Collection<HandshakeKeySet> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
for (HandshakeKeySet ks : keys) {
TransportId t = ks.getKeys().getTransportId();
if (db.containsTransport(txn, t))
db.updateHandshakeKeys(txn, ks);
}
}
@Override @Override
public void updateTransportKeys(Transaction transaction, public void updateTransportKeys(Transaction transaction,
Collection<TransportKeySet> keys) throws DbException { Collection<TransportKeySet> keys) throws DbException {

View File

@@ -20,11 +20,9 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose; import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
/** /**
* Contains all the H2-specific code for the database. * Contains all the H2-specific code for the database.
@@ -63,18 +61,8 @@ class H2Database extends JdbcDatabase {
public boolean open(SecretKey key, @Nullable MigrationListener listener) public boolean open(SecretKey key, @Nullable MigrationListener listener)
throws DbException { throws DbException {
this.key = key; this.key = key;
File dir = config.getDatabaseDirectory(); boolean reopen = !config.getDatabaseDirectory().mkdirs();
if (LOG.isLoggable(INFO)) {
LOG.info("Contents of account directory before opening DB:");
logFileOrDir(LOG, INFO, dir.getParentFile());
}
boolean reopen = !dir.mkdirs();
if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen);
super.open("org.h2.Driver", reopen, key, listener); super.open("org.h2.Driver", reopen, key, listener);
if (LOG.isLoggable(INFO)) {
LOG.info("Contents of account directory after opening DB:");
logFileOrDir(LOG, INFO, dir.getParentFile());
}
return reopen; return reopen;
} }

View File

@@ -5,13 +5,7 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
import org.briarproject.bramble.api.db.DataTooNewException; import org.briarproject.bramble.api.db.DataTooNewException;
import org.briarproject.bramble.api.db.DataTooOldException; import org.briarproject.bramble.api.db.DataTooOldException;
import org.briarproject.bramble.api.db.DbClosedException; import org.briarproject.bramble.api.db.DbClosedException;
@@ -36,10 +30,13 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.sql.Connection; import java.sql.Connection;
@@ -65,13 +62,11 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.sql.Types.BINARY; import static java.sql.Types.BINARY;
import static java.sql.Types.BOOLEAN;
import static java.sql.Types.INTEGER; import static java.sql.Types.INTEGER;
import static java.sql.Types.VARCHAR; import static java.sql.Types.VARCHAR;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.db.Metadata.REMOVE; import static org.briarproject.bramble.api.db.Metadata.REMOVE;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
@@ -98,7 +93,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
abstract class JdbcDatabase implements Database<Connection> { abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // Package access for testing
static final int CODE_SCHEMA_VERSION = 44; static final int CODE_SCHEMA_VERSION = 43;
// Time period offsets for incoming transport keys // Time period offsets for incoming transport keys
private static final int OFFSET_PREV = -1; private static final int OFFSET_PREV = -1;
@@ -259,28 +254,16 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " maxLatency INT NOT NULL," + " maxLatency INT NOT NULL,"
+ " PRIMARY KEY (transportId))"; + " PRIMARY KEY (transportId))";
private static final String CREATE_PENDING_CONTACTS =
"CREATE TABLE pendingContacts"
+ " (pendingContactId _HASH NOT NULL,"
+ " publicKey _BINARY NOT NULL,"
+ " alias _STRING NOT NULL,"
+ " state INT NOT NULL,"
+ " timestamp BIGINT NOT NULL,"
+ " PRIMARY KEY (pendingContactId))";
private static final String CREATE_OUTGOING_KEYS = private static final String CREATE_OUTGOING_KEYS =
"CREATE TABLE outgoingKeys" "CREATE TABLE outgoingKeys"
+ " (transportId _STRING NOT NULL," + " (transportId _STRING NOT NULL,"
+ " keySetId _COUNTER," + " keySetId _COUNTER,"
+ " timePeriod BIGINT NOT NULL," + " timePeriod BIGINT NOT NULL,"
+ " contactId INT," // Null if contact is pending + " contactId INT NOT NULL,"
+ " pendingContactId _HASH," // Null if not pending
+ " tagKey _SECRET NOT NULL," + " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL," + " headerKey _SECRET NOT NULL,"
+ " stream BIGINT NOT NULL," + " stream BIGINT NOT NULL,"
+ " active BOOLEAN NOT NULL," + " active BOOLEAN NOT NULL,"
+ " rootKey _SECRET," // Null for rotation keys
+ " alice BOOLEAN," // Null for rotation keys
+ " PRIMARY KEY (transportId, keySetId)," + " PRIMARY KEY (transportId, keySetId),"
+ " FOREIGN KEY (transportId)" + " FOREIGN KEY (transportId)"
+ " REFERENCES transports (transportId)" + " REFERENCES transports (transportId)"
@@ -288,9 +271,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " UNIQUE (keySetId)," + " UNIQUE (keySetId),"
+ " FOREIGN KEY (contactId)" + " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (contactId)" + " REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (pendingContactId)"
+ " REFERENCES pendingContacts (pendingContactId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
private static final String CREATE_INCOMING_KEYS = private static final String CREATE_INCOMING_KEYS =
@@ -311,6 +291,57 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " REFERENCES outgoingKeys (keySetId)" + " REFERENCES outgoingKeys (keySetId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
private static final String CREATE_PENDING_CONTACTS =
"CREATE TABLE pendingContacts"
+ " (pendingContactId _HASH NOT NULL,"
+ " publicKey _BINARY NOT NULL,"
+ " alias _STRING NOT NULL,"
+ " state INT NOT NULL,"
+ " timestamp BIGINT NOT NULL,"
+ " PRIMARY KEY (pendingContactId))";
private static final String CREATE_OUTGOING_HANDSHAKE_KEYS =
"CREATE TABLE outgoingHandshakeKeys"
+ " (transportId _STRING NOT NULL,"
+ " keySetId _COUNTER,"
+ " timePeriod BIGINT NOT NULL,"
+ " contactId INT," // Null if contact is pending
+ " pendingContactId _HASH," // Null if not pending
+ " rootKey _SECRET NOT NULL,"
+ " alice BOOLEAN NOT NULL,"
+ " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL,"
+ " stream BIGINT NOT NULL,"
+ " PRIMARY KEY (transportId, keySetId),"
+ " FOREIGN KEY (transportId)"
+ " REFERENCES transports (transportId)"
+ " ON DELETE CASCADE,"
+ " UNIQUE (keySetId),"
+ " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (pendingContactId)"
+ " REFERENCES pendingContacts (pendingContactId)"
+ " ON DELETE CASCADE)";
private static final String CREATE_INCOMING_HANDSHAKE_KEYS =
"CREATE TABLE incomingHandshakeKeys"
+ " (transportId _STRING NOT NULL,"
+ " keySetId INT NOT NULL,"
+ " timePeriod BIGINT NOT NULL,"
+ " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL,"
+ " base BIGINT NOT NULL,"
+ " bitmap _BINARY NOT NULL,"
+ " periodOffset INT NOT NULL,"
+ " PRIMARY KEY (transportId, keySetId, periodOffset),"
+ " FOREIGN KEY (transportId)"
+ " REFERENCES transports (transportId)"
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (keySetId)"
+ " REFERENCES outgoingHandshakeKeys (keySetId)"
+ " ON DELETE CASCADE)";
private static final String INDEX_CONTACTS_BY_AUTHOR_ID = private static final String INDEX_CONTACTS_BY_AUTHOR_ID =
"CREATE INDEX IF NOT EXISTS contactsByAuthorId" "CREATE INDEX IF NOT EXISTS contactsByAuthorId"
+ " ON contacts (authorId)"; + " ON contacts (authorId)";
@@ -336,7 +367,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " ON statuses (contactId, timestamp)"; + " ON statuses (contactId, timestamp)";
private static final Logger LOG = private static final Logger LOG =
getLogger(JdbcDatabase.class.getName()); Logger.getLogger(JdbcDatabase.class.getName());
// Different database libraries use different names for certain types // Different database libraries use different names for certain types
private final MessageFactory messageFactory; private final MessageFactory messageFactory;
@@ -456,8 +487,7 @@ abstract class JdbcDatabase implements Database<Connection> {
new Migration39_40(), new Migration39_40(),
new Migration40_41(dbTypes), new Migration40_41(dbTypes),
new Migration41_42(dbTypes), new Migration41_42(dbTypes),
new Migration42_43(dbTypes), new Migration42_43(dbTypes)
new Migration43_44(dbTypes)
); );
} }
@@ -505,9 +535,13 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(dbTypes.replaceTypes(CREATE_OFFERS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_OFFERS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_STATUSES)); s.executeUpdate(dbTypes.replaceTypes(CREATE_STATUSES));
s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_PENDING_CONTACTS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_PENDING_CONTACTS));
s.executeUpdate(dbTypes.replaceTypes(
CREATE_OUTGOING_HANDSHAKE_KEYS));
s.executeUpdate(dbTypes.replaceTypes(
CREATE_INCOMING_HANDSHAKE_KEYS));
s.close(); s.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s, LOG, WARNING); tryToClose(s, LOG, WARNING);
@@ -643,7 +677,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(1, remote.getId().getBytes()); ps.setBytes(1, remote.getId().getBytes());
ps.setInt(2, remote.getFormatVersion()); ps.setInt(2, remote.getFormatVersion());
ps.setString(3, remote.getName()); ps.setString(3, remote.getName());
ps.setBytes(4, remote.getPublicKey().getEncoded()); ps.setBytes(4, remote.getPublicKey());
ps.setBytes(5, local.getBytes()); ps.setBytes(5, local.getBytes());
ps.setBoolean(6, verified); ps.setBoolean(6, verified);
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
@@ -743,6 +777,103 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public HandshakeKeySetId addHandshakeKeys(Connection txn, ContactId c,
HandshakeKeys k) throws DbException {
return addHandshakeKeys(txn, c, null, k);
}
@Override
public HandshakeKeySetId addHandshakeKeys(Connection txn,
PendingContactId p, HandshakeKeys k) throws DbException {
return addHandshakeKeys(txn, null, p, k);
}
private HandshakeKeySetId addHandshakeKeys(Connection txn,
@Nullable ContactId c, @Nullable PendingContactId p,
HandshakeKeys k) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Store the outgoing keys
String sql = "INSERT INTO outgoingHandshakeKeys (contactId,"
+ " pendingContactId, transportId, rootKey, alice,"
+ " timePeriod, tagKey, headerKey, stream)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
if (c == null) ps.setNull(1, INTEGER);
else ps.setInt(1, c.getInt());
if (p == null) ps.setNull(2, BINARY);
else ps.setBytes(2, p.getBytes());
ps.setString(3, k.getTransportId().getString());
ps.setBytes(4, k.getRootKey().getBytes());
ps.setBoolean(5, k.isAlice());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
ps.setLong(6, outCurr.getTimePeriod());
ps.setBytes(7, outCurr.getTagKey().getBytes());
ps.setBytes(8, outCurr.getHeaderKey().getBytes());
ps.setLong(9, outCurr.getStreamCounter());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
// Get the new (highest) key set ID
sql = "SELECT keySetId FROM outgoingHandshakeKeys"
+ " ORDER BY keySetId DESC LIMIT 1";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
HandshakeKeySetId keySetId = new HandshakeKeySetId(rs.getInt(1));
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
// Store the incoming keys
sql = "INSERT INTO incomingHandshakeKeys (keySetId, transportId,"
+ " timePeriod, tagKey, headerKey, base, bitmap,"
+ " periodOffset)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setInt(1, keySetId.getInt());
ps.setString(2, k.getTransportId().getString());
// Previous time period
IncomingKeys inPrev = k.getPreviousIncomingKeys();
ps.setLong(3, inPrev.getTimePeriod());
ps.setBytes(4, inPrev.getTagKey().getBytes());
ps.setBytes(5, inPrev.getHeaderKey().getBytes());
ps.setLong(6, inPrev.getWindowBase());
ps.setBytes(7, inPrev.getWindowBitmap());
ps.setInt(8, OFFSET_PREV);
ps.addBatch();
// Current time period
IncomingKeys inCurr = k.getCurrentIncomingKeys();
ps.setLong(3, inCurr.getTimePeriod());
ps.setBytes(4, inCurr.getTagKey().getBytes());
ps.setBytes(5, inCurr.getHeaderKey().getBytes());
ps.setLong(6, inCurr.getWindowBase());
ps.setBytes(7, inCurr.getWindowBitmap());
ps.setInt(8, OFFSET_CURR);
ps.addBatch();
// Next time period
IncomingKeys inNext = k.getNextIncomingKeys();
ps.setLong(3, inNext.getTimePeriod());
ps.setBytes(4, inNext.getTagKey().getBytes());
ps.setBytes(5, inNext.getHeaderKey().getBytes());
ps.setLong(6, inNext.getWindowBase());
ps.setBytes(7, inNext.getWindowBitmap());
ps.setInt(8, OFFSET_NEXT);
ps.addBatch();
int[] batchAffected = ps.executeBatch();
if (batchAffected.length != 3) throw new DbStateException();
for (int rows : batchAffected)
if (rows != 1) throw new DbStateException();
ps.close();
return keySetId;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void addIdentity(Connection txn, Identity i) throws DbException { public void addIdentity(Connection txn, Identity i) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -756,12 +887,12 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(1, local.getId().getBytes()); ps.setBytes(1, local.getId().getBytes());
ps.setInt(2, local.getFormatVersion()); ps.setInt(2, local.getFormatVersion());
ps.setString(3, local.getName()); ps.setString(3, local.getName());
ps.setBytes(4, local.getPublicKey().getEncoded()); ps.setBytes(4, local.getPublicKey());
ps.setBytes(5, local.getPrivateKey().getEncoded()); ps.setBytes(5, local.getPrivateKey());
if (i.getHandshakePublicKey() == null) ps.setNull(6, BINARY); if (i.getHandshakePublicKey() == null) ps.setNull(6, BINARY);
else ps.setBytes(6, i.getHandshakePublicKey().getEncoded()); else ps.setBytes(6, i.getHandshakePublicKey());
if (i.getHandshakePrivateKey() == null) ps.setNull(7, BINARY); if (i.getHandshakePrivateKey() == null) ps.setNull(7, BINARY);
else ps.setBytes(7, i.getHandshakePrivateKey().getEncoded()); else ps.setBytes(7, i.getHandshakePrivateKey());
ps.setLong(8, i.getTimeCreated()); ps.setLong(8, i.getTimeCreated());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
@@ -937,7 +1068,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " VALUES (?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, p.getId().getBytes()); ps.setBytes(1, p.getId().getBytes());
ps.setBytes(2, p.getPublicKey().getEncoded()); ps.setBytes(2, p.getPublicKey());
ps.setString(3, p.getAlias()); ps.setString(3, p.getAlias());
ps.setInt(4, p.getState().getValue()); ps.setInt(4, p.getState().getValue());
ps.setLong(5, p.getTimestamp()); ps.setLong(5, p.getTimestamp());
@@ -970,47 +1101,24 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public KeySetId addTransportKeys(Connection txn, ContactId c, public TransportKeySetId addTransportKeys(Connection txn, ContactId c,
TransportKeys k) throws DbException {
return addTransportKeys(txn, c, null, k);
}
@Override
public KeySetId addTransportKeys(Connection txn,
PendingContactId p, TransportKeys k) throws DbException {
return addTransportKeys(txn, null, p, k);
}
private KeySetId addTransportKeys(Connection txn,
@Nullable ContactId c, @Nullable PendingContactId p,
TransportKeys k) throws DbException { TransportKeys k) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
// Store the outgoing keys // Store the outgoing keys
String sql = "INSERT INTO outgoingKeys (transportId, timePeriod," String sql = "INSERT INTO outgoingKeys (contactId, transportId,"
+ " contactId, pendingContactId, tagKey, headerKey," + " timePeriod, tagKey, headerKey, stream, active)"
+ " stream, active, rootKey, alice)" + " VALUES (?, ?, ?, ?, ?, ?, ?)";
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setString(1, k.getTransportId().getString()); ps.setInt(1, c.getInt());
ps.setLong(2, k.getTimePeriod()); ps.setString(2, k.getTransportId().getString());
if (c == null) ps.setNull(3, INTEGER);
else ps.setInt(3, c.getInt());
if (p == null) ps.setNull(4, BINARY);
else ps.setBytes(4, p.getBytes());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
ps.setBytes(5, outCurr.getTagKey().getBytes()); ps.setLong(3, outCurr.getTimePeriod());
ps.setBytes(6, outCurr.getHeaderKey().getBytes()); ps.setBytes(4, outCurr.getTagKey().getBytes());
ps.setLong(7, outCurr.getStreamCounter()); ps.setBytes(5, outCurr.getHeaderKey().getBytes());
ps.setBoolean(8, outCurr.isActive()); ps.setLong(6, outCurr.getStreamCounter());
if (k.isHandshakeMode()) { ps.setBoolean(7, outCurr.isActive());
ps.setBytes(9, k.getRootKey().getBytes());
ps.setBoolean(10, k.isAlice());
} else {
ps.setNull(9, BINARY);
ps.setNull(10, BOOLEAN);
}
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -1020,18 +1128,18 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
rs = ps.executeQuery(); rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException(); if (!rs.next()) throw new DbStateException();
KeySetId keySetId = new KeySetId(rs.getInt(1)); TransportKeySetId keySetId = new TransportKeySetId(rs.getInt(1));
if (rs.next()) throw new DbStateException(); if (rs.next()) throw new DbStateException();
rs.close(); rs.close();
ps.close(); ps.close();
// Store the incoming keys // Store the incoming keys
sql = "INSERT INTO incomingKeys (transportId, keySetId," sql = "INSERT INTO incomingKeys (keySetId, transportId,"
+ " timePeriod, tagKey, headerKey, base, bitmap," + " timePeriod, tagKey, headerKey, base, bitmap,"
+ " periodOffset)" + " periodOffset)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setString(1, k.getTransportId().getString()); ps.setInt(1, keySetId.getInt());
ps.setInt(2, keySetId.getInt()); ps.setString(2, k.getTransportId().getString());
// Previous time period // Previous time period
IncomingKeys inPrev = k.getPreviousIncomingKeys(); IncomingKeys inPrev = k.getPreviousIncomingKeys();
ps.setLong(3, inPrev.getTimePeriod()); ps.setLong(3, inPrev.getTimePeriod());
@@ -1336,16 +1444,14 @@ abstract class JdbcDatabase implements Database<Connection> {
int formatVersion = rs.getInt(2); int formatVersion = rs.getInt(2);
String name = rs.getString(3); String name = rs.getString(3);
String alias = rs.getString(4); String alias = rs.getString(4);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(5)); byte[] publicKey = rs.getBytes(5);
byte[] handshakePub = rs.getBytes(6); byte[] handshakePublicKey = rs.getBytes(6);
AuthorId localAuthorId = new AuthorId(rs.getBytes(7)); AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
boolean verified = rs.getBoolean(8); boolean verified = rs.getBoolean(8);
rs.close(); rs.close();
ps.close(); ps.close();
Author author = Author author =
new Author(authorId, formatVersion, name, publicKey); new Author(authorId, formatVersion, name, publicKey);
PublicKey handshakePublicKey = handshakePub == null ?
null : new AgreementPublicKey(handshakePub);
return new Contact(c, author, localAuthorId, alias, return new Contact(c, author, localAuthorId, alias,
handshakePublicKey, verified); handshakePublicKey, verified);
} catch (SQLException e) { } catch (SQLException e) {
@@ -1373,14 +1479,12 @@ abstract class JdbcDatabase implements Database<Connection> {
int formatVersion = rs.getInt(3); int formatVersion = rs.getInt(3);
String name = rs.getString(4); String name = rs.getString(4);
String alias = rs.getString(5); String alias = rs.getString(5);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(6)); byte[] publicKey = rs.getBytes(6);
byte[] handshakePub = rs.getBytes(7); byte[] handshakePublicKey = rs.getBytes(7);
AuthorId localAuthorId = new AuthorId(rs.getBytes(8)); AuthorId localAuthorId = new AuthorId(rs.getBytes(8));
boolean verified = rs.getBoolean(9); boolean verified = rs.getBoolean(9);
Author author = Author author =
new Author(authorId, formatVersion, name, publicKey); new Author(authorId, formatVersion, name, publicKey);
PublicKey handshakePublicKey = handshakePub == null ?
null : new AgreementPublicKey(handshakePub);
contacts.add(new Contact(contactId, author, localAuthorId, contacts.add(new Contact(contactId, author, localAuthorId,
alias, handshakePublicKey, verified)); alias, handshakePublicKey, verified));
} }
@@ -1436,14 +1540,12 @@ abstract class JdbcDatabase implements Database<Connection> {
int formatVersion = rs.getInt(2); int formatVersion = rs.getInt(2);
String name = rs.getString(3); String name = rs.getString(3);
String alias = rs.getString(4); String alias = rs.getString(4);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(5)); byte[] publicKey = rs.getBytes(5);
byte[] handshakePub = rs.getBytes(6); byte[] handshakePublicKey = rs.getBytes(6);
AuthorId localAuthorId = new AuthorId(rs.getBytes(7)); AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
boolean verified = rs.getBoolean(8); boolean verified = rs.getBoolean(8);
Author author = Author author =
new Author(remote, formatVersion, name, publicKey); new Author(remote, formatVersion, name, publicKey);
PublicKey handshakePublicKey = handshakePub == null ?
null : new AgreementPublicKey(handshakePub);
contacts.add(new Contact(contactId, author, localAuthorId, contacts.add(new Contact(contactId, author, localAuthorId,
alias, handshakePublicKey, verified)); alias, handshakePublicKey, verified));
} }
@@ -1559,6 +1661,86 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public Collection<HandshakeKeySet> getHandshakeKeys(Connection txn,
TransportId t) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Retrieve the incoming keys
String sql = "SELECT timePeriod, tagKey, headerKey, base, bitmap"
+ " FROM incomingHandshakeKeys"
+ " WHERE transportId = ?"
+ " ORDER BY keySetId, periodOffset";
ps = txn.prepareStatement(sql);
ps.setString(1, t.getString());
rs = ps.executeQuery();
List<IncomingKeys> inKeys = new ArrayList<>();
while (rs.next()) {
long timePeriod = rs.getLong(1);
SecretKey tagKey = new SecretKey(rs.getBytes(2));
SecretKey headerKey = new SecretKey(rs.getBytes(3));
long windowBase = rs.getLong(4);
byte[] windowBitmap = rs.getBytes(5);
inKeys.add(new IncomingKeys(tagKey, headerKey, timePeriod,
windowBase, windowBitmap));
}
rs.close();
ps.close();
// Retrieve the outgoing keys in the same order
sql = "SELECT keySetId, contactId, pendingContactId, timePeriod,"
+ " tagKey, headerKey, rootKey, alice, stream"
+ " FROM outgoingHandshakeKeys"
+ " WHERE transportId = ?"
+ " ORDER BY keySetId";
ps = txn.prepareStatement(sql);
ps.setString(1, t.getString());
rs = ps.executeQuery();
Collection<HandshakeKeySet> keys = new ArrayList<>();
for (int i = 0; rs.next(); i++) {
// There should be three times as many incoming keys
if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
HandshakeKeySetId keySetId =
new HandshakeKeySetId(rs.getInt(1));
ContactId contactId = null;
int cId = rs.getInt(2);
if (!rs.wasNull()) contactId = new ContactId(cId);
PendingContactId pendingContactId = null;
byte[] pId = rs.getBytes(3);
if (!rs.wasNull()) pendingContactId = new PendingContactId(pId);
long timePeriod = rs.getLong(4);
SecretKey tagKey = new SecretKey(rs.getBytes(5));
SecretKey headerKey = new SecretKey(rs.getBytes(6));
SecretKey rootKey = new SecretKey(rs.getBytes(7));
boolean alice = rs.getBoolean(8);
long streamCounter = rs.getLong(9);
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
timePeriod, streamCounter, true);
IncomingKeys inPrev = inKeys.get(i * 3);
IncomingKeys inCurr = inKeys.get(i * 3 + 1);
IncomingKeys inNext = inKeys.get(i * 3 + 2);
HandshakeKeys handshakeKeys = new HandshakeKeys(t, inPrev,
inCurr, inNext, outCurr, rootKey, alice);
if (contactId == null) {
if (pendingContactId == null) throw new DbStateException();
keys.add(new HandshakeKeySet(keySetId, pendingContactId,
handshakeKeys));
} else {
if (pendingContactId != null) throw new DbStateException();
keys.add(new HandshakeKeySet(keySetId, contactId,
handshakeKeys));
}
}
rs.close();
ps.close();
return keys;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public Identity getIdentity(Connection txn, AuthorId a) throws DbException { public Identity getIdentity(Connection txn, AuthorId a) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -1574,20 +1756,16 @@ abstract class JdbcDatabase implements Database<Connection> {
if (!rs.next()) throw new DbStateException(); if (!rs.next()) throw new DbStateException();
int formatVersion = rs.getInt(1); int formatVersion = rs.getInt(1);
String name = rs.getString(2); String name = rs.getString(2);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(3)); byte[] publicKey = rs.getBytes(3);
PrivateKey privateKey = new SignaturePrivateKey(rs.getBytes(4)); byte[] privateKey = rs.getBytes(4);
byte[] handshakePub = rs.getBytes(5); byte[] handshakePublicKey = rs.getBytes(5);
byte[] handshakePriv = rs.getBytes(6); byte[] handshakePrivateKey = rs.getBytes(6);
long created = rs.getLong(7); long created = rs.getLong(7);
if (rs.next()) throw new DbStateException(); if (rs.next()) throw new DbStateException();
rs.close(); rs.close();
ps.close(); ps.close();
LocalAuthor local = new LocalAuthor(a, formatVersion, name, LocalAuthor local = new LocalAuthor(a, formatVersion, name,
publicKey, privateKey); publicKey, privateKey);
PublicKey handshakePublicKey = handshakePub == null ?
null : new AgreementPublicKey(handshakePub);
PrivateKey handshakePrivateKey = handshakePriv == null ?
null : new AgreementPrivateKey(handshakePriv);
return new Identity(local, handshakePublicKey, handshakePrivateKey, return new Identity(local, handshakePublicKey, handshakePrivateKey,
created); created);
} catch (SQLException e) { } catch (SQLException e) {
@@ -1614,17 +1792,13 @@ abstract class JdbcDatabase implements Database<Connection> {
AuthorId authorId = new AuthorId(rs.getBytes(1)); AuthorId authorId = new AuthorId(rs.getBytes(1));
int formatVersion = rs.getInt(2); int formatVersion = rs.getInt(2);
String name = rs.getString(3); String name = rs.getString(3);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(4)); byte[] publicKey = rs.getBytes(4);
PrivateKey privateKey = new SignaturePrivateKey(rs.getBytes(5)); byte[] privateKey = rs.getBytes(5);
byte[] handshakePub = rs.getBytes(6); byte[] handshakePublicKey = rs.getBytes(6);
byte[] handshakePriv = rs.getBytes(7); byte[] handshakePrivateKey = rs.getBytes(7);
long created = rs.getLong(8); long created = rs.getLong(8);
LocalAuthor local = new LocalAuthor(authorId, formatVersion, LocalAuthor local = new LocalAuthor(authorId, formatVersion,
name, publicKey, privateKey); name, publicKey, privateKey);
PublicKey handshakePublicKey = handshakePub == null ?
null : new AgreementPublicKey(handshakePub);
PrivateKey handshakePrivateKey = handshakePriv == null ?
null : new AgreementPrivateKey(handshakePriv);
identities.add(new Identity(local, handshakePublicKey, identities.add(new Identity(local, handshakePublicKey,
handshakePrivateKey, created)); handshakePrivateKey, created));
} }
@@ -2221,7 +2395,7 @@ abstract class JdbcDatabase implements Database<Connection> {
List<PendingContact> pendingContacts = new ArrayList<>(); List<PendingContact> pendingContacts = new ArrayList<>();
while (rs.next()) { while (rs.next()) {
PendingContactId id = new PendingContactId(rs.getBytes(1)); PendingContactId id = new PendingContactId(rs.getBytes(1));
PublicKey publicKey = new AgreementPublicKey(rs.getBytes(2)); byte[] publicKey = rs.getBytes(2);
String alias = rs.getString(3); String alias = rs.getString(3);
PendingContactState state = PendingContactState state =
PendingContactState.fromValue(rs.getInt(4)); PendingContactState.fromValue(rs.getInt(4));
@@ -2328,8 +2502,8 @@ abstract class JdbcDatabase implements Database<Connection> {
rs.close(); rs.close();
ps.close(); ps.close();
// Retrieve the outgoing keys in the same order // Retrieve the outgoing keys in the same order
sql = "SELECT keySetId, timePeriod, contactId, pendingContactId," sql = "SELECT keySetId, contactId, timePeriod,"
+ " tagKey, headerKey, stream, active, rootKey, alice" + " tagKey, headerKey, stream, active"
+ " FROM outgoingKeys" + " FROM outgoingKeys"
+ " WHERE transportId = ?" + " WHERE transportId = ?"
+ " ORDER BY keySetId"; + " ORDER BY keySetId";
@@ -2340,34 +2514,23 @@ abstract class JdbcDatabase implements Database<Connection> {
for (int i = 0; rs.next(); i++) { for (int i = 0; rs.next(); i++) {
// There should be three times as many incoming keys // There should be three times as many incoming keys
if (inKeys.size() < (i + 1) * 3) throw new DbStateException(); if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
KeySetId keySetId = new KeySetId(rs.getInt(1)); TransportKeySetId keySetId =
long timePeriod = rs.getLong(2); new TransportKeySetId(rs.getInt(1));
int cId = rs.getInt(3); ContactId contactId = new ContactId(rs.getInt(2));
ContactId contactId = rs.wasNull() ? null : new ContactId(cId); long timePeriod = rs.getLong(3);
byte[] pId = rs.getBytes(4); SecretKey tagKey = new SecretKey(rs.getBytes(4));
PendingContactId pendingContactId = pId == null ? SecretKey headerKey = new SecretKey(rs.getBytes(5));
null : new PendingContactId(pId); long streamCounter = rs.getLong(6);
SecretKey tagKey = new SecretKey(rs.getBytes(5)); boolean active = rs.getBoolean(7);
SecretKey headerKey = new SecretKey(rs.getBytes(6));
long streamCounter = rs.getLong(7);
boolean active = rs.getBoolean(8);
byte[] rootKey = rs.getBytes(9);
boolean alice = rs.getBoolean(10);
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey, OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
timePeriod, streamCounter, active); timePeriod, streamCounter, active);
IncomingKeys inPrev = inKeys.get(i * 3); IncomingKeys inPrev = inKeys.get(i * 3);
IncomingKeys inCurr = inKeys.get(i * 3 + 1); IncomingKeys inCurr = inKeys.get(i * 3 + 1);
IncomingKeys inNext = inKeys.get(i * 3 + 2); IncomingKeys inNext = inKeys.get(i * 3 + 2);
TransportKeys transportKeys; TransportKeys transportKeys = new TransportKeys(t, inPrev,
if (rootKey == null) { inCurr, inNext, outCurr);
transportKeys = new TransportKeys(t, inPrev, inCurr,
inNext, outCurr);
} else {
transportKeys = new TransportKeys(t, inPrev, inCurr,
inNext, outCurr, new SecretKey(rootKey), alice);
}
keys.add(new TransportKeySet(keySetId, contactId, keys.add(new TransportKeySet(keySetId, contactId,
pendingContactId, transportKeys)); transportKeys));
} }
rs.close(); rs.close();
ps.close(); ps.close();
@@ -2381,7 +2544,26 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public void incrementStreamCounter(Connection txn, TransportId t, public void incrementStreamCounter(Connection txn, TransportId t,
KeySetId k) throws DbException { HandshakeKeySetId k) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE outgoingHandshakeKeys SET stream = stream + 1"
+ " WHERE transportId = ? AND keySetId = ?";
ps = txn.prepareStatement(sql);
ps.setString(1, t.getString());
ps.setInt(2, k.getInt());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public void incrementStreamCounter(Connection txn, TransportId t,
TransportKeySetId k) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "UPDATE outgoingKeys SET stream = stream + 1" String sql = "UPDATE outgoingKeys SET stream = stream + 1"
@@ -2759,6 +2941,27 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void removeHandshakeKeys(Connection txn, TransportId t,
HandshakeKeySetId k) throws DbException {
PreparedStatement ps = null;
try {
// Delete any existing outgoing keys - this will also remove any
// incoming keys with the same key set ID
String sql = "DELETE FROM outgoingHandshakeKeys"
+ " WHERE transportId = ? AND keySetId = ?";
ps = txn.prepareStatement(sql);
ps.setString(1, t.getString());
ps.setInt(2, k.getInt());
int affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void removeIdentity(Connection txn, AuthorId a) throws DbException { public void removeIdentity(Connection txn, AuthorId a) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -2871,8 +3074,8 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public void removeTransportKeys(Connection txn, TransportId t, KeySetId k) public void removeTransportKeys(Connection txn, TransportId t,
throws DbException { TransportKeySetId k) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
// Delete any existing outgoing keys - this will also remove any // Delete any existing outgoing keys - this will also remove any
@@ -2979,15 +3182,15 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public void setHandshakeKeyPair(Connection txn, AuthorId local, public void setHandshakeKeyPair(Connection txn, AuthorId local,
PublicKey publicKey, PrivateKey privateKey) throws DbException { byte[] publicKey, byte[] privateKey) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "UPDATE localAuthors" String sql = "UPDATE localAuthors"
+ " SET handshakePublicKey = ?, handshakePrivateKey = ?" + " SET handshakePublicKey = ?, handshakePrivateKey = ?"
+ " WHERE authorId = ?"; + " WHERE authorId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, publicKey.getEncoded()); ps.setBytes(1, publicKey);
ps.setBytes(2, privateKey.getEncoded()); ps.setBytes(2, privateKey);
ps.setBytes(3, local.getBytes()); ps.setBytes(3, local.getBytes());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
@@ -3088,7 +3291,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(1, state.getValue()); ps.setInt(1, state.getValue());
ps.setBytes(2, p.getBytes()); ps.setBytes(2, p.getBytes());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps, LOG, WARNING); tryToClose(ps, LOG, WARNING);
@@ -3097,7 +3300,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public void setReorderingWindow(Connection txn, KeySetId k, public void setReorderingWindow(Connection txn, TransportKeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap) TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -3120,9 +3323,33 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void setReorderingWindow(Connection txn, HandshakeKeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE incomingHandshakeKeys SET base = ?, bitmap = ?"
+ " WHERE transportId = ? AND keySetId = ?"
+ " AND timePeriod = ?";
ps = txn.prepareStatement(sql);
ps.setLong(1, base);
ps.setBytes(2, bitmap);
ps.setString(3, t.getString());
ps.setInt(4, k.getInt());
ps.setLong(5, timePeriod);
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void setTransportKeysActive(Connection txn, TransportId t, public void setTransportKeysActive(Connection txn, TransportId t,
KeySetId k) throws DbException { TransportKeySetId k) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "UPDATE outgoingKeys SET active = true" String sql = "UPDATE outgoingKeys SET active = true"
@@ -3242,4 +3469,71 @@ abstract class JdbcDatabase implements Database<Connection> {
throw new DbException(e); throw new DbException(e);
} }
} }
@Override
public void updateHandshakeKeys(Connection txn, HandshakeKeySet ks)
throws DbException {
PreparedStatement ps = null;
try {
// Update the outgoing keys
String sql = "UPDATE outgoingHandshakeKeys SET timePeriod = ?,"
+ " tagKey = ?, headerKey = ?, stream = ?"
+ " WHERE transportId = ? AND keySetId = ?";
ps = txn.prepareStatement(sql);
HandshakeKeys k = ks.getKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
ps.setLong(1, outCurr.getTimePeriod());
ps.setBytes(2, outCurr.getTagKey().getBytes());
ps.setBytes(3, outCurr.getHeaderKey().getBytes());
ps.setLong(4, outCurr.getStreamCounter());
ps.setString(5, k.getTransportId().getString());
ps.setInt(6, ks.getKeySetId().getInt());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
// Update the incoming keys
sql = "UPDATE incomingHandshakeKeys SET timePeriod = ?,"
+ " tagKey = ?, headerKey = ?, base = ?, bitmap = ?"
+ " WHERE transportId = ? AND keySetId = ?"
+ " AND periodOffset = ?";
ps = txn.prepareStatement(sql);
ps.setString(6, k.getTransportId().getString());
ps.setInt(7, ks.getKeySetId().getInt());
// Previous time period
IncomingKeys inPrev = k.getPreviousIncomingKeys();
ps.setLong(1, inPrev.getTimePeriod());
ps.setBytes(2, inPrev.getTagKey().getBytes());
ps.setBytes(3, inPrev.getHeaderKey().getBytes());
ps.setLong(4, inPrev.getWindowBase());
ps.setBytes(5, inPrev.getWindowBitmap());
ps.setInt(8, OFFSET_PREV);
ps.addBatch();
// Current time period
IncomingKeys inCurr = k.getCurrentIncomingKeys();
ps.setLong(1, inCurr.getTimePeriod());
ps.setBytes(2, inCurr.getTagKey().getBytes());
ps.setBytes(3, inCurr.getHeaderKey().getBytes());
ps.setLong(4, inCurr.getWindowBase());
ps.setBytes(5, inCurr.getWindowBitmap());
ps.setInt(8, OFFSET_CURR);
ps.addBatch();
// Next time period
IncomingKeys inNext = k.getNextIncomingKeys();
ps.setLong(1, inNext.getTimePeriod());
ps.setBytes(2, inNext.getTagKey().getBytes());
ps.setBytes(3, inNext.getHeaderKey().getBytes());
ps.setLong(4, inNext.getWindowBase());
ps.setBytes(5, inNext.getWindowBitmap());
ps.setInt(8, OFFSET_NEXT);
ps.addBatch();
int[] batchAffected = ps.executeBatch();
if (batchAffected.length != 3) throw new DbStateException();
for (int rows : batchAffected)
if (rows < 0 || rows > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
} }

View File

@@ -1,58 +0,0 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration43_44 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration43_44.class.getName());
private final DatabaseTypes dbTypes;
Migration43_44(DatabaseTypes dbTypes) {
this.dbTypes = dbTypes;
}
@Override
public int getStartVersion() {
return 43;
}
@Override
public int getEndVersion() {
return 44;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("DROP TABLE outgoingHandshakeKeys");
s.execute("DROP TABLE incomingHandshakeKeys");
s.execute("ALTER TABLE outgoingKeys"
+ " ALTER COLUMN contactId DROP NOT NULL");
s.execute(dbTypes.replaceTypes("ALTER TABLE outgoingKeys"
+ " ADD COLUMN pendingContactId _HASH"));
s.execute("ALTER TABLE outgoingKeys"
+ " ADD FOREIGN KEY (pendingContactId)"
+ " REFERENCES pendingContacts (pendingContactId)"
+ " ON DELETE CASCADE");
s.execute(dbTypes.replaceTypes("ALTER TABLE outgoingKeys"
+ " ADD COLUMN rootKey _SECRET"));
s.execute("ALTER TABLE outgoingKeys"
+ " ADD COLUMN alice BOOLEAN");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -2,13 +2,13 @@ package org.briarproject.bramble.identity;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory; import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
@@ -16,8 +16,6 @@ import javax.inject.Inject;
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION; import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
import static org.briarproject.bramble.api.identity.AuthorId.LABEL; import static org.briarproject.bramble.api.identity.AuthorId.LABEL;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.ByteUtils.writeUint32;
import static org.briarproject.bramble.util.StringUtils.toUtf8;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -31,13 +29,13 @@ class AuthorFactoryImpl implements AuthorFactory {
} }
@Override @Override
public Author createAuthor(String name, PublicKey publicKey) { public Author createAuthor(String name, byte[] publicKey) {
return createAuthor(FORMAT_VERSION, name, publicKey); return createAuthor(FORMAT_VERSION, name, publicKey);
} }
@Override @Override
public Author createAuthor(int formatVersion, String name, public Author createAuthor(int formatVersion, String name,
PublicKey publicKey) { byte[] publicKey) {
AuthorId id = getId(formatVersion, name, publicKey); AuthorId id = getId(formatVersion, name, publicKey);
return new Author(id, formatVersion, name, publicKey); return new Author(id, formatVersion, name, publicKey);
} }
@@ -45,17 +43,16 @@ class AuthorFactoryImpl implements AuthorFactory {
@Override @Override
public LocalAuthor createLocalAuthor(String name) { public LocalAuthor createLocalAuthor(String name) {
KeyPair signatureKeyPair = crypto.generateSignatureKeyPair(); KeyPair signatureKeyPair = crypto.generateSignatureKeyPair();
PublicKey publicKey = signatureKeyPair.getPublic(); byte[] publicKey = signatureKeyPair.getPublic().getEncoded();
PrivateKey privateKey = signatureKeyPair.getPrivate(); byte[] privateKey = signatureKeyPair.getPrivate().getEncoded();
AuthorId id = getId(FORMAT_VERSION, name, publicKey); AuthorId id = getId(FORMAT_VERSION, name, publicKey);
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey); return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
} }
private AuthorId getId(int formatVersion, String name, private AuthorId getId(int formatVersion, String name, byte[] publicKey) {
PublicKey publicKey) {
byte[] formatVersionBytes = new byte[INT_32_BYTES]; byte[] formatVersionBytes = new byte[INT_32_BYTES];
writeUint32(formatVersion, formatVersionBytes, 0); ByteUtils.writeUint32(formatVersion, formatVersionBytes, 0);
return new AuthorId(crypto.hash(LABEL, formatVersionBytes, return new AuthorId(crypto.hash(LABEL, formatVersionBytes,
toUtf8(name), publicKey.getEncoded())); StringUtils.toUtf8(name), publicKey));
} }
} }

View File

@@ -2,8 +2,6 @@ package org.briarproject.bramble.identity;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
@@ -75,8 +73,8 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
long start = now(); long start = now();
LocalAuthor localAuthor = authorFactory.createLocalAuthor(name); LocalAuthor localAuthor = authorFactory.createLocalAuthor(name);
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair(); KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
PublicKey handshakePub = handshakeKeyPair.getPublic(); byte[] handshakePub = handshakeKeyPair.getPublic().getEncoded();
PrivateKey handshakePriv = handshakeKeyPair.getPrivate(); byte[] handshakePriv = handshakeKeyPair.getPrivate().getEncoded();
logDuration(LOG, "Creating identity", start); logDuration(LOG, "Creating identity", start);
return new Identity(localAuthor, handshakePub, handshakePriv, return new Identity(localAuthor, handshakePub, handshakePriv,
clock.currentTimeMillis()); clock.currentTimeMillis());
@@ -100,9 +98,9 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
} else if (shouldStoreKeys) { } else if (shouldStoreKeys) {
// Handshake keys were generated when loading the identity - // Handshake keys were generated when loading the identity -
// store them // store them
PublicKey handshakePub = byte[] handshakePub =
requireNonNull(cached.getHandshakePublicKey()); requireNonNull(cached.getHandshakePublicKey());
PrivateKey handshakePriv = byte[] handshakePriv =
requireNonNull(cached.getHandshakePrivateKey()); requireNonNull(cached.getHandshakePrivateKey());
db.setHandshakeKeyPair(txn, cached.getId(), handshakePub, db.setHandshakeKeyPair(txn, cached.getId(), handshakePub,
handshakePriv); handshakePriv);
@@ -124,12 +122,12 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
} }
@Override @Override
public KeyPair getHandshakeKeys(Transaction txn) throws DbException { public byte[][] getHandshakeKeys(Transaction txn) throws DbException {
Identity cached = getCachedIdentity(txn); Identity cached = getCachedIdentity(txn);
PublicKey handshakePub = requireNonNull(cached.getHandshakePublicKey()); return new byte[][] {
PrivateKey handshakePriv = cached.getHandshakePublicKey(),
requireNonNull(cached.getHandshakePrivateKey()); cached.getHandshakePrivateKey()
return new KeyPair(handshakePub, handshakePriv); };
} }
/** /**
@@ -161,8 +159,8 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
LOG.info("Identity loaded"); LOG.info("Identity loaded");
if (i.hasHandshakeKeyPair()) return i; if (i.hasHandshakeKeyPair()) return i;
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair(); KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
PublicKey handshakePub = handshakeKeyPair.getPublic(); byte[] handshakePub = handshakeKeyPair.getPublic().getEncoded();
PrivateKey handshakePriv = handshakeKeyPair.getPrivate(); byte[] handshakePriv = handshakeKeyPair.getPrivate().getEncoded();
LOG.info("Handshake key pair generated"); LOG.info("Handshake key pair generated");
shouldStoreKeys = true; shouldStoreKeys = true;
return new Identity(i.getLocalAuthor(), handshakePub, handshakePriv, return new Identity(i.getLocalAuthor(), handshakePub, handshakePriv,

View File

@@ -96,7 +96,6 @@ class ConnectionManagerImpl implements ConnectionManager {
TransportConnectionReader r) throws IOException { TransportConnectionReader r) throws IOException {
InputStream streamReader = streamReaderFactory.createStreamReader( InputStream streamReader = streamReaderFactory.createStreamReader(
r.getInputStream(), ctx); r.getInputStream(), ctx);
// TODO: Pending contacts, handshake mode
return syncSessionFactory.createIncomingSession(ctx.getContactId(), return syncSessionFactory.createIncomingSession(ctx.getContactId(),
streamReader); streamReader);
} }
@@ -105,7 +104,6 @@ class ConnectionManagerImpl implements ConnectionManager {
TransportConnectionWriter w) throws IOException { TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter( StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx); w.getOutputStream(), ctx);
// TODO: Pending contacts, handshake mode
return syncSessionFactory.createSimplexOutgoingSession( return syncSessionFactory.createSimplexOutgoingSession(
ctx.getContactId(), w.getMaxLatency(), streamWriter); ctx.getContactId(), w.getMaxLatency(), streamWriter);
} }
@@ -114,7 +112,6 @@ class ConnectionManagerImpl implements ConnectionManager {
TransportConnectionWriter w) throws IOException { TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter( StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx); w.getOutputStream(), ctx);
// TODO: Pending contacts, handshake mode
return syncSessionFactory.createDuplexOutgoingSession( return syncSessionFactory.createDuplexOutgoingSession(
ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(), ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(),
streamWriter); streamWriter);
@@ -148,7 +145,6 @@ class ConnectionManagerImpl implements ConnectionManager {
disposeReader(false, false); disposeReader(false, false);
return; return;
} }
// TODO: Pending contacts
ContactId contactId = ctx.getContactId(); ContactId contactId = ctx.getContactId();
connectionRegistry.registerConnection(contactId, transportId, true); connectionRegistry.registerConnection(contactId, transportId, true);
try { try {
@@ -392,7 +388,7 @@ class ConnectionManagerImpl implements ConnectionManager {
return; return;
} }
// Check that the stream comes from the expected contact // Check that the stream comes from the expected contact
if (!contactId.equals(ctx.getContactId())) { if (!ctx.getContactId().equals(contactId)) {
LOG.warning("Wrong contact ID for returning stream"); LOG.warning("Wrong contact ID for returning stream");
disposeReader(true, true); disposeReader(true, true);
return; return;

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
@@ -30,10 +29,7 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -42,7 +38,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -64,15 +59,11 @@ class PluginManagerImpl implements PluginManager, Service {
Logger.getLogger(PluginManagerImpl.class.getName()); Logger.getLogger(PluginManagerImpl.class.getName());
private final Executor ioExecutor; private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final EventBus eventBus; private final EventBus eventBus;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final SettingsManager settingsManager; private final SettingsManager settingsManager;
private final TransportPropertyManager transportPropertyManager; private final TransportPropertyManager transportPropertyManager;
private final SecureRandom random;
private final Clock clock;
private final Map<TransportId, Plugin> plugins; private final Map<TransportId, Plugin> plugins;
private final List<SimplexPlugin> simplexPlugins; private final List<SimplexPlugin> simplexPlugins;
private final List<DuplexPlugin> duplexPlugins; private final List<DuplexPlugin> duplexPlugins;
@@ -80,41 +71,25 @@ class PluginManagerImpl implements PluginManager, Service {
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@Inject @Inject
PluginManagerImpl(@IoExecutor Executor ioExecutor, PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus,
@Scheduler ScheduledExecutorService scheduler, EventBus eventBus,
PluginConfig pluginConfig, ConnectionManager connectionManager, PluginConfig pluginConfig, ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry,
SettingsManager settingsManager, SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager) {
SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.eventBus = eventBus; this.eventBus = eventBus;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.transportPropertyManager = transportPropertyManager; this.transportPropertyManager = transportPropertyManager;
this.random = random;
this.clock = clock;
plugins = new ConcurrentHashMap<>(); plugins = new ConcurrentHashMap<>();
simplexPlugins = new CopyOnWriteArrayList<>(); simplexPlugins = new CopyOnWriteArrayList<>();
duplexPlugins = new CopyOnWriteArrayList<>(); duplexPlugins = new CopyOnWriteArrayList<>();
startLatches = new ConcurrentHashMap<>(); startLatches = new ConcurrentHashMap<>();
} }
@Override @Override
public void startService() { public void startService() {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
// Instantiate the poller
if (pluginConfig.shouldPoll()) {
LOG.info("Starting poller");
Poller poller = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, this, transportPropertyManager, random,
clock);
eventBus.addListener(poller);
}
// Instantiate the simplex plugins and start them asynchronously // Instantiate the simplex plugins and start them asynchronously
LOG.info("Starting simplex plugins"); LOG.info("Starting simplex plugins");
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) { for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) {

View File

@@ -1,9 +1,11 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import javax.inject.Inject; import javax.inject.Inject;
@@ -18,6 +20,8 @@ public class PluginModule {
public static class EagerSingletons { public static class EagerSingletons {
@Inject @Inject
PluginManager pluginManager; PluginManager pluginManager;
@Inject
Poller poller;
} }
@Provides @Provides
@@ -46,4 +50,12 @@ public class PluginModule {
lifecycleManager.registerService(pluginManager); lifecycleManager.registerService(pluginManager);
return pluginManager; return pluginManager;
} }
@Provides
@Singleton
Poller providePoller(PluginConfig config, EventBus eventBus,
PollerImpl poller) {
if (config.shouldPoll()) eventBus.addListener(poller);
return poller;
}
} }

View File

@@ -1,259 +1,7 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId; /**
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; * Empty interface used for injecting the poller.
import org.briarproject.bramble.api.db.DbException; */
import org.briarproject.bramble.api.event.Event; interface Poller {
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@ThreadSafe
@NotNullByDefault
class Poller implements EventListener {
private static final Logger LOG = Logger.getLogger(Poller.class.getName());
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final PluginManager pluginManager;
private final TransportPropertyManager transportPropertyManager;
private final SecureRandom random;
private final Clock clock;
private final Lock lock;
private final Map<TransportId, ScheduledPollTask> tasks; // Locking: lock
Poller(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager,
TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.pluginManager = pluginManager;
this.transportPropertyManager = transportPropertyManager;
this.random = random;
this.clock = clock;
lock = new ReentrantLock();
tasks = new HashMap<>();
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactAddedEvent) {
ContactAddedEvent c = (ContactAddedEvent) e;
// Connect to the newly activated contact
connectToContact(c.getContactId());
} else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
if (!c.isIncoming()) {
// Connect to the disconnected contact
connectToContact(c.getContactId(), c.getTransportId());
}
} else if (e instanceof ConnectionOpenedEvent) {
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
} else if (e instanceof TransportEnabledEvent) {
TransportEnabledEvent t = (TransportEnabledEvent) e;
// Poll the newly enabled transport
pollNow(t.getTransportId());
} else if (e instanceof TransportDisabledEvent) {
TransportDisabledEvent t = (TransportDisabledEvent) e;
// Cancel polling for the disabled transport
cancel(t.getTransportId());
}
}
private void connectToContact(ContactId c) {
for (SimplexPlugin s : pluginManager.getSimplexPlugins())
if (s.shouldPoll()) connectToContact(c, s);
for (DuplexPlugin d : pluginManager.getDuplexPlugins())
if (d.shouldPoll()) connectToContact(c, d);
}
private void connectToContact(ContactId c, TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p instanceof SimplexPlugin && p.shouldPoll())
connectToContact(c, (SimplexPlugin) p);
else if (p instanceof DuplexPlugin && p.shouldPoll())
connectToContact(c, (DuplexPlugin) p);
}
private void connectToContact(ContactId c, SimplexPlugin p) {
ioExecutor.execute(() -> {
TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return;
try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
TransportConnectionWriter w = p.createWriter(props);
if (w != null)
connectionManager.manageOutgoingConnection(c, t, w);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void connectToContact(ContactId c, DuplexPlugin p) {
ioExecutor.execute(() -> {
TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return;
try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
DuplexTransportConnection d = p.createConnection(props);
if (d != null)
connectionManager.manageOutgoingConnection(c, t, d);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void reschedule(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p != null && p.shouldPoll())
schedule(p, p.getPollingInterval(), false);
}
private void pollNow(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
// Randomise next polling interval
if (p != null && p.shouldPoll()) schedule(p, 0, true);
}
private void schedule(Plugin p, int delay, boolean randomiseNext) {
// Replace any later scheduled task for this plugin
long due = clock.currentTimeMillis() + delay;
TransportId t = p.getId();
lock.lock();
try {
ScheduledPollTask scheduled = tasks.get(t);
if (scheduled == null || due < scheduled.task.due) {
// If a later task exists, cancel it. If it's already started
// it will abort safely when it finds it's been replaced
if (scheduled != null) scheduled.future.cancel(false);
PollTask task = new PollTask(p, due, randomiseNext);
Future future = scheduler.schedule(
() -> ioExecutor.execute(task), delay, MILLISECONDS);
tasks.put(t, new ScheduledPollTask(task, future));
}
} finally {
lock.unlock();
}
}
private void cancel(TransportId t) {
lock.lock();
try {
ScheduledPollTask scheduled = tasks.remove(t);
if (scheduled != null) scheduled.future.cancel(false);
} finally {
lock.unlock();
}
}
@IoExecutor
private void poll(Plugin p) {
TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t);
try {
Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(t);
Collection<ContactId> connected =
connectionRegistry.getConnectedContacts(t);
remote = new HashMap<>(remote);
remote.keySet().removeAll(connected);
if (!remote.isEmpty()) p.poll(remote);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
}
private class ScheduledPollTask {
private final PollTask task;
private final Future future;
private ScheduledPollTask(PollTask task, Future future) {
this.task = task;
this.future = future;
}
}
private class PollTask implements Runnable {
private final Plugin plugin;
private final long due;
private final boolean randomiseNext;
private PollTask(Plugin plugin, long due, boolean randomiseNext) {
this.plugin = plugin;
this.due = due;
this.randomiseNext = randomiseNext;
}
@Override
@IoExecutor
public void run() {
lock.lock();
try {
TransportId t = plugin.getId();
ScheduledPollTask scheduled = tasks.get(t);
if (scheduled != null && scheduled.task != this)
return; // Replaced by another task
tasks.remove(t);
} finally {
lock.unlock();
}
int delay = plugin.getPollingInterval();
if (randomiseNext) delay = (int) (delay * random.nextDouble());
schedule(plugin, delay, false);
poll(plugin);
}
}
} }

View File

@@ -0,0 +1,264 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@ThreadSafe
@NotNullByDefault
class PollerImpl implements Poller, EventListener {
private static final Logger LOG = getLogger(PollerImpl.class.getName());
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final PluginManager pluginManager;
private final TransportPropertyManager transportPropertyManager;
private final SecureRandom random;
private final Clock clock;
private final Lock lock;
@GuardedBy("lock")
private final Map<TransportId, ScheduledPollTask> tasks;
@Inject
PollerImpl(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager,
TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.pluginManager = pluginManager;
this.transportPropertyManager = transportPropertyManager;
this.random = random;
this.clock = clock;
lock = new ReentrantLock();
tasks = new HashMap<>();
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactAddedEvent) {
ContactAddedEvent c = (ContactAddedEvent) e;
// Connect to the newly added contact
connectToContact(c.getContactId());
} else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
if (!c.isIncoming()) {
// Connect to the disconnected contact
connectToContact(c.getContactId(), c.getTransportId());
}
} else if (e instanceof ConnectionOpenedEvent) {
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
} else if (e instanceof TransportEnabledEvent) {
TransportEnabledEvent t = (TransportEnabledEvent) e;
// Poll the newly enabled transport
pollNow(t.getTransportId());
} else if (e instanceof TransportDisabledEvent) {
TransportDisabledEvent t = (TransportDisabledEvent) e;
// Cancel polling for the disabled transport
cancel(t.getTransportId());
}
}
private void connectToContact(ContactId c) {
for (SimplexPlugin s : pluginManager.getSimplexPlugins())
if (s.shouldPoll()) connectToContact(c, s);
for (DuplexPlugin d : pluginManager.getDuplexPlugins())
if (d.shouldPoll()) connectToContact(c, d);
}
private void connectToContact(ContactId c, TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p instanceof SimplexPlugin && p.shouldPoll())
connectToContact(c, (SimplexPlugin) p);
else if (p instanceof DuplexPlugin && p.shouldPoll())
connectToContact(c, (DuplexPlugin) p);
}
private void connectToContact(ContactId c, SimplexPlugin p) {
ioExecutor.execute(() -> {
TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return;
try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
TransportConnectionWriter w = p.createWriter(props);
if (w != null)
connectionManager.manageOutgoingConnection(c, t, w);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void connectToContact(ContactId c, DuplexPlugin p) {
ioExecutor.execute(() -> {
TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return;
try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
DuplexTransportConnection d = p.createConnection(props);
if (d != null)
connectionManager.manageOutgoingConnection(c, t, d);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void reschedule(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p != null && p.shouldPoll())
schedule(p, p.getPollingInterval(), false);
}
private void pollNow(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
// Randomise next polling interval
if (p != null && p.shouldPoll()) schedule(p, 0, true);
}
private void schedule(Plugin p, int delay, boolean randomiseNext) {
// Replace any later scheduled task for this plugin
long due = clock.currentTimeMillis() + delay;
TransportId t = p.getId();
lock.lock();
try {
ScheduledPollTask scheduled = tasks.get(t);
if (scheduled == null || due < scheduled.task.due) {
// If a later task exists, cancel it. If it's already started
// it will abort safely when it finds it's been replaced
if (scheduled != null) scheduled.future.cancel(false);
PollTask task = new PollTask(p, due, randomiseNext);
Future future = scheduler.schedule(() ->
ioExecutor.execute(task), delay, MILLISECONDS);
tasks.put(t, new ScheduledPollTask(task, future));
}
} finally {
lock.unlock();
}
}
private void cancel(TransportId t) {
lock.lock();
try {
ScheduledPollTask scheduled = tasks.remove(t);
if (scheduled != null) scheduled.future.cancel(false);
} finally {
lock.unlock();
}
}
@IoExecutor
private void poll(Plugin p) {
TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t);
try {
Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(t);
Collection<ContactId> connected =
connectionRegistry.getConnectedContacts(t);
remote = new HashMap<>(remote);
remote.keySet().removeAll(connected);
if (!remote.isEmpty()) p.poll(remote);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
}
private class ScheduledPollTask {
private final PollTask task;
private final Future future;
private ScheduledPollTask(PollTask task, Future future) {
this.task = task;
this.future = future;
}
}
private class PollTask implements Runnable {
private final Plugin plugin;
private final long due;
private final boolean randomiseNext;
private PollTask(Plugin plugin, long due, boolean randomiseNext) {
this.plugin = plugin;
this.due = due;
this.randomiseNext = randomiseNext;
}
@Override
@IoExecutor
public void run() {
lock.lock();
try {
TransportId t = plugin.getId();
ScheduledPollTask scheduled = tasks.get(t);
if (scheduled != null && scheduled.task != this)
return; // Replaced by another task
tasks.remove(t);
} finally {
lock.unlock();
}
int delay = plugin.getPollingInterval();
if (randomiseNext) delay = (int) (delay * random.nextDouble());
schedule(plugin, delay, false);
poll(plugin);
}
}
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -18,8 +17,8 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -29,7 +28,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
@@ -90,53 +88,29 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
@Override @Override
public Map<TransportId, KeySetId> addContactWithRotationKeys( public Map<TransportId, TransportKeySetId> addContact(Transaction txn,
Transaction txn, ContactId c, SecretKey rootKey, long timestamp, ContactId c, SecretKey rootKey, long timestamp, boolean alice,
boolean alice, boolean active) throws DbException { boolean active) throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>(); Map<TransportId, TransportKeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) { for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
TransportKeyManager m = e.getValue(); TransportKeyManager m = e.getValue();
ids.put(t, m.addContactWithRotationKeys(txn, c, rootKey, timestamp, ids.put(t, m.addContact(txn, c, rootKey, timestamp, alice, active));
alice, active));
} }
return ids; return ids;
} }
@Override @Override
public Map<TransportId, KeySetId> addContactWithHandshakeKeys( public void activateKeys(Transaction txn, Map<TransportId,
Transaction txn, ContactId c, SecretKey rootKey, boolean alice) TransportKeySetId> keys) throws DbException {
throws DbException { for (Entry<TransportId, TransportKeySetId> e : keys.entrySet()) {
Map<TransportId, KeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
TransportKeyManager m = e.getValue(); TransportKeyManager m = managers.get(t);
ids.put(t, m.addContactWithHandshakeKeys(txn, c, rootKey, alice)); if (m == null) {
} if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
return ids; } else {
}
@Override
public Map<TransportId, KeySetId> addPendingContact(Transaction txn,
PendingContactId p, SecretKey rootKey, boolean alice)
throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey();
TransportKeyManager m = e.getValue();
ids.put(t, m.addPendingContact(txn, p, rootKey, alice));
}
return ids;
}
@Override
public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException {
for (Entry<TransportId, KeySetId> e : keys.entrySet()) {
withManager(e.getKey(), m -> {
m.activateKeys(txn, e.getValue()); m.activateKeys(txn, e.getValue());
return null; }
});
} }
} }
@@ -146,34 +120,28 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
return m != null && m.canSendOutgoingStreams(c); return m != null && m.canSendOutgoingStreams(c);
} }
@Override
public boolean canSendOutgoingStreams(PendingContactId p, TransportId t) {
TransportKeyManager m = managers.get(t);
return m != null && m.canSendOutgoingStreams(p);
}
@Override @Override
public StreamContext getStreamContext(ContactId c, TransportId t) public StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException { throws DbException {
return withManager(t, m -> TransportKeyManager m = managers.get(t);
db.transactionWithNullableResult(false, txn -> if (m == null) {
m.getStreamContext(txn, c))); if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
} return null;
}
@Override return db.transactionWithNullableResult(false, txn ->
public StreamContext getStreamContext(PendingContactId p, TransportId t) m.getStreamContext(txn, c));
throws DbException {
return withManager(t, m ->
db.transactionWithNullableResult(false, txn ->
m.getStreamContext(txn, p)));
} }
@Override @Override
public StreamContext getStreamContext(TransportId t, byte[] tag) public StreamContext getStreamContext(TransportId t, byte[] tag)
throws DbException { throws DbException {
return withManager(t, m -> TransportKeyManager m = managers.get(t);
db.transactionWithNullableResult(false, txn -> if (m == null) {
m.getStreamContext(txn, tag))); if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
return null;
}
return db.transactionWithNullableResult(false, txn ->
m.getStreamContext(txn, tag));
} }
@Override @Override
@@ -188,20 +156,4 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
for (TransportKeyManager m : managers.values()) m.removeContact(c); for (TransportKeyManager m : managers.values()) m.removeContact(c);
}); });
} }
@Nullable
private <T> T withManager(TransportId t, ManagerTask<T> task)
throws DbException {
TransportKeyManager m = managers.get(t);
if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
return null;
}
return task.run(m);
}
private interface ManagerTask<T> {
@Nullable
T run(TransportKeyManager m) throws DbException;
}
} }

View File

@@ -0,0 +1,30 @@
package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.transport.TransportKeySetId;
class MutableKeySet {
private final TransportKeySetId keySetId;
private final ContactId contactId;
private final MutableTransportKeys transportKeys;
MutableKeySet(TransportKeySetId keySetId, ContactId contactId,
MutableTransportKeys transportKeys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.transportKeys = transportKeys;
}
TransportKeySetId getKeySetId() {
return keySetId;
}
ContactId getContactId() {
return contactId;
}
MutableTransportKeys getTransportKeys() {
return transportKeys;
}
}

View File

@@ -1,51 +0,0 @@
package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeySetId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
@NotThreadSafe
@NotNullByDefault
class MutableTransportKeySet {
private final KeySetId keySetId;
@Nullable
private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final MutableTransportKeys keys;
MutableTransportKeySet(KeySetId keySetId, @Nullable ContactId contactId,
@Nullable PendingContactId pendingContactId,
MutableTransportKeys keys) {
requireExactlyOneNull(contactId, pendingContactId);
this.keySetId = keySetId;
this.contactId = contactId;
this.pendingContactId = pendingContactId;
this.keys = keys;
}
KeySetId getKeySetId() {
return keySetId;
}
@Nullable
ContactId getContactId() {
return contactId;
}
@Nullable
PendingContactId getPendingContactId() {
return pendingContactId;
}
MutableTransportKeys getKeys() {
return keys;
}
}

View File

@@ -1,11 +1,9 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe @NotThreadSafe
@@ -15,9 +13,6 @@ class MutableTransportKeys {
private final TransportId transportId; private final TransportId transportId;
private final MutableIncomingKeys inPrev, inCurr, inNext; private final MutableIncomingKeys inPrev, inCurr, inNext;
private final MutableOutgoingKeys outCurr; private final MutableOutgoingKeys outCurr;
@Nullable
private final SecretKey rootKey;
private final boolean alice;
MutableTransportKeys(TransportKeys k) { MutableTransportKeys(TransportKeys k) {
transportId = k.getTransportId(); transportId = k.getTransportId();
@@ -25,24 +20,11 @@ class MutableTransportKeys {
inCurr = new MutableIncomingKeys(k.getCurrentIncomingKeys()); inCurr = new MutableIncomingKeys(k.getCurrentIncomingKeys());
inNext = new MutableIncomingKeys(k.getNextIncomingKeys()); inNext = new MutableIncomingKeys(k.getNextIncomingKeys());
outCurr = new MutableOutgoingKeys(k.getCurrentOutgoingKeys()); outCurr = new MutableOutgoingKeys(k.getCurrentOutgoingKeys());
if (k.isHandshakeMode()) {
rootKey = k.getRootKey();
alice = k.isAlice();
} else {
rootKey = null;
alice = false;
}
} }
TransportKeys snapshot() { TransportKeys snapshot() {
if (rootKey == null) { return new TransportKeys(transportId, inPrev.snapshot(),
return new TransportKeys(transportId, inPrev.snapshot(), inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot());
inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot());
} else {
return new TransportKeys(transportId, inPrev.snapshot(),
inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot(),
rootKey, alice);
}
} }
TransportId getTransportId() { TransportId getTransportId() {
@@ -64,18 +46,4 @@ class MutableTransportKeys {
MutableOutgoingKeys getCurrentOutgoingKeys() { MutableOutgoingKeys getCurrentOutgoingKeys() {
return outCurr; return outCurr;
} }
boolean isHandshakeMode() {
return rootKey != null;
}
SecretKey getRootKey() {
if (rootKey == null) throw new UnsupportedOperationException();
return rootKey;
}
boolean isAlice() {
if (rootKey == null) throw new UnsupportedOperationException();
return alice;
}
} }

View File

@@ -1,13 +1,12 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -16,34 +15,20 @@ interface TransportKeyManager {
void start(Transaction txn) throws DbException; void start(Transaction txn) throws DbException;
KeySetId addContactWithRotationKeys(Transaction txn, ContactId c, TransportKeySetId addContact(Transaction txn, ContactId c,
SecretKey rootKey, long timestamp, boolean alice, boolean active) SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException; throws DbException;
KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c, void activateKeys(Transaction txn, TransportKeySetId k) throws DbException;
SecretKey rootKey, boolean alice) throws DbException;
KeySetId addPendingContact(Transaction txn, PendingContactId p,
SecretKey rootKey, boolean alice) throws DbException;
void activateKeys(Transaction txn, KeySetId k) throws DbException;
void removeContact(ContactId c); void removeContact(ContactId c);
void removePendingContact(PendingContactId p);
boolean canSendOutgoingStreams(ContactId c); boolean canSendOutgoingStreams(ContactId c);
boolean canSendOutgoingStreams(PendingContactId p);
@Nullable @Nullable
StreamContext getStreamContext(Transaction txn, ContactId c) StreamContext getStreamContext(Transaction txn, ContactId c)
throws DbException; throws DbException;
@Nullable
StreamContext getStreamContext(Transaction txn, PendingContactId p)
throws DbException;
@Nullable @Nullable
StreamContext getStreamContext(Transaction txn, byte[] tag) StreamContext getStreamContext(Transaction txn, byte[] tag)
throws DbException; throws DbException;

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -12,9 +11,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler; import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.transport.ReorderingWindow.Change; import org.briarproject.bramble.transport.ReorderingWindow.Change;
@@ -29,14 +28,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
@@ -48,7 +43,7 @@ import static org.briarproject.bramble.util.LogUtils.logException;
class TransportKeyManagerImpl implements TransportKeyManager { class TransportKeyManagerImpl implements TransportKeyManager {
private static final Logger LOG = private static final Logger LOG =
getLogger(TransportKeyManagerImpl.class.getName()); Logger.getLogger(TransportKeyManagerImpl.class.getName());
private final DatabaseComponent db; private final DatabaseComponent db;
private final TransportCrypto transportCrypto; private final TransportCrypto transportCrypto;
@@ -60,16 +55,10 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
@GuardedBy("lock") // The following are locking: lock
private final Map<KeySetId, MutableTransportKeySet> keys = new HashMap<>(); private final Map<TransportKeySetId, MutableKeySet> keys = new HashMap<>();
@GuardedBy("lock")
private final Map<Bytes, TagContext> inContexts = new HashMap<>(); private final Map<Bytes, TagContext> inContexts = new HashMap<>();
@GuardedBy("lock") private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>();
private final Map<ContactId, MutableTransportKeySet>
contactOutContexts = new HashMap<>();
@GuardedBy("lock")
private final Map<PendingContactId, MutableTransportKeySet>
pendingContactOutContexts = new HashMap<>();
TransportKeyManagerImpl(DatabaseComponent db, TransportKeyManagerImpl(DatabaseComponent db,
TransportCrypto transportCrypto, Executor dbExecutor, TransportCrypto transportCrypto, Executor dbExecutor,
@@ -93,71 +82,62 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Load the transport keys from the DB // Load the transport keys from the DB
Collection<TransportKeySet> loaded = Collection<TransportKeySet> loaded =
db.getTransportKeys(txn, transportId); db.getTransportKeys(txn, transportId);
// Update the keys to the current time period // Rotate the keys to the current time period
UpdateResult updateResult = updateKeys(loaded, now); RotationResult rotationResult = rotateKeys(loaded, now);
// Initialise mutable state for all contacts // Initialise mutable state for all contacts
addKeys(updateResult.current); addKeys(rotationResult.current);
// Write any updated keys back to the DB // Write any rotated keys back to the DB
if (!updateResult.updated.isEmpty()) if (!rotationResult.rotated.isEmpty())
db.updateTransportKeys(txn, updateResult.updated); db.updateTransportKeys(txn, rotationResult.rotated);
} finally { } finally {
lock.unlock(); lock.unlock();
} }
// Schedule the next key update // Schedule the next key rotation
scheduleKeyUpdate(now); scheduleKeyRotation(now);
} }
private UpdateResult updateKeys(Collection<TransportKeySet> keys, private RotationResult rotateKeys(Collection<TransportKeySet> keys,
long now) { long now) {
UpdateResult updateResult = new UpdateResult(); RotationResult rotationResult = new RotationResult();
long timePeriod = now / timePeriodLength; long timePeriod = now / timePeriodLength;
for (TransportKeySet ks : keys) { for (TransportKeySet ks : keys) {
TransportKeys k = ks.getKeys(); TransportKeys k = ks.getKeys();
TransportKeys k1 = transportCrypto.updateTransportKeys(k, TransportKeys k1 = transportCrypto.rotateTransportKeys(k,
timePeriod); timePeriod);
TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(), TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
ks.getContactId(), null, k1); ks.getContactId(), k1);
if (k1.getTimePeriod() > k.getTimePeriod()) if (k1.getTimePeriod() > k.getTimePeriod())
updateResult.updated.add(ks1); rotationResult.rotated.add(ks1);
updateResult.current.add(ks1); rotationResult.current.add(ks1);
} }
return updateResult; return rotationResult;
} }
@GuardedBy("lock") // Locking: lock
private void addKeys(Collection<TransportKeySet> keys) { private void addKeys(Collection<TransportKeySet> keys) {
for (TransportKeySet ks : keys) { for (TransportKeySet ks : keys) {
addKeys(ks.getKeySetId(), ks.getContactId(), addKeys(ks.getKeySetId(), ks.getContactId(),
ks.getPendingContactId(),
new MutableTransportKeys(ks.getKeys())); new MutableTransportKeys(ks.getKeys()));
} }
} }
@GuardedBy("lock") // Locking: lock
private void addKeys(KeySetId keySetId, @Nullable ContactId contactId, private void addKeys(TransportKeySetId keySetId, ContactId contactId,
@Nullable PendingContactId pendingContactId, MutableTransportKeys m) {
MutableTransportKeys keys) { MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
requireExactlyOneNull(contactId, pendingContactId); keys.put(keySetId, ks);
MutableTransportKeySet ks = new MutableTransportKeySet(keySetId, encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
contactId, pendingContactId, keys); encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
this.keys.put(keySetId, ks); encodeTags(keySetId, contactId, m.getNextIncomingKeys());
boolean handshakeMode = keys.isHandshakeMode();
encodeTags(keySetId, contactId, pendingContactId,
keys.getPreviousIncomingKeys(), handshakeMode);
encodeTags(keySetId, contactId, pendingContactId,
keys.getCurrentIncomingKeys(), handshakeMode);
encodeTags(keySetId, contactId, pendingContactId,
keys.getNextIncomingKeys(), handshakeMode);
considerReplacingOutgoingKeys(ks); considerReplacingOutgoingKeys(ks);
} }
@GuardedBy("lock") // Locking: lock
private void encodeTags(KeySetId keySetId, @Nullable ContactId contactId, private void encodeTags(TransportKeySetId keySetId, ContactId contactId,
@Nullable PendingContactId pendingContactId, MutableIncomingKeys inKeys) {
MutableIncomingKeys inKeys, boolean handshakeMode) {
for (long streamNumber : inKeys.getWindow().getUnseen()) { for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = new TagContext(keySetId, contactId, TagContext tagCtx =
pendingContactId, inKeys, streamNumber, handshakeMode); new TagContext(keySetId, contactId, inKeys, streamNumber);
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION, transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber); streamNumber);
@@ -165,41 +145,27 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
} }
@GuardedBy("lock") // Locking: lock
private void considerReplacingOutgoingKeys(MutableTransportKeySet ks) { private void considerReplacingOutgoingKeys(MutableKeySet ks) {
// Use the active outgoing keys with the highest key set ID, preferring // Use the active outgoing keys with the highest key set ID
// rotation keys to handshake keys if (ks.getTransportKeys().getCurrentOutgoingKeys().isActive()) {
if (ks.getKeys().getCurrentOutgoingKeys().isActive()) { MutableKeySet old = outContexts.get(ks.getContactId());
MutableTransportKeySet old = getOutgoingKeySet(ks.getContactId(), if (old == null ||
ks.getPendingContactId());
if (old == null || (old.getKeys().isHandshakeMode() &&
!ks.getKeys().isHandshakeMode()) ||
old.getKeySetId().getInt() < ks.getKeySetId().getInt()) { old.getKeySetId().getInt() < ks.getKeySetId().getInt()) {
if (ks.getContactId() == null) outContexts.put(ks.getContactId(), ks);
pendingContactOutContexts.put(ks.getPendingContactId(), ks);
else contactOutContexts.put(ks.getContactId(), ks);
} }
} }
} }
@GuardedBy("lock") private void scheduleKeyRotation(long now) {
@Nullable
private MutableTransportKeySet getOutgoingKeySet(@Nullable ContactId c,
@Nullable PendingContactId p) {
requireExactlyOneNull(c, p);
if (c == null) return pendingContactOutContexts.get(p);
else return contactOutContexts.get(c);
}
private void scheduleKeyUpdate(long now) {
long delay = timePeriodLength - now % timePeriodLength; long delay = timePeriodLength - now % timePeriodLength;
scheduler.schedule((Runnable) this::updateKeys, delay, MILLISECONDS); scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS);
} }
private void updateKeys() { private void rotateKeys() {
dbExecutor.execute(() -> { dbExecutor.execute(() -> {
try { try {
db.transaction(false, this::updateKeys); db.transaction(false, this::rotateKeys);
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
} }
@@ -207,7 +173,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public KeySetId addContactWithRotationKeys(Transaction txn, ContactId c, public TransportKeySetId addContact(Transaction txn, ContactId c,
SecretKey rootKey, long timestamp, boolean alice, boolean active) SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException { throws DbException {
lock.lock(); lock.lock();
@@ -215,15 +181,15 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Work out what time period the timestamp belongs to // Work out what time period the timestamp belongs to
long timePeriod = timestamp / timePeriodLength; long timePeriod = timestamp / timePeriodLength;
// Derive the transport keys // Derive the transport keys
TransportKeys k = transportCrypto.deriveRotationKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, timePeriod, alice, active); rootKey, timePeriod, alice, active);
// Update the keys to the current time period if necessary // Rotate the keys to the current time period if necessary
timePeriod = clock.currentTimeMillis() / timePeriodLength; timePeriod = clock.currentTimeMillis() / timePeriodLength;
k = transportCrypto.updateTransportKeys(k, timePeriod); k = transportCrypto.rotateTransportKeys(k, timePeriod);
// Write the keys back to the DB // Write the keys back to the DB
KeySetId keySetId = db.addTransportKeys(txn, c, k); TransportKeySetId keySetId = db.addTransportKeys(txn, c, k);
// Initialise mutable state for the contact // Initialise mutable state for the contact
addKeys(keySetId, c, null, new MutableTransportKeys(k)); addKeys(keySetId, c, new MutableTransportKeys(k));
return keySetId; return keySetId;
} finally { } finally {
lock.unlock(); lock.unlock();
@@ -231,52 +197,13 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c, public void activateKeys(Transaction txn, TransportKeySetId k)
SecretKey rootKey, boolean alice) throws DbException { throws DbException {
lock.lock(); lock.lock();
try { try {
// Work out what time period we're in MutableKeySet ks = keys.get(k);
long timePeriod = clock.currentTimeMillis() / timePeriodLength;
// Derive the transport keys
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, timePeriod, alice);
// Write the keys back to the DB
KeySetId keySetId = db.addTransportKeys(txn, c, k);
// Initialise mutable state for the contact
addKeys(keySetId, c, null, new MutableTransportKeys(k));
return keySetId;
} finally {
lock.unlock();
}
}
@Override
public KeySetId addPendingContact(Transaction txn, PendingContactId p,
SecretKey rootKey, boolean alice) throws DbException {
lock.lock();
try {
// Work out what time period we're in
long timePeriod = clock.currentTimeMillis() / timePeriodLength;
// Derive the transport keys
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, timePeriod, alice);
// Write the keys back to the DB
KeySetId keySetId = db.addTransportKeys(txn, p, k);
// Initialise mutable state for the pending contact
addKeys(keySetId, null, p, new MutableTransportKeys(k));
return keySetId;
} finally {
lock.unlock();
}
}
@Override
public void activateKeys(Transaction txn, KeySetId k) throws DbException {
lock.lock();
try {
MutableTransportKeySet ks = keys.get(k);
if (ks == null) throw new IllegalArgumentException(); if (ks == null) throw new IllegalArgumentException();
MutableTransportKeys m = ks.getKeys(); MutableTransportKeys m = ks.getTransportKeys();
m.getCurrentOutgoingKeys().activate(); m.getCurrentOutgoingKeys().activate();
considerReplacingOutgoingKeys(ks); considerReplacingOutgoingKeys(ks);
db.setTransportKeysActive(txn, m.getTransportId(), k); db.setTransportKeysActive(txn, m.getTransportId(), k);
@@ -291,29 +218,13 @@ class TransportKeyManagerImpl implements TransportKeyManager {
try { try {
// Remove mutable state for the contact // Remove mutable state for the contact
Iterator<TagContext> it = inContexts.values().iterator(); Iterator<TagContext> it = inContexts.values().iterator();
while (it.hasNext()) while (it.hasNext()) if (it.next().contactId.equals(c)) it.remove();
if (c.equals(it.next().contactId)) it.remove(); outContexts.remove(c);
contactOutContexts.remove(c); Iterator<MutableKeySet> it1 = keys.values().iterator();
Iterator<MutableTransportKeySet> it1 = keys.values().iterator(); while (it1.hasNext()) {
while (it1.hasNext()) ContactId c1 = it1.next().getContactId();
if (c.equals(it1.next().getContactId())) it1.remove(); if (c1 != null && c1.equals(c)) it1.remove();
} finally { }
lock.unlock();
}
}
@Override
public void removePendingContact(PendingContactId p) {
lock.lock();
try {
// Remove mutable state for the pending contact
Iterator<TagContext> it = inContexts.values().iterator();
while (it.hasNext())
if (p.equals(it.next().pendingContactId)) it.remove();
pendingContactOutContexts.remove(p);
Iterator<MutableTransportKeySet> it1 = keys.values().iterator();
while (it1.hasNext())
if (p.equals(it1.next().getPendingContactId())) it1.remove();
} finally { } finally {
lock.unlock(); lock.unlock();
} }
@@ -321,21 +232,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
@Override @Override
public boolean canSendOutgoingStreams(ContactId c) { public boolean canSendOutgoingStreams(ContactId c) {
return canSendOutgoingStreams(c, null);
}
@Override
public boolean canSendOutgoingStreams(PendingContactId p) {
return canSendOutgoingStreams(null, p);
}
private boolean canSendOutgoingStreams(@Nullable ContactId c,
@Nullable PendingContactId p) {
lock.lock(); lock.lock();
try { try {
MutableTransportKeySet ks = getOutgoingKeySet(c, p); MutableKeySet ks = outContexts.get(c);
if (ks == null) return false; if (ks == null) return false;
MutableOutgoingKeys outKeys = ks.getKeys().getCurrentOutgoingKeys(); MutableOutgoingKeys outKeys =
ks.getTransportKeys().getCurrentOutgoingKeys();
if (!outKeys.isActive()) throw new AssertionError(); if (!outKeys.isActive()) throw new AssertionError();
return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED; return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED;
} finally { } finally {
@@ -346,32 +248,19 @@ class TransportKeyManagerImpl implements TransportKeyManager {
@Override @Override
public StreamContext getStreamContext(Transaction txn, ContactId c) public StreamContext getStreamContext(Transaction txn, ContactId c)
throws DbException { throws DbException {
return getStreamContext(txn, c, null);
}
@Override
public StreamContext getStreamContext(Transaction txn, PendingContactId p)
throws DbException {
return getStreamContext(txn, null, p);
}
@Nullable
private StreamContext getStreamContext(Transaction txn,
@Nullable ContactId c, @Nullable PendingContactId p)
throws DbException {
lock.lock(); lock.lock();
try { try {
// Look up the outgoing keys for the contact // Look up the outgoing keys for the contact
MutableTransportKeySet ks = getOutgoingKeySet(c, p); MutableKeySet ks = outContexts.get(c);
if (ks == null) return null; if (ks == null) return null;
MutableTransportKeys keys = ks.getKeys(); MutableOutgoingKeys outKeys =
MutableOutgoingKeys outKeys = keys.getCurrentOutgoingKeys(); ks.getTransportKeys().getCurrentOutgoingKeys();
if (!outKeys.isActive()) throw new AssertionError(); if (!outKeys.isActive()) throw new AssertionError();
if (outKeys.getStreamCounter() > MAX_32_BIT_UNSIGNED) return null; if (outKeys.getStreamCounter() > MAX_32_BIT_UNSIGNED) return null;
// Create a stream context // Create a stream context
StreamContext ctx = new StreamContext(c, p, transportId, StreamContext ctx = new StreamContext(c, transportId,
outKeys.getTagKey(), outKeys.getHeaderKey(), outKeys.getTagKey(), outKeys.getHeaderKey(),
outKeys.getStreamCounter(), keys.isHandshakeMode()); outKeys.getStreamCounter());
// Increment the stream counter and write it back to the DB // Increment the stream counter and write it back to the DB
outKeys.incrementStreamCounter(); outKeys.incrementStreamCounter();
db.incrementStreamCounter(txn, transportId, ks.getKeySetId()); db.incrementStreamCounter(txn, transportId, ks.getKeySetId());
@@ -391,10 +280,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
if (tagCtx == null) return null; if (tagCtx == null) return null;
MutableIncomingKeys inKeys = tagCtx.inKeys; MutableIncomingKeys inKeys = tagCtx.inKeys;
// Create a stream context // Create a stream context
StreamContext ctx = new StreamContext(tagCtx.contactId, StreamContext ctx = new StreamContext(tagCtx.contactId, transportId,
tagCtx.pendingContactId, transportId,
inKeys.getTagKey(), inKeys.getHeaderKey(), inKeys.getTagKey(), inKeys.getHeaderKey(),
tagCtx.streamNumber, tagCtx.handshakeMode); tagCtx.streamNumber);
// Update the reordering window // Update the reordering window
ReorderingWindow window = inKeys.getWindow(); ReorderingWindow window = inKeys.getWindow();
Change change = window.setSeen(tagCtx.streamNumber); Change change = window.setSeen(tagCtx.streamNumber);
@@ -404,8 +292,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
transportCrypto.encodeTag(addTag, inKeys.getTagKey(), transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber); PROTOCOL_VERSION, streamNumber);
TagContext tagCtx1 = new TagContext(tagCtx.keySetId, TagContext tagCtx1 = new TagContext(tagCtx.keySetId,
tagCtx.contactId, tagCtx.pendingContactId, inKeys, tagCtx.contactId, inKeys, streamNumber);
streamNumber, tagCtx.handshakeMode);
inContexts.put(new Bytes(addTag), tagCtx1); inContexts.put(new Bytes(addTag), tagCtx1);
} }
// Remove tags for any stream numbers removed from the window // Remove tags for any stream numbers removed from the window
@@ -421,9 +308,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
inKeys.getTimePeriod(), window.getBase(), inKeys.getTimePeriod(), window.getBase(),
window.getBitmap()); window.getBitmap());
// If the outgoing keys are inactive, activate them // If the outgoing keys are inactive, activate them
MutableTransportKeySet ks = keys.get(tagCtx.keySetId); MutableKeySet ks = keys.get(tagCtx.keySetId);
MutableOutgoingKeys outKeys = MutableOutgoingKeys outKeys =
ks.getKeys().getCurrentOutgoingKeys(); ks.getTransportKeys().getCurrentOutgoingKeys();
if (!outKeys.isActive()) { if (!outKeys.isActive()) {
LOG.info("Activating outgoing keys"); LOG.info("Activating outgoing keys");
outKeys.activate(); outKeys.activate();
@@ -436,62 +323,51 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
} }
private void updateKeys(Transaction txn) throws DbException { private void rotateKeys(Transaction txn) throws DbException {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
lock.lock(); lock.lock();
try { try {
// Update the keys to the current time period // Rotate the keys to the current time period
Collection<TransportKeySet> snapshot = new ArrayList<>(keys.size()); Collection<TransportKeySet> snapshot = new ArrayList<>(keys.size());
for (MutableTransportKeySet ks : keys.values()) { for (MutableKeySet ks : keys.values()) {
snapshot.add(new TransportKeySet(ks.getKeySetId(), snapshot.add(new TransportKeySet(ks.getKeySetId(),
ks.getContactId(), ks.getPendingContactId(), ks.getContactId(), ks.getTransportKeys().snapshot()));
ks.getKeys().snapshot()));
} }
UpdateResult updateResult = updateKeys(snapshot, now); RotationResult rotationResult = rotateKeys(snapshot, now);
// Rebuild the mutable state for all contacts // Rebuild the mutable state for all contacts
inContexts.clear(); inContexts.clear();
contactOutContexts.clear(); outContexts.clear();
pendingContactOutContexts.clear();
keys.clear(); keys.clear();
addKeys(updateResult.current); addKeys(rotationResult.current);
// Write any updated keys back to the DB // Write any rotated keys back to the DB
if (!updateResult.updated.isEmpty()) if (!rotationResult.rotated.isEmpty())
db.updateTransportKeys(txn, updateResult.updated); db.updateTransportKeys(txn, rotationResult.rotated);
} finally { } finally {
lock.unlock(); lock.unlock();
} }
// Schedule the next key update // Schedule the next key rotation
scheduleKeyUpdate(now); scheduleKeyRotation(now);
} }
private static class TagContext { private static class TagContext {
private final KeySetId keySetId; private final TransportKeySetId keySetId;
@Nullable
private final ContactId contactId; private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final MutableIncomingKeys inKeys; private final MutableIncomingKeys inKeys;
private final long streamNumber; private final long streamNumber;
private final boolean handshakeMode;
private TagContext(KeySetId keySetId, @Nullable ContactId contactId, private TagContext(TransportKeySetId keySetId, ContactId contactId,
@Nullable PendingContactId pendingContactId, MutableIncomingKeys inKeys, long streamNumber) {
MutableIncomingKeys inKeys, long streamNumber,
boolean handshakeMode) {
requireExactlyOneNull(contactId, pendingContactId);
this.keySetId = keySetId; this.keySetId = keySetId;
this.contactId = contactId; this.contactId = contactId;
this.pendingContactId = pendingContactId;
this.inKeys = inKeys; this.inKeys = inKeys;
this.streamNumber = streamNumber; this.streamNumber = streamNumber;
this.handshakeMode = handshakeMode;
} }
} }
private static class UpdateResult { private static class RotationResult {
private final Collection<TransportKeySet> current = new ArrayList<>(); private final Collection<TransportKeySet> current = new ArrayList<>();
private final Collection<TransportKeySet> updated = new ArrayList<>(); private final Collection<TransportKeySet> rotated = new ArrayList<>();
} }
} }

View File

@@ -3,9 +3,6 @@ package org.briarproject.bramble.client;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry; import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
@@ -46,8 +43,6 @@ import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSignaturePrivateKey;
import static org.briarproject.bramble.test.TestUtils.getSignaturePublicKey;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -71,7 +66,6 @@ public class ClientHelperImplTest extends BrambleTestCase {
context.mock(CryptoComponent.class); context.mock(CryptoComponent.class);
private final AuthorFactory authorFactory = private final AuthorFactory authorFactory =
context.mock(AuthorFactory.class); context.mock(AuthorFactory.class);
private final KeyParser keyParser = context.mock(KeyParser.class);
private final GroupId groupId = new GroupId(getRandomId()); private final GroupId groupId = new GroupId(getRandomId());
private final BdfDictionary dictionary = new BdfDictionary(); private final BdfDictionary dictionary = new BdfDictionary();
@@ -268,25 +262,24 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testSign() throws Exception { public void testSign() throws Exception {
PrivateKey privateKey = getSignaturePrivateKey(); byte[] privateKey = getRandomBytes(42);
byte[] signature = getRandomBytes(42); byte[] signed = getRandomBytes(42);
byte[] bytes = expectToByteArray(list); byte[] bytes = expectToByteArray(list);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(cryptoComponent).sign(label, bytes, privateKey); oneOf(cryptoComponent).sign(label, bytes, privateKey);
will(returnValue(signature)); will(returnValue(signed));
}}); }});
assertArrayEquals(signature, assertArrayEquals(signed, clientHelper.sign(label, list, privateKey));
clientHelper.sign(label, list, privateKey));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test @Test
public void testVerifySignature() throws Exception { public void testVerifySignature() throws Exception {
byte[] signature = getRandomBytes(MAX_SIGNATURE_LENGTH); byte[] signature = getRandomBytes(MAX_SIGNATURE_LENGTH);
byte[] publicKey = getRandomBytes(42);
byte[] signed = expectToByteArray(list); byte[] signed = expectToByteArray(list);
PublicKey publicKey = getSignaturePublicKey();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(cryptoComponent).verifySignature(signature, label, signed, oneOf(cryptoComponent).verifySignature(signature, label, signed,
@@ -301,8 +294,8 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testVerifyWrongSignature() throws Exception { public void testVerifyWrongSignature() throws Exception {
byte[] signature = getRandomBytes(MAX_SIGNATURE_LENGTH); byte[] signature = getRandomBytes(MAX_SIGNATURE_LENGTH);
byte[] publicKey = getRandomBytes(42);
byte[] signed = expectToByteArray(list); byte[] signed = expectToByteArray(list);
PublicKey publicKey = getSignaturePublicKey();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(cryptoComponent).verifySignature(signature, label, signed, oneOf(cryptoComponent).verifySignature(signature, label, signed,
@@ -322,10 +315,6 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testParsesAndEncodesAuthor() throws Exception { public void testParsesAndEncodesAuthor() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(cryptoComponent).getSignatureKeyParser();
will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(author.getPublicKey().getEncoded());
will(returnValue(author.getPublicKey()));
oneOf(authorFactory).createAuthor(author.getFormatVersion(), oneOf(authorFactory).createAuthor(author.getFormatVersion(),
author.getName(), author.getPublicKey()); author.getName(), author.getPublicKey());
will(returnValue(author)); will(returnValue(author));
@@ -340,14 +329,10 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList authorList = BdfList.of( BdfList authorList = BdfList.of(
author.getFormatVersion(), author.getFormatVersion(),
author.getName(), author.getName(),
author.getPublicKey().getEncoded() author.getPublicKey()
); );
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(cryptoComponent).getSignatureKeyParser();
will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(author.getPublicKey().getEncoded());
will(returnValue(author.getPublicKey()));
oneOf(authorFactory).createAuthor(author.getFormatVersion(), oneOf(authorFactory).createAuthor(author.getFormatVersion(),
author.getName(), author.getPublicKey()); author.getName(), author.getPublicKey());
will(returnValue(author)); will(returnValue(author));
@@ -370,7 +355,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of( BdfList invalidAuthor = BdfList.of(
author.getFormatVersion(), author.getFormatVersion(),
author.getName(), author.getName(),
author.getPublicKey().getEncoded(), author.getPublicKey(),
"foo" "foo"
); );
clientHelper.parseAndValidateAuthor(invalidAuthor); clientHelper.parseAndValidateAuthor(invalidAuthor);
@@ -381,7 +366,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of( BdfList invalidAuthor = BdfList.of(
null, null,
author.getName(), author.getName(),
author.getPublicKey().getEncoded() author.getPublicKey()
); );
clientHelper.parseAndValidateAuthor(invalidAuthor); clientHelper.parseAndValidateAuthor(invalidAuthor);
} }
@@ -392,7 +377,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of( BdfList invalidAuthor = BdfList.of(
"foo", "foo",
author.getName(), author.getName(),
author.getPublicKey().getEncoded() author.getPublicKey()
); );
clientHelper.parseAndValidateAuthor(invalidAuthor); clientHelper.parseAndValidateAuthor(invalidAuthor);
} }
@@ -402,7 +387,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of( BdfList invalidAuthor = BdfList.of(
author.getFormatVersion() + 1, author.getFormatVersion() + 1,
author.getName(), author.getName(),
author.getPublicKey().getEncoded() author.getPublicKey()
); );
clientHelper.parseAndValidateAuthor(invalidAuthor); clientHelper.parseAndValidateAuthor(invalidAuthor);
} }
@@ -412,7 +397,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of( BdfList invalidAuthor = BdfList.of(
author.getFormatVersion(), author.getFormatVersion(),
"", "",
author.getPublicKey().getEncoded() author.getPublicKey()
); );
clientHelper.parseAndValidateAuthor(invalidAuthor); clientHelper.parseAndValidateAuthor(invalidAuthor);
} }
@@ -422,7 +407,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of( BdfList invalidAuthor = BdfList.of(
author.getFormatVersion(), author.getFormatVersion(),
getRandomString(MAX_AUTHOR_NAME_LENGTH + 1), getRandomString(MAX_AUTHOR_NAME_LENGTH + 1),
author.getPublicKey().getEncoded() author.getPublicKey()
); );
clientHelper.parseAndValidateAuthor(invalidAuthor); clientHelper.parseAndValidateAuthor(invalidAuthor);
} }
@@ -432,7 +417,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of( BdfList invalidAuthor = BdfList.of(
author.getFormatVersion(), author.getFormatVersion(),
null, null,
author.getPublicKey().getEncoded() author.getPublicKey()
); );
clientHelper.parseAndValidateAuthor(invalidAuthor); clientHelper.parseAndValidateAuthor(invalidAuthor);
} }
@@ -487,24 +472,6 @@ public class ClientHelperImplTest extends BrambleTestCase {
clientHelper.parseAndValidateAuthor(invalidAuthor); clientHelper.parseAndValidateAuthor(invalidAuthor);
} }
@Test(expected = FormatException.class)
public void testRejectsAuthorWithInvalidPublicKey() throws Exception {
BdfList invalidAuthor = BdfList.of(
author.getFormatVersion(),
author.getName(),
author.getPublicKey().getEncoded()
);
context.checking(new Expectations() {{
oneOf(cryptoComponent).getSignatureKeyParser();
will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(author.getPublicKey().getEncoded());
will(throwException(new GeneralSecurityException()));
}});
clientHelper.parseAndValidateAuthor(invalidAuthor);
}
private byte[] expectToByteArray(BdfList list) throws Exception { private byte[] expectToByteArray(BdfList list) throws Exception {
BdfWriter bdfWriter = context.mock(BdfWriter.class); BdfWriter bdfWriter = context.mock(BdfWriter.class);

View File

@@ -73,8 +73,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
oneOf(db).transactionWithResult(with(false), withDbCallable(txn)); oneOf(db).transactionWithResult(with(false), withDbCallable(txn));
oneOf(db).addContact(txn, remote, local, verified); oneOf(db).addContact(txn, remote, local, verified);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(keyManager).addContactWithRotationKeys(txn, contactId, oneOf(keyManager).addContact(txn, contactId, rootKey, timestamp,
rootKey, timestamp, alice, active); alice, active);
oneOf(db).getContact(txn, contactId); oneOf(db).getContact(txn, contactId);
will(returnValue(contact)); will(returnValue(contact));
}}); }});

View File

@@ -21,7 +21,7 @@ import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LAB
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES; import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION; import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
@@ -34,11 +34,12 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
private final CryptoComponent crypto = context.mock(CryptoComponent.class); private final CryptoComponent crypto = context.mock(CryptoComponent.class);
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final KeyParser keyParser = context.mock(KeyParser.class); private final KeyParser keyParser = context.mock(KeyParser.class);
private final PublicKey publicKey = context.mock(PublicKey.class);
private final PendingContactFactory pendingContactFactory = private final PendingContactFactory pendingContactFactory =
new PendingContactFactoryImpl(crypto, clock); new PendingContactFactoryImpl(crypto, clock);
private final String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH); private final String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
private final PublicKey publicKey = getAgreementPublicKey(); private final byte[] publicKeyBytes = getRandomBytes(RAW_LINK_BYTES - 1);
private final byte[] idBytes = getRandomId(); private final byte[] idBytes = getRandomId();
private final long timestamp = System.currentTimeMillis(); private final long timestamp = System.currentTimeMillis();
@@ -63,7 +64,7 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).getAgreementKeyParser(); oneOf(crypto).getAgreementKeyParser();
will(returnValue(keyParser)); will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(publicKey.getEncoded()); oneOf(keyParser).parsePublicKey(with(equal(publicKeyBytes)));
will(throwException(new GeneralSecurityException())); will(throwException(new GeneralSecurityException()));
}}); }});
@@ -94,9 +95,11 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).getAgreementKeyParser(); oneOf(crypto).getAgreementKeyParser();
will(returnValue(keyParser)); will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(publicKey.getEncoded()); oneOf(keyParser).parsePublicKey(with(equal(publicKeyBytes)));
will(returnValue(publicKey)); will(returnValue(publicKey));
oneOf(crypto).hash(ID_LABEL, publicKey.getEncoded()); allowing(publicKey).getEncoded();
will(returnValue(publicKeyBytes));
oneOf(crypto).hash(ID_LABEL, publicKeyBytes);
will(returnValue(idBytes)); will(returnValue(idBytes));
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(timestamp)); will(returnValue(timestamp));
@@ -105,8 +108,7 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
PendingContact p = PendingContact p =
pendingContactFactory.createPendingContact(link, alias); pendingContactFactory.createPendingContact(link, alias);
assertArrayEquals(idBytes, p.getId().getBytes()); assertArrayEquals(idBytes, p.getId().getBytes());
assertArrayEquals(publicKey.getEncoded(), assertArrayEquals(publicKeyBytes, p.getPublicKey());
p.getPublicKey().getEncoded());
assertEquals(alias, p.getAlias()); assertEquals(alias, p.getAlias());
assertEquals(WAITING_FOR_CONNECTION, p.getState()); assertEquals(WAITING_FOR_CONNECTION, p.getState());
assertEquals(timestamp, p.getTimestamp()); assertEquals(timestamp, p.getTimestamp());
@@ -119,7 +121,6 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
private String encodeLink(int formatVersion) { private String encodeLink(int formatVersion) {
byte[] rawLink = new byte[RAW_LINK_BYTES]; byte[] rawLink = new byte[RAW_LINK_BYTES];
rawLink[0] = (byte) formatVersion; rawLink[0] = (byte) formatVersion;
byte[] publicKeyBytes = publicKey.getEncoded();
arraycopy(publicKeyBytes, 0, rawLink, 1, publicKeyBytes.length); arraycopy(publicKeyBytes, 0, rawLink, 1, publicKeyBytes.length);
String base32 = Base32.encode(rawLink).toLowerCase(); String base32 = Base32.encode(rawLink).toLowerCase();
assertEquals(BASE32_LINK_BYTES, base32.length()); assertEquals(BASE32_LINK_BYTES, base32.length());

View File

@@ -1,10 +1,6 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
import org.junit.Test; import org.junit.Test;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
@@ -141,14 +137,14 @@ public class EdSignatureTest extends SignatureTest {
} }
@Override @Override
protected byte[] sign(String label, byte[] toSign, PrivateKey privateKey) protected byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException { throws GeneralSecurityException {
return crypto.sign(label, toSign, privateKey); return crypto.sign(label, toSign, privateKey);
} }
@Override @Override
protected boolean verify(byte[] signature, String label, byte[] signed, protected boolean verify(byte[] signature, String label, byte[] signed,
PublicKey publicKey) throws GeneralSecurityException { byte[] publicKey) throws GeneralSecurityException {
return crypto.verifySignature(signature, label, signed, publicKey); return crypto.verifySignature(signature, label, signed, publicKey);
} }
@@ -161,11 +157,11 @@ public class EdSignatureTest extends SignatureTest {
byte[] signatureBytes = fromHexString(vector[3]); byte[] signatureBytes = fromHexString(vector[3]);
EdSignature signature = new EdSignature(); EdSignature signature = new EdSignature();
signature.initSign(new SignaturePrivateKey(privateKeyBytes)); signature.initSign(new EdPrivateKey(privateKeyBytes));
signature.update(messageBytes); signature.update(messageBytes);
assertArrayEquals(signatureBytes, signature.sign()); assertArrayEquals(signatureBytes, signature.sign());
signature.initVerify(new SignaturePublicKey(publicKeyBytes)); signature.initVerify(new EdPublicKey(publicKeyBytes));
signature.update(messageBytes); signature.update(messageBytes);
assertTrue(signature.verify(signatureBytes)); assertTrue(signature.verify(signatureBytes));
} }

View File

@@ -0,0 +1,167 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.junit.Test;
import java.util.Arrays;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertAllDifferent;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertMatches;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
public class HandshakeKeyDerivationTest extends BrambleTestCase {
private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
private final TransportCrypto transportCrypto =
new TransportCryptoImpl(crypto);
private final TransportId transportId = getTransportId();
private final SecretKey rootKey = getSecretKey();
@Test
public void testKeysAreDistinct() {
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
assertAllDifferent(kA);
assertAllDifferent(kB);
}
@Test
public void testKeysAreNotUpdatedToPreviousPeriod() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 122);
assertSame(k, k1);
}
@Test
public void testKeysAreNotUpdatedToCurrentPeriod() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 123);
assertSame(k, k1);
}
@Test
public void testKeysAreUpdatedByOnePeriod() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 124);
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
}
@Test
public void testKeysAreUpdatedByTwoPeriods() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 125);
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
}
@Test
public void testKeysAreUpdatedByThreePeriods() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 126);
assertAllDifferent(k, k1);
}
@Test
public void testCurrentKeysMatchContact() {
// Start in time period 123
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
// Update into the future
kA = transportCrypto.updateHandshakeKeys(kA, 456);
kB = transportCrypto.updateHandshakeKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
}
@Test
public void testPreviousKeysMatchContact() {
// Start in time period 123
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Compare Alice's previous keys in period 456 with Bob's current keys
// in period 455
kA = transportCrypto.updateHandshakeKeys(kA, 456);
kB = transportCrypto.updateHandshakeKeys(kB, 455);
// Alice's previous incoming keys should equal Bob's current
// outgoing keys
assertMatches(kA.getPreviousIncomingKeys(),
kB.getCurrentOutgoingKeys());
// Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457
kB = transportCrypto.updateHandshakeKeys(kB, 457);
// Bob's previous incoming keys should equal Alice's current
// outgoing keys
assertMatches(kB.getPreviousIncomingKeys(),
kA.getCurrentOutgoingKeys());
}
@Test
public void testNextKeysMatchContact() {
// Start in time period 123
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Compare Alice's current keys in period 456 with Bob's next keys in
// period 455
kA = transportCrypto.updateHandshakeKeys(kA, 456);
kB = transportCrypto.updateHandshakeKeys(kB, 455);
// Bob's next incoming keys should equal Alice's current outgoing keys
assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys());
// Compare Alice's next keys in period 456 with Bob's current keys
// in period 457
kB = transportCrypto.updateHandshakeKeys(kB, 457);
// Alice's next incoming keys should equal Bob's current outgoing keys
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
}
@Test
public void testRootKeyAffectsOutput() {
SecretKey rootKey1 = getSecretKey();
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.deriveHandshakeKeys(transportId,
rootKey1, 123, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId();
assertNotEquals(transportId.getString(), transportId1.getString());
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.deriveHandshakeKeys(transportId1,
rootKey, 123, true);
assertAllDifferent(k, k1);
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
@@ -11,14 +10,12 @@ import org.junit.Test;
import org.whispersystems.curve25519.Curve25519; import org.whispersystems.curve25519.Curve25519;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Random; import java.util.Random;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.util.StringUtils.fromHexString; import static org.briarproject.bramble.util.StringUtils.fromHexString;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
public class KeyAgreementTest extends BrambleTestCase { public class KeyAgreementTest extends BrambleTestCase {
@@ -38,7 +35,6 @@ public class KeyAgreementTest extends BrambleTestCase {
private final CryptoComponent crypto = private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), null); new CryptoComponentImpl(new TestSecureRandomProvider(), null);
private final String label = getRandomString(123);
private final byte[][] inputs; private final byte[][] inputs;
public KeyAgreementTest() { public KeyAgreementTest() {
@@ -52,9 +48,9 @@ public class KeyAgreementTest extends BrambleTestCase {
public void testDerivesSharedSecret() throws Exception { public void testDerivesSharedSecret() throws Exception {
KeyPair aPair = crypto.generateAgreementKeyPair(); KeyPair aPair = crypto.generateAgreementKeyPair();
KeyPair bPair = crypto.generateAgreementKeyPair(); KeyPair bPair = crypto.generateAgreementKeyPair();
SecretKey aShared = crypto.deriveSharedSecret(label, SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
bPair.getPublic(), aPair, inputs); bPair.getPublic(), aPair, inputs);
SecretKey bShared = crypto.deriveSharedSecret(label, SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
aPair.getPublic(), bPair, inputs); aPair.getPublic(), bPair, inputs);
assertArrayEquals(aShared.getBytes(), bShared.getBytes()); assertArrayEquals(aShared.getBytes(), bShared.getBytes());
} }
@@ -62,15 +58,18 @@ public class KeyAgreementTest extends BrambleTestCase {
@Test(expected = GeneralSecurityException.class) @Test(expected = GeneralSecurityException.class)
public void testRejectsInvalidPublicKey() throws Exception { public void testRejectsInvalidPublicKey() throws Exception {
KeyPair keyPair = crypto.generateAgreementKeyPair(); KeyPair keyPair = crypto.generateAgreementKeyPair();
PublicKey invalid = new AgreementPublicKey(new byte[32]); PublicKey invalid = new Curve25519PublicKey(new byte[32]);
crypto.deriveSharedSecret(label, invalid, keyPair, inputs); crypto.deriveSharedSecret(SHARED_SECRET_LABEL, invalid, keyPair,
inputs);
} }
@Test @Test
public void testRfc7748TestVector() { public void testRfc7748TestVector() throws Exception {
byte[] aPriv = parsePrivateKey(ALICE_PRIVATE); // Private keys need to be clamped because curve25519-java does the
// clamping at key generation time, not multiplication time
byte[] aPriv = Curve25519KeyParser.clamp(fromHexString(ALICE_PRIVATE));
byte[] aPub = fromHexString(ALICE_PUBLIC); byte[] aPub = fromHexString(ALICE_PUBLIC);
byte[] bPriv = parsePrivateKey(BOB_PRIVATE); byte[] bPriv = Curve25519KeyParser.clamp(fromHexString(BOB_PRIVATE));
byte[] bPub = fromHexString(BOB_PUBLIC); byte[] bPub = fromHexString(BOB_PUBLIC);
byte[] sharedSecret = fromHexString(SHARED_SECRET); byte[] sharedSecret = fromHexString(SHARED_SECRET);
Curve25519 curve25519 = Curve25519.getInstance("java"); Curve25519 curve25519 = Curve25519.getInstance("java");
@@ -79,82 +78,4 @@ public class KeyAgreementTest extends BrambleTestCase {
assertArrayEquals(sharedSecret, assertArrayEquals(sharedSecret,
curve25519.calculateAgreement(bPub, aPriv)); curve25519.calculateAgreement(bPub, aPriv));
} }
@Test
public void testDerivesSameSharedSecretFromEquivalentPublicKey() {
byte[] aPub = fromHexString(ALICE_PUBLIC);
byte[] bPriv = parsePrivateKey(BOB_PRIVATE);
byte[] sharedSecret = fromHexString(SHARED_SECRET);
Curve25519 curve25519 = Curve25519.getInstance("java");
// Flip the unused most significant bit of the little-endian public key
byte[] aPubEquiv = aPub.clone();
aPubEquiv[31] ^= (byte) 128;
// The public keys should be different but give the same shared secret
assertFalse(Arrays.equals(aPub, aPubEquiv));
assertArrayEquals(sharedSecret,
curve25519.calculateAgreement(aPub, bPriv));
assertArrayEquals(sharedSecret,
curve25519.calculateAgreement(aPubEquiv, bPriv));
}
@Test
public void testDerivesSameSharedSecretFromEquivalentPublicKeyWithoutPublicKeysHashedIn()
throws Exception {
KeyPair aPair = crypto.generateAgreementKeyPair();
KeyPair bPair = crypto.generateAgreementKeyPair();
// Flip the unused most significant bit of the little-endian public key
byte[] aPub = aPair.getPublic().getEncoded();
byte[] aPubEquiv = aPub.clone();
aPubEquiv[31] ^= (byte) 128;
KeyPair aPairEquiv = new KeyPair(new AgreementPublicKey(aPubEquiv),
aPair.getPrivate());
// The public keys should be different but give the same shared secret
assertFalse(Arrays.equals(aPub, aPubEquiv));
SecretKey shared = crypto.deriveSharedSecret(label,
aPair.getPublic(), bPair);
SecretKey sharedEquiv = crypto.deriveSharedSecret(label,
aPairEquiv.getPublic(), bPair);
assertArrayEquals(shared.getBytes(), sharedEquiv.getBytes());
}
@Test
public void testDerivesDifferentSharedSecretFromEquivalentPublicKeyWithPublicKeysHashedIn()
throws Exception {
KeyPair aPair = crypto.generateAgreementKeyPair();
KeyPair bPair = crypto.generateAgreementKeyPair();
// Flip the unused most significant bit of the little-endian public key
byte[] aPub = aPair.getPublic().getEncoded();
byte[] aPubEquiv = aPub.clone();
aPubEquiv[31] ^= (byte) 128;
KeyPair aPairEquiv = new KeyPair(new AgreementPublicKey(aPubEquiv),
aPair.getPrivate());
// The public keys should be different and give different shared secrets
assertFalse(Arrays.equals(aPub, aPubEquiv));
SecretKey shared = deriveSharedSecretWithPublicKeysHashedIn(label,
aPair.getPublic(), bPair);
SecretKey sharedEquiv = deriveSharedSecretWithPublicKeysHashedIn(label,
aPairEquiv.getPublic(), bPair);
assertFalse(Arrays.equals(shared.getBytes(), sharedEquiv.getBytes()));
}
private SecretKey deriveSharedSecretWithPublicKeysHashedIn(String label,
PublicKey publicKey, KeyPair keyPair) throws Exception {
byte[][] inputs = new byte[][] {
publicKey.getEncoded(),
keyPair.getPublic().getEncoded()
};
return crypto.deriveSharedSecret(label, publicKey, keyPair, inputs);
}
private byte[] parsePrivateKey(String hex) {
// Private keys need to be clamped because curve25519-java does the
// clamping at key generation time, not multiplication time
return AgreementKeyParser.clamp(fromHexString(hex));
}
} }

View File

@@ -0,0 +1,45 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.transport.AbstractTransportKeys;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
class KeyDerivationTestUtils {
static void assertAllDifferent(AbstractTransportKeys... transportKeys) {
List<SecretKey> secretKeys = new ArrayList<>();
for (AbstractTransportKeys k : transportKeys) {
secretKeys.add(k.getPreviousIncomingKeys().getTagKey());
secretKeys.add(k.getPreviousIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentIncomingKeys().getTagKey());
secretKeys.add(k.getCurrentIncomingKeys().getHeaderKey());
secretKeys.add(k.getNextIncomingKeys().getTagKey());
secretKeys.add(k.getNextIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentOutgoingKeys().getTagKey());
secretKeys.add(k.getCurrentOutgoingKeys().getHeaderKey());
}
assertAllDifferent(secretKeys);
}
static void assertAllDifferent(List<SecretKey> keys) {
Set<Bytes> set = new HashSet<>();
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
}
static void assertMatches(IncomingKeys in, OutgoingKeys out) {
assertArrayEquals(in.getTagKey().getBytes(),
out.getTagKey().getBytes());
assertArrayEquals(in.getHeaderKey().getBytes(),
out.getHeaderKey().getBytes());
}
}

View File

@@ -23,7 +23,7 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
new CryptoComponentImpl(new TestSecureRandomProvider(), null); new CryptoComponentImpl(new TestSecureRandomProvider(), null);
@Test @Test
public void testAgreementPublicKeyLength() { public void testAgreementPublicKeyLength() throws Exception {
// Generate 10 agreement key pairs // Generate 10 agreement key pairs
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
KeyPair keyPair = crypto.generateAgreementKeyPair(); KeyPair keyPair = crypto.generateAgreementKeyPair();
@@ -70,7 +70,7 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
} }
@Test @Test
public void testAgreementKeyParserByFuzzing() { public void testAgreementKeyParserByFuzzing() throws Exception {
KeyParser parser = crypto.getAgreementKeyParser(); KeyParser parser = crypto.getAgreementKeyParser();
// Generate a key pair to get the proper public key length // Generate a key pair to get the proper public key length
KeyPair p = crypto.generateAgreementKeyPair(); KeyPair p = crypto.generateAgreementKeyPair();
@@ -92,7 +92,7 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
} }
@Test @Test
public void testSignaturePublicKeyLength() { public void testSignaturePublicKeyLength() throws Exception {
// Generate 10 signature key pairs // Generate 10 signature key pairs
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
KeyPair keyPair = crypto.generateSignatureKeyPair(); KeyPair keyPair = crypto.generateSignatureKeyPair();
@@ -107,10 +107,10 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
// Generate 10 signature key pairs // Generate 10 signature key pairs
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
KeyPair keyPair = crypto.generateSignatureKeyPair(); KeyPair keyPair = crypto.generateSignatureKeyPair();
PrivateKey privateKey = keyPair.getPrivate(); byte[] key = keyPair.getPrivate().getEncoded();
// Sign some random data and check the length of the signature // Sign some random data and check the length of the signature
byte[] toBeSigned = getRandomBytes(1234); byte[] toBeSigned = getRandomBytes(1234);
byte[] signature = crypto.sign("label", toBeSigned, privateKey); byte[] signature = crypto.sign("label", toBeSigned, key);
assertTrue(signature.length <= MAX_SIGNATURE_BYTES); assertTrue(signature.length <= MAX_SIGNATURE_BYTES);
} }
} }
@@ -123,15 +123,16 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
PublicKey publicKey = keyPair.getPublic(); PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate(); PrivateKey privateKey = keyPair.getPrivate();
byte[] message = getRandomBytes(123); byte[] message = getRandomBytes(123);
byte[] signature = crypto.sign("test", message, privateKey); byte[] signature = crypto.sign("test", message,
privateKey.getEncoded());
// Verify the signature // Verify the signature
assertTrue(crypto.verifySignature(signature, "test", message, assertTrue(crypto.verifySignature(signature, "test", message,
publicKey)); publicKey.getEncoded()));
// Encode and parse the public key - no exceptions should be thrown // Encode and parse the public key - no exceptions should be thrown
publicKey = parser.parsePublicKey(publicKey.getEncoded()); publicKey = parser.parsePublicKey(publicKey.getEncoded());
// Verify the signature again // Verify the signature again
assertTrue(crypto.verifySignature(signature, "test", message, assertTrue(crypto.verifySignature(signature, "test", message,
publicKey)); publicKey.getEncoded()));
} }
@Test @Test
@@ -142,21 +143,23 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
PublicKey publicKey = keyPair.getPublic(); PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate(); PrivateKey privateKey = keyPair.getPrivate();
byte[] message = getRandomBytes(123); byte[] message = getRandomBytes(123);
byte[] signature = crypto.sign("test", message, privateKey); byte[] signature = crypto.sign("test", message,
privateKey.getEncoded());
// Verify the signature // Verify the signature
assertTrue(crypto.verifySignature(signature, "test", message, assertTrue(crypto.verifySignature(signature, "test", message,
publicKey)); publicKey.getEncoded()));
// Encode and parse the private key - no exceptions should be thrown // Encode and parse the private key - no exceptions should be thrown
privateKey = parser.parsePrivateKey(privateKey.getEncoded()); privateKey = parser.parsePrivateKey(privateKey.getEncoded());
// Sign the data again - the signatures should be the same // Sign the data again - the signatures should be the same
byte[] signature1 = crypto.sign("test", message, privateKey); byte[] signature1 = crypto.sign("test", message,
privateKey.getEncoded());
assertTrue(crypto.verifySignature(signature1, "test", message, assertTrue(crypto.verifySignature(signature1, "test", message,
publicKey)); publicKey.getEncoded()));
assertArrayEquals(signature, signature1); assertArrayEquals(signature, signature1);
} }
@Test @Test
public void testSignatureKeyParserByFuzzing() { public void testSignatureKeyParserByFuzzing() throws Exception {
KeyParser parser = crypto.getSignatureKeyParser(); KeyParser parser = crypto.getSignatureKeyParser();
// Generate a key pair to get the proper public key length // Generate a key pair to get the proper public key length
KeyPair p = crypto.generateSignatureKeyPair(); KeyPair p = crypto.generateSignatureKeyPair();

View File

@@ -2,8 +2,6 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider; import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
@@ -21,24 +19,23 @@ public abstract class SignatureTest extends BrambleTestCase {
protected final CryptoComponent crypto; protected final CryptoComponent crypto;
private final PublicKey publicKey; private final byte[] publicKey, privateKey;
private final PrivateKey privateKey;
private final String label = StringUtils.getRandomString(42); private final String label = StringUtils.getRandomString(42);
private final byte[] inputBytes = TestUtils.getRandomBytes(123); private final byte[] inputBytes = TestUtils.getRandomBytes(123);
protected abstract KeyPair generateKeyPair(); protected abstract KeyPair generateKeyPair();
protected abstract byte[] sign(String label, byte[] toSign, protected abstract byte[] sign(String label, byte[] toSign,
PrivateKey privateKey) throws GeneralSecurityException; byte[] privateKey) throws GeneralSecurityException;
protected abstract boolean verify(byte[] signature, String label, protected abstract boolean verify(byte[] signature, String label,
byte[] signed, PublicKey publicKey) throws GeneralSecurityException; byte[] signed, byte[] publicKey) throws GeneralSecurityException;
SignatureTest() { SignatureTest() {
crypto = new CryptoComponentImpl(new TestSecureRandomProvider(), null); crypto = new CryptoComponentImpl(new TestSecureRandomProvider(), null);
KeyPair k = generateKeyPair(); KeyPair k = generateKeyPair();
publicKey = k.getPublic(); publicKey = k.getPublic().getEncoded();
privateKey = k.getPrivate(); privateKey = k.getPrivate().getEncoded();
} }
@Test @Test
@@ -54,7 +51,7 @@ public abstract class SignatureTest extends BrambleTestCase {
public void testDifferentKeysProduceDifferentSignatures() throws Exception { public void testDifferentKeysProduceDifferentSignatures() throws Exception {
// Generate second private key // Generate second private key
KeyPair k2 = generateKeyPair(); KeyPair k2 = generateKeyPair();
PrivateKey privateKey2 = k2.getPrivate(); byte[] privateKey2 = k2.getPrivate().getEncoded();
// Calculate the signature with each key // Calculate the signature with each key
byte[] sig1 = sign(label, inputBytes, privateKey); byte[] sig1 = sign(label, inputBytes, privateKey);
byte[] sig2 = sign(label, inputBytes, privateKey2); byte[] sig2 = sign(label, inputBytes, privateKey2);
@@ -95,7 +92,7 @@ public abstract class SignatureTest extends BrambleTestCase {
public void testDifferentKeyFailsVerification() throws Exception { public void testDifferentKeyFailsVerification() throws Exception {
// Generate second private key // Generate second private key
KeyPair k2 = generateKeyPair(); KeyPair k2 = generateKeyPair();
PrivateKey privateKey2 = k2.getPrivate(); byte[] privateKey2 = k2.getPrivate().getEncoded();
// calculate the signature with different key, should fail to verify // calculate the signature with different key, should fail to verify
byte[] sig = sign(label, inputBytes, privateKey2); byte[] sig = sign(label, inputBytes, privateKey2);
assertFalse(verify(sig, label, inputBytes, publicKey)); assertFalse(verify(sig, label, inputBytes, publicKey));

View File

@@ -1,30 +1,23 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider; import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertAllDifferent;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertMatches;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
public class TransportKeyDerivationTest extends BrambleTestCase { public class TransportKeyDerivationTest extends BrambleTestCase {
@@ -36,70 +29,70 @@ public class TransportKeyDerivationTest extends BrambleTestCase {
private final SecretKey rootKey = getSecretKey(); private final SecretKey rootKey = getSecretKey();
@Test @Test
public void testRotationKeysAreDistinct() { public void testKeysAreDistinct() {
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId, TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId, TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true); rootKey, 123, false, true);
assertAllDifferent(kA); assertAllDifferent(kA);
assertAllDifferent(kB); assertAllDifferent(kB);
} }
@Test @Test
public void testRotationKeysAreNotRotatedToPreviousPeriod() { public void testKeysAreNotRotatedToPreviousPeriod() {
TransportKeys k = transportCrypto.deriveRotationKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 122); TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 122);
assertSame(k, k1); assertSame(k, k1);
} }
@Test @Test
public void testRotationKeysAreNotRotatedToCurrentPeriod() { public void testKeysAreNotRotatedToCurrentPeriod() {
TransportKeys k = transportCrypto.deriveRotationKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 123); TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 123);
assertSame(k, k1); assertSame(k, k1);
} }
@Test @Test
public void testRotationKeysAreRotatedByOnePeriod() { public void testKeysAreRotatedByOnePeriod() {
TransportKeys k = transportCrypto.deriveRotationKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 124); TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 124);
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys()); assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys()); assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
} }
@Test @Test
public void testRotationKeysAreRotatedByTwoPeriods() { public void testKeysAreRotatedByTwoPeriods() {
TransportKeys k = transportCrypto.deriveRotationKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 125); TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 125);
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys()); assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
} }
@Test @Test
public void testRotationKeysAreRotatedByThreePeriods() { public void testKeysAreRotatedByThreePeriods() {
TransportKeys k = transportCrypto.deriveRotationKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 126); TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 126);
assertAllDifferent(k, k1); assertAllDifferent(k, k1);
} }
@Test @Test
public void testCurrentRotationKeysMatchContact() { public void testCurrentKeysMatchContact() {
// Start in time period 123 // Start in time period 123
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId, TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId, TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true); rootKey, 123, false, true);
// Alice's incoming keys should equal Bob's outgoing keys // Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys()); assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys // Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys()); assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
// Rotate into the future // Rotate into the future
kA = transportCrypto.updateTransportKeys(kA, 456); kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.updateTransportKeys(kB, 456); kB = transportCrypto.rotateTransportKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys // Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys()); assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys // Bob's incoming keys should equal Alice's outgoing keys
@@ -107,23 +100,23 @@ public class TransportKeyDerivationTest extends BrambleTestCase {
} }
@Test @Test
public void testPreviousRotationKeysMatchContact() { public void testPreviousKeysMatchContact() {
// Start in time period 123 // Start in time period 123
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId, TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId, TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true); rootKey, 123, false, true);
// Compare Alice's previous keys in period 456 with Bob's current keys // Compare Alice's previous keys in period 456 with Bob's current keys
// in period 455 // in period 455
kA = transportCrypto.updateTransportKeys(kA, 456); kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.updateTransportKeys(kB, 455); kB = transportCrypto.rotateTransportKeys(kB, 455);
// Alice's previous incoming keys should equal Bob's current // Alice's previous incoming keys should equal Bob's current
// outgoing keys // outgoing keys
assertMatches(kA.getPreviousIncomingKeys(), assertMatches(kA.getPreviousIncomingKeys(),
kB.getCurrentOutgoingKeys()); kB.getCurrentOutgoingKeys());
// Compare Alice's current keys in period 456 with Bob's previous keys // Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457 // in period 457
kB = transportCrypto.updateTransportKeys(kB, 457); kB = transportCrypto.rotateTransportKeys(kB, 457);
// Bob's previous incoming keys should equal Alice's current // Bob's previous incoming keys should equal Alice's current
// outgoing keys // outgoing keys
assertMatches(kB.getPreviousIncomingKeys(), assertMatches(kB.getPreviousIncomingKeys(),
@@ -131,208 +124,44 @@ public class TransportKeyDerivationTest extends BrambleTestCase {
} }
@Test @Test
public void testNextRotationKeysMatchContact() { public void testNextKeysMatchContact() {
// Start in time period 123 // Start in time period 123
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId, TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId, TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true); rootKey, 123, false, true);
// Compare Alice's current keys in period 456 with Bob's next keys in // Compare Alice's current keys in period 456 with Bob's next keys in
// period 455 // period 455
kA = transportCrypto.updateTransportKeys(kA, 456); kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.updateTransportKeys(kB, 455); kB = transportCrypto.rotateTransportKeys(kB, 455);
// Bob's next incoming keys should equal Alice's current outgoing keys // Bob's next incoming keys should equal Alice's current outgoing keys
assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys()); assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys());
// Compare Alice's next keys in period 456 with Bob's current keys // Compare Alice's next keys in period 456 with Bob's current keys
// in period 457 // in period 457
kB = transportCrypto.updateTransportKeys(kB, 457); kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's next incoming keys should equal Bob's current outgoing keys // Alice's next incoming keys should equal Bob's current outgoing keys
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys()); assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
} }
@Test @Test
public void testRootKeyAffectsRotationKeyDerivation() { public void testRootKeyAffectsOutput() {
SecretKey rootKey1 = getSecretKey(); SecretKey rootKey1 = getSecretKey();
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes())); assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
TransportKeys k = transportCrypto.deriveRotationKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.deriveRotationKeys(transportId, TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
rootKey1, 123, true, true); rootKey1, 123, true, true);
assertAllDifferent(k, k1); assertAllDifferent(k, k1);
} }
@Test @Test
public void testTransportIdAffectsRotationKeyDerivation() { public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId(); TransportId transportId1 = getTransportId();
assertNotEquals(transportId.getString(), transportId1.getString()); assertNotEquals(transportId.getString(), transportId1.getString());
TransportKeys k = transportCrypto.deriveRotationKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true); rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.deriveRotationKeys(transportId1, TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
rootKey, 123, true, true); rootKey, 123, true, true);
assertAllDifferent(k, k1); assertAllDifferent(k, k1);
} }
@Test
public void testHandshakeKeysAreDistinct() {
TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
assertAllDifferent(kA);
assertAllDifferent(kB);
}
@Test
public void testHandshakeKeysAreNotUpdatedToPreviousPeriod() {
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 122);
assertSame(k, k1);
}
@Test
public void testHandshakeKeysAreNotUpdatedToCurrentPeriod() {
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 123);
assertSame(k, k1);
}
@Test
public void testHandshakeKeysAreUpdatedByOnePeriod() {
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 124);
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
}
@Test
public void testHandshakeKeysAreUpdatedByTwoPeriods() {
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 125);
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
}
@Test
public void testHandshakeKeysAreUpdatedByThreePeriods() {
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 126);
assertAllDifferent(k, k1);
}
@Test
public void testCurrentHandshakeKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
// Update into the future
kA = transportCrypto.updateTransportKeys(kA, 456);
kB = transportCrypto.updateTransportKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
}
@Test
public void testPreviousHandshakeKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Compare Alice's previous keys in period 456 with Bob's current keys
// in period 455
kA = transportCrypto.updateTransportKeys(kA, 456);
kB = transportCrypto.updateTransportKeys(kB, 455);
// Alice's previous incoming keys should equal Bob's current
// outgoing keys
assertMatches(kA.getPreviousIncomingKeys(),
kB.getCurrentOutgoingKeys());
// Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457
kB = transportCrypto.updateTransportKeys(kB, 457);
// Bob's previous incoming keys should equal Alice's current
// outgoing keys
assertMatches(kB.getPreviousIncomingKeys(),
kA.getCurrentOutgoingKeys());
}
@Test
public void testNextHandshakeKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Compare Alice's current keys in period 456 with Bob's next keys in
// period 455
kA = transportCrypto.updateTransportKeys(kA, 456);
kB = transportCrypto.updateTransportKeys(kB, 455);
// Bob's next incoming keys should equal Alice's current outgoing keys
assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys());
// Compare Alice's next keys in period 456 with Bob's current keys
// in period 457
kB = transportCrypto.updateTransportKeys(kB, 457);
// Alice's next incoming keys should equal Bob's current outgoing keys
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
}
@Test
public void testRootKeyAffectsHandshakeKeyDerivation() {
SecretKey rootKey1 = getSecretKey();
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys k1 = transportCrypto.deriveHandshakeKeys(transportId,
rootKey1, 123, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsHandshakeKeyDerivation() {
TransportId transportId1 = getTransportId();
assertNotEquals(transportId.getString(), transportId1.getString());
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
TransportKeys k1 = transportCrypto.deriveHandshakeKeys(transportId1,
rootKey, 123, true);
assertAllDifferent(k, k1);
}
private void assertAllDifferent(TransportKeys... transportKeys) {
List<SecretKey> secretKeys = new ArrayList<>();
for (TransportKeys k : transportKeys) {
secretKeys.add(k.getPreviousIncomingKeys().getTagKey());
secretKeys.add(k.getPreviousIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentIncomingKeys().getTagKey());
secretKeys.add(k.getCurrentIncomingKeys().getHeaderKey());
secretKeys.add(k.getNextIncomingKeys().getTagKey());
secretKeys.add(k.getNextIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentOutgoingKeys().getTagKey());
secretKeys.add(k.getCurrentOutgoingKeys().getHeaderKey());
}
assertAllDifferent(secretKeys);
}
private void assertAllDifferent(List<SecretKey> keys) {
Set<Bytes> set = new HashSet<>();
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
}
private void assertMatches(IncomingKeys in, OutgoingKeys out) {
assertArrayEquals(in.getTagKey().getBytes(),
out.getTagKey().getBytes());
assertArrayEquals(in.getHeaderKey().getBytes(),
out.getHeaderKey().getBytes());
}
} }

View File

@@ -5,8 +5,6 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -48,10 +46,11 @@ import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent; import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent; import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.CaptureArgumentAction; import org.briarproject.bramble.test.CaptureArgumentAction;
@@ -67,6 +66,7 @@ import java.util.concurrent.atomic.AtomicReference;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
@@ -75,14 +75,13 @@ import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERE
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN; import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE; import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES; import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getContact; import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getIdentity; import static org.briarproject.bramble.test.TestUtils.getIdentity;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -118,7 +117,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final int maxLatency; private final int maxLatency;
private final ContactId contactId; private final ContactId contactId;
private final Contact contact; private final Contact contact;
private final KeySetId keySetId; private final TransportKeySetId keySetId;
private final PendingContactId pendingContactId; private final PendingContactId pendingContactId;
public DatabaseComponentImplTest() { public DatabaseComponentImplTest() {
@@ -140,7 +139,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
contact = getContact(author, localAuthor.getId(), true); contact = getContact(author, localAuthor.getId(), true);
contactId = contact.getId(); contactId = contact.getId();
alias = contact.getAlias(); alias = contact.getAlias();
keySetId = new KeySetId(345); keySetId = new TransportKeySetId(345);
pendingContactId = new PendingContactId(getRandomId()); pendingContactId = new PendingContactId(getRandomId());
} }
@@ -285,15 +284,24 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not) // Check whether the contact is in the DB (which it's not)
exactly(16).of(database).startTransaction(); exactly(17).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(16).of(database).containsContact(txn, contactId); exactly(17).of(database).containsContact(txn, contactId);
will(returnValue(false)); will(returnValue(false));
exactly(16).of(database).abortTransaction(txn); exactly(17).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addHandshakeKeys(transaction, contactId,
createHandshakeKeys()));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addTransportKeys(transaction, contactId, db.addTransportKeys(transaction, contactId,
@@ -468,8 +476,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} }
try { try {
PublicKey publicKey = getAgreementPublicKey(); byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
PrivateKey privateKey = getAgreementPrivateKey(); byte[] privateKey = getRandomBytes(123);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.setHandshakeKeyPair(transaction, localAuthor.getId(), db.setHandshakeKeyPair(transaction, localAuthor.getId(),
publicKey, privateKey)); publicKey, privateKey));
@@ -489,8 +497,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
exactly(8).of(database).containsGroup(txn, groupId); exactly(8).of(database).containsGroup(txn, groupId);
will(returnValue(false)); will(returnValue(false));
exactly(8).of(database).abortTransaction(txn); exactly(8).of(database).abortTransaction(txn);
// Allow other checks to pass // This is needed for getMessageStatus() and setGroupVisibility()
allowing(database).containsContact(txn, contactId); exactly(2).of(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
@@ -573,8 +581,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
exactly(11).of(database).containsMessage(txn, messageId); exactly(11).of(database).containsMessage(txn, messageId);
will(returnValue(false)); will(returnValue(false));
exactly(11).of(database).abortTransaction(txn); exactly(11).of(database).abortTransaction(txn);
// Allow other checks to pass // This is needed for getMessageStatus() to proceed
allowing(database).containsContact(txn, contactId); exactly(1).of(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
@@ -674,38 +682,15 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the transport is in the DB (which it's not) // Check whether the transport is in the DB (which it's not)
exactly(8).of(database).startTransaction(); exactly(5).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(8).of(database).containsTransport(txn, transportId); exactly(5).of(database).containsTransport(txn, transportId);
will(returnValue(false)); will(returnValue(false));
exactly(8).of(database).abortTransaction(txn); exactly(5).of(database).abortTransaction(txn);
// Allow other checks to pass
allowing(database).containsContact(txn, contactId);
will(returnValue(true));
allowing(database).containsPendingContact(txn, pendingContactId);
will(returnValue(true));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addTransportKeys(transaction, contactId,
createHandshakeKeys()));
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
try {
db.transaction(false, transaction ->
db.addTransportKeys(transaction, pendingContactId,
createHandshakeKeys()));
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.getTransportKeys(transaction, transportId)); db.getTransportKeys(transaction, transportId));
@@ -725,7 +710,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.removeTransportKeys(transaction, transportId, keySetId)); db.removeTransport(transaction, transportId));
fail(); fail();
} catch (NoSuchTransportException expected) { } catch (NoSuchTransportException expected) {
// Expected // Expected
@@ -733,7 +718,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.removeTransport(transaction, transportId)); db.removeTransportKeys(transaction, transportId, keySetId));
fail(); fail();
} catch (NoSuchTransportException expected) { } catch (NoSuchTransportException expected) {
// Expected // Expected
@@ -747,15 +732,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} catch (NoSuchTransportException expected) { } catch (NoSuchTransportException expected) {
// Expected // Expected
} }
try {
db.transaction(false, transaction ->
db.setTransportKeysActive(transaction, transportId,
keySetId));
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
} }
@Test @Test
@@ -775,7 +751,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addTransportKeys(transaction, pendingContactId, db.addHandshakeKeys(transaction, pendingContactId,
createHandshakeKeys())); createHandshakeKeys()));
fail(); fail();
} catch (NoSuchPendingContactException expected) { } catch (NoSuchPendingContactException expected) {
@@ -1191,7 +1167,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
public void testTransportKeys() throws Exception { public void testTransportKeys() throws Exception {
TransportKeys transportKeys = createTransportKeys(); TransportKeys transportKeys = createTransportKeys();
TransportKeySet ks = TransportKeySet ks =
new TransportKeySet(keySetId, contactId, null, transportKeys); new TransportKeySet(keySetId, contactId, transportKeys);
Collection<TransportKeySet> keys = singletonList(ks); Collection<TransportKeySet> keys = singletonList(ks);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -1319,7 +1295,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}); });
} }
private TransportKeys createHandshakeKeys() { private HandshakeKeys createHandshakeKeys() {
SecretKey inPrevTagKey = getSecretKey(); SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey, IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
@@ -1336,7 +1312,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
SecretKey outCurrHeaderKey = getSecretKey(); SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey, OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
2, 456, true); 2, 456, true);
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr, return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
getSecretKey(), true); getSecretKey(), true);
} }

View File

@@ -3,8 +3,6 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -24,10 +22,13 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.system.SystemClock; import org.briarproject.bramble.system.SystemClock;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
@@ -57,6 +58,7 @@ import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED; import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.db.Metadata.REMOVE; import static org.briarproject.bramble.api.db.Metadata.REMOVE;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
@@ -70,14 +72,13 @@ import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPAC
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY; import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS; import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getIdentity; import static org.briarproject.bramble.test.TestUtils.getIdentity;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getPendingContact; import static org.briarproject.bramble.test.TestUtils.getPendingContact;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
@@ -111,7 +112,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private final MessageId messageId; private final MessageId messageId;
private final TransportId transportId; private final TransportId transportId;
private final ContactId contactId; private final ContactId contactId;
private final KeySetId keySetId, keySetId1; private final TransportKeySetId keySetId, keySetId1;
private final HandshakeKeySetId handshakeKeySetId, handshakeKeySetId1;
private final PendingContact pendingContact; private final PendingContact pendingContact;
private final Random random = new Random(); private final Random random = new Random();
@@ -127,8 +129,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
messageId = message.getId(); messageId = message.getId();
transportId = getTransportId(); transportId = getTransportId();
contactId = new ContactId(1); contactId = new ContactId(1);
keySetId = new KeySetId(1); keySetId = new TransportKeySetId(1);
keySetId1 = new KeySetId(2); keySetId1 = new TransportKeySetId(2);
handshakeKeySetId = new HandshakeKeySetId(1);
handshakeKeySetId1 = new HandshakeKeySetId(2);
pendingContact = getPendingContact(); pendingContact = getPendingContact();
} }
@@ -697,14 +701,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
} }
} }
// Update the transport keys // Rotate the transport keys
TransportKeys updated = createTransportKeys(timePeriod + 1, active); TransportKeys rotated = createTransportKeys(timePeriod + 1, active);
TransportKeys updated1 = TransportKeys rotated1 =
createTransportKeys(timePeriod1 + 1, active); createTransportKeys(timePeriod1 + 1, active);
db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId, db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId,
null, updated)); rotated));
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId, db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId,
null, updated1)); rotated1));
// Retrieve the transport keys again // Retrieve the transport keys again
allKeys = db.getTransportKeys(txn, transportId); allKeys = db.getTransportKeys(txn, transportId);
@@ -712,10 +716,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
for (TransportKeySet ks : allKeys) { for (TransportKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
if (ks.getKeySetId().equals(keySetId)) { if (ks.getKeySetId().equals(keySetId)) {
assertKeysEquals(updated, ks.getKeys()); assertKeysEquals(rotated, ks.getKeys());
} else { } else {
assertEquals(keySetId1, ks.getKeySetId()); assertEquals(keySetId1, ks.getKeySetId());
assertKeysEquals(updated1, ks.getKeys()); assertKeysEquals(rotated1, ks.getKeys());
} }
} }
@@ -739,14 +743,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
actual.getNextIncomingKeys()); actual.getNextIncomingKeys());
assertKeysEquals(expected.getCurrentOutgoingKeys(), assertKeysEquals(expected.getCurrentOutgoingKeys(),
actual.getCurrentOutgoingKeys()); actual.getCurrentOutgoingKeys());
if (expected.isHandshakeMode()) {
assertTrue(actual.isHandshakeMode());
assertArrayEquals(expected.getRootKey().getBytes(),
actual.getRootKey().getBytes());
assertEquals(expected.isAlice(), actual.isAlice());
} else {
assertFalse(actual.isHandshakeMode());
}
} }
private void assertKeysEquals(IncomingKeys expected, IncomingKeys actual) { private void assertKeysEquals(IncomingKeys expected, IncomingKeys actual) {
@@ -775,135 +771,154 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
SecretKey rootKey = getSecretKey(); SecretKey rootKey = getSecretKey();
SecretKey rootKey1 = getSecretKey(); SecretKey rootKey1 = getSecretKey();
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
TransportKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice); HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Initially there should be no handshake keys in the database // Initially there should be no handshake keys in the database
assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
// Add the contact, the transport and the handshake keys // Add the contact, the transport and the handshake keys
db.addIdentity(txn, identity); db.addIdentity(txn, identity);
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(handshakeKeySetId,
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1)); db.addHandshakeKeys(txn, contactId, keys));
assertEquals(handshakeKeySetId1,
db.addHandshakeKeys(txn, contactId, keys1));
// Retrieve the handshake keys // Retrieve the handshake keys
Collection<TransportKeySet> allKeys = Collection<HandshakeKeySet> allKeys =
db.getTransportKeys(txn, transportId); db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size()); assertEquals(2, allKeys.size());
for (TransportKeySet ks : allKeys) { for (HandshakeKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
assertNull(ks.getPendingContactId()); assertNull(ks.getPendingContactId());
if (ks.getKeySetId().equals(keySetId)) { if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(keys, ks.getKeys()); assertKeysEquals(keys, ks.getKeys());
} else { } else {
assertEquals(keySetId1, ks.getKeySetId()); assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getKeys()); assertKeysEquals(keys1, ks.getKeys());
} }
} }
// Update the handshake keys // Update the handshake keys
TransportKeys updated = HandshakeKeys updated =
createHandshakeKeys(timePeriod + 1, rootKey, alice); createHandshakeKeys(timePeriod + 1, rootKey, alice);
TransportKeys updated1 = HandshakeKeys updated1 =
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice); createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId, db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
null, updated)); contactId, updated));
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId, db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
null, updated1)); contactId, updated1));
// Retrieve the handshake keys again // Retrieve the handshake keys again
allKeys = db.getTransportKeys(txn, transportId); allKeys = db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size()); assertEquals(2, allKeys.size());
for (TransportKeySet ks : allKeys) { for (HandshakeKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
assertNull(ks.getPendingContactId()); assertNull(ks.getPendingContactId());
if (ks.getKeySetId().equals(keySetId)) { if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(updated, ks.getKeys()); assertKeysEquals(updated, ks.getKeys());
} else { } else {
assertEquals(keySetId1, ks.getKeySetId()); assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(updated1, ks.getKeys()); assertKeysEquals(updated1, ks.getKeys());
} }
} }
// Removing the contact should remove the handshake keys // Removing the contact should remove the handshake keys
db.removeContact(txn, contactId); db.removeContact(txn, contactId);
assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
} }
private void assertKeysEquals(HandshakeKeys expected,
HandshakeKeys actual) {
assertEquals(expected.getTransportId(), actual.getTransportId());
assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
assertArrayEquals(expected.getRootKey().getBytes(),
actual.getRootKey().getBytes());
assertEquals(expected.isAlice(), actual.isAlice());
assertKeysEquals(expected.getPreviousIncomingKeys(),
actual.getPreviousIncomingKeys());
assertKeysEquals(expected.getCurrentIncomingKeys(),
actual.getCurrentIncomingKeys());
assertKeysEquals(expected.getNextIncomingKeys(),
actual.getNextIncomingKeys());
assertKeysEquals(expected.getCurrentOutgoingKeys(),
actual.getCurrentOutgoingKeys());
}
@Test @Test
public void testHandshakeKeysForPendingContact() throws Exception { public void testHandshakeKeysForPendingContact() throws Exception {
long timePeriod = 123, timePeriod1 = 234; long timePeriod = 123, timePeriod1 = 234;
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
SecretKey rootKey = getSecretKey(); SecretKey rootKey = getSecretKey();
SecretKey rootKey1 = getSecretKey(); SecretKey rootKey1 = getSecretKey();
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
TransportKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice); HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Initially there should be no handshake keys in the database // Initially there should be no handshake keys in the database
assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
// Add the pending contact, the transport and the handshake keys // Add the pending contact, the transport and the handshake keys
db.addPendingContact(txn, pendingContact); db.addPendingContact(txn, pendingContact);
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, assertEquals(handshakeKeySetId, db.addHandshakeKeys(txn,
db.addTransportKeys(txn, pendingContact.getId(), keys)); pendingContact.getId(), keys));
assertEquals(keySetId1, assertEquals(handshakeKeySetId1, db.addHandshakeKeys(txn,
db.addTransportKeys(txn, pendingContact.getId(), keys1)); pendingContact.getId(), keys1));
// Retrieve the handshake keys // Retrieve the handshake keys
Collection<TransportKeySet> allKeys = Collection<HandshakeKeySet> allKeys =
db.getTransportKeys(txn, transportId); db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size()); assertEquals(2, allKeys.size());
for (TransportKeySet ks : allKeys) { for (HandshakeKeySet ks : allKeys) {
assertNull(ks.getContactId()); assertNull(ks.getContactId());
assertEquals(pendingContact.getId(), ks.getPendingContactId()); assertEquals(pendingContact.getId(), ks.getPendingContactId());
if (ks.getKeySetId().equals(keySetId)) { if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(keys, ks.getKeys()); assertKeysEquals(keys, ks.getKeys());
} else { } else {
assertEquals(keySetId1, ks.getKeySetId()); assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getKeys()); assertKeysEquals(keys1, ks.getKeys());
} }
} }
// Update the handshake keys // Update the handshake keys
TransportKeys updated = HandshakeKeys updated =
createHandshakeKeys(timePeriod + 1, rootKey, alice); createHandshakeKeys(timePeriod + 1, rootKey, alice);
TransportKeys updated1 = HandshakeKeys updated1 =
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice); createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
db.updateTransportKeys(txn, new TransportKeySet(keySetId, null, db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
pendingContact.getId(), updated)); pendingContact.getId(), updated));
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, null, db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
pendingContact.getId(), updated1)); pendingContact.getId(), updated1));
// Retrieve the handshake keys again // Retrieve the handshake keys again
allKeys = db.getTransportKeys(txn, transportId); allKeys = db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size()); assertEquals(2, allKeys.size());
for (TransportKeySet ks : allKeys) { for (HandshakeKeySet ks : allKeys) {
assertNull(ks.getContactId()); assertNull(ks.getContactId());
assertEquals(pendingContact.getId(), ks.getPendingContactId()); assertEquals(pendingContact.getId(), ks.getPendingContactId());
if (ks.getKeySetId().equals(keySetId)) { if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(updated, ks.getKeys()); assertKeysEquals(updated, ks.getKeys());
} else { } else {
assertEquals(keySetId1, ks.getKeySetId()); assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(updated1, ks.getKeys()); assertKeysEquals(updated1, ks.getKeys());
} }
} }
// Removing the pending contact should remove the handshake keys // Removing the pending contact should remove the handshake keys
db.removePendingContact(txn, pendingContact.getId()); db.removePendingContact(txn, pendingContact.getId());
assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -956,7 +971,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
long timePeriod = 123; long timePeriod = 123;
SecretKey rootKey = getSecretKey(); SecretKey rootKey = getSecretKey();
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter(); long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
Database<Connection> db = open(false); Database<Connection> db = open(false);
@@ -967,20 +982,20 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys));
// Increment the stream counter twice and retrieve the handshake keys // Increment the stream counter twice and retrieve the handshake keys
db.incrementStreamCounter(txn, transportId, keySetId); db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
db.incrementStreamCounter(txn, transportId, keySetId); db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
Collection<TransportKeySet> newKeys = Collection<HandshakeKeySet> newKeys =
db.getTransportKeys(txn, transportId); db.getHandshakeKeys(txn, transportId);
assertEquals(1, newKeys.size()); assertEquals(1, newKeys.size());
TransportKeySet ks = newKeys.iterator().next(); HandshakeKeySet ks = newKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId()); assertEquals(handshakeKeySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
TransportKeys k = ks.getKeys(); HandshakeKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId()); assertEquals(transportId, k.getTransportId());
assertNotNull(k.getRootKey());
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes()); assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
assertEquals(alice, k.isAlice()); assertEquals(alice, k.isAlice());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
@@ -1049,7 +1064,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
long timePeriod = 123; long timePeriod = 123;
SecretKey rootKey = getSecretKey(); SecretKey rootKey = getSecretKey();
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice); HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
long base = keys.getCurrentIncomingKeys().getWindowBase(); long base = keys.getCurrentIncomingKeys().getWindowBase();
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap(); byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
@@ -1061,21 +1076,21 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true)); db.addContact(txn, author, localAuthor.getId(), true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys));
// Update the reordering window and retrieve the handshake keys // Update the reordering window and retrieve the handshake keys
random.nextBytes(bitmap); random.nextBytes(bitmap);
db.setReorderingWindow(txn, keySetId, transportId, timePeriod, db.setReorderingWindow(txn, handshakeKeySetId, transportId, timePeriod,
base + 1, bitmap); base + 1, bitmap);
Collection<TransportKeySet> newKeys = Collection<HandshakeKeySet> newKeys =
db.getTransportKeys(txn, transportId); db.getHandshakeKeys(txn, transportId);
assertEquals(1, newKeys.size()); assertEquals(1, newKeys.size());
TransportKeySet ks = newKeys.iterator().next(); HandshakeKeySet ks = newKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId()); assertEquals(handshakeKeySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
TransportKeys k = ks.getKeys(); HandshakeKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId()); assertEquals(transportId, k.getTransportId());
assertNotNull(k.getRootKey());
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes()); assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
assertEquals(alice, k.isAlice()); assertEquals(alice, k.isAlice());
IncomingKeys inCurr = k.getCurrentIncomingKeys(); IncomingKeys inCurr = k.getCurrentIncomingKeys();
@@ -2235,8 +2250,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Identity withoutKeys = Identity withoutKeys =
new Identity(localAuthor, null, null, identity.getTimeCreated()); new Identity(localAuthor, null, null, identity.getTimeCreated());
assertFalse(withoutKeys.hasHandshakeKeyPair()); assertFalse(withoutKeys.hasHandshakeKeyPair());
PublicKey publicKey = getAgreementPublicKey(); byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
PrivateKey privateKey = getAgreementPrivateKey(); byte[] privateKey = getRandomBytes(123);
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
@@ -2247,12 +2262,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.setHandshakeKeyPair(txn, localAuthor.getId(), publicKey, privateKey); db.setHandshakeKeyPair(txn, localAuthor.getId(), publicKey, privateKey);
retrieved = db.getIdentity(txn, localAuthor.getId()); retrieved = db.getIdentity(txn, localAuthor.getId());
assertTrue(retrieved.hasHandshakeKeyPair()); assertTrue(retrieved.hasHandshakeKeyPair());
PublicKey handshakePub = retrieved.getHandshakePublicKey(); assertArrayEquals(publicKey, retrieved.getHandshakePublicKey());
assertNotNull(handshakePub); assertArrayEquals(privateKey, retrieved.getHandshakePrivateKey());
assertArrayEquals(publicKey.getEncoded(), handshakePub.getEncoded());
PrivateKey handshakePriv = retrieved.getHandshakePrivateKey();
assertNotNull(handshakePriv);
assertArrayEquals(privateKey.getEncoded(), handshakePriv.getEncoded());
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -2291,7 +2302,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr); return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
} }
private TransportKeys createHandshakeKeys(long timePeriod, private HandshakeKeys createHandshakeKeys(long timePeriod,
SecretKey rootKey, boolean alice) { SecretKey rootKey, boolean alice) {
SecretKey inPrevTagKey = getSecretKey(); SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey();
@@ -2309,7 +2320,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
SecretKey outCurrHeaderKey = getSecretKey(); SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey, OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
timePeriod, 456, true); timePeriod, 456, true);
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr, return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
rootKey, alice); rootKey, alice);
} }

View File

@@ -18,8 +18,6 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
import static org.briarproject.bramble.test.TestUtils.getIdentity; import static org.briarproject.bramble.test.TestUtils.getIdentity;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -30,16 +28,21 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
private final AuthorFactory authorFactory = private final AuthorFactory authorFactory =
context.mock(AuthorFactory.class); context.mock(AuthorFactory.class);
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final PublicKey handshakePublicKey = context.mock(PublicKey.class);
private final PrivateKey handshakePrivateKey =
context.mock(PrivateKey.class);
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
private final Identity identityWithKeys = getIdentity(); private final Identity identityWithKeys = getIdentity();
private final LocalAuthor localAuthor = identityWithKeys.getLocalAuthor(); private final LocalAuthor localAuthor = identityWithKeys.getLocalAuthor();
private final Identity identityWithoutKeys = new Identity(localAuthor, private final Identity identityWithoutKeys = new Identity(localAuthor,
null, null, identityWithKeys.getTimeCreated()); null, null, identityWithKeys.getTimeCreated());
private final PublicKey handshakePublicKey = getAgreementPublicKey();
private final PrivateKey handshakePrivateKey = getAgreementPrivateKey();
private final KeyPair handshakeKeyPair = private final KeyPair handshakeKeyPair =
new KeyPair(handshakePublicKey, handshakePrivateKey); new KeyPair(handshakePublicKey, handshakePrivateKey);
private final byte[] handshakePublicKeyBytes =
identityWithKeys.getHandshakePublicKey();
private final byte[] handshakePrivateKeyBytes =
identityWithKeys.getHandshakePrivateKey();
private IdentityManagerImpl identityManager; private IdentityManagerImpl identityManager;
@@ -66,8 +69,12 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
will(returnValue(singletonList(identityWithoutKeys))); will(returnValue(singletonList(identityWithoutKeys)));
oneOf(crypto).generateAgreementKeyPair(); oneOf(crypto).generateAgreementKeyPair();
will(returnValue(handshakeKeyPair)); will(returnValue(handshakeKeyPair));
oneOf(handshakePublicKey).getEncoded();
will(returnValue(handshakePublicKeyBytes));
oneOf(handshakePrivateKey).getEncoded();
will(returnValue(handshakePrivateKeyBytes));
oneOf(db).setHandshakeKeyPair(txn, localAuthor.getId(), oneOf(db).setHandshakeKeyPair(txn, localAuthor.getId(),
handshakePublicKey, handshakePrivateKey); handshakePublicKeyBytes, handshakePrivateKeyBytes);
}}); }});
identityManager.onDatabaseOpened(txn); identityManager.onDatabaseOpened(txn);
@@ -97,6 +104,10 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
will(returnValue(singletonList(identityWithoutKeys))); will(returnValue(singletonList(identityWithoutKeys)));
oneOf(crypto).generateAgreementKeyPair(); oneOf(crypto).generateAgreementKeyPair();
will(returnValue(handshakeKeyPair)); will(returnValue(handshakeKeyPair));
oneOf(handshakePublicKey).getEncoded();
will(returnValue(handshakePublicKeyBytes));
oneOf(handshakePrivateKey).getEncoded();
will(returnValue(handshakePrivateKeyBytes));
}}); }});
assertEquals(localAuthor, identityManager.getLocalAuthor()); assertEquals(localAuthor, identityManager.getLocalAuthor());

View File

@@ -16,13 +16,10 @@ import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_LABEL;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
@@ -37,17 +34,21 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
setImposteriser(ClassImposteriser.INSTANCE); setImposteriser(ClassImposteriser.INSTANCE);
}}; }};
private final PublicKey alicePubKey = getAgreementPublicKey(); private final PublicKey alicePubKey =
context.mock(PublicKey.class, "alice");
private final byte[] alicePubKeyBytes = getRandomBytes(32);
private final byte[] aliceCommit = getRandomBytes(COMMIT_LENGTH); private final byte[] aliceCommit = getRandomBytes(COMMIT_LENGTH);
private final byte[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8); private final byte[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8);
private final byte[] aliceConfirm = getRandomBytes(SecretKey.LENGTH); private final byte[] aliceConfirm = getRandomBytes(SecretKey.LENGTH);
private final PublicKey bobPubKey = getAgreementPublicKey(); private final PublicKey bobPubKey = context.mock(PublicKey.class, "bob");
private final byte[] bobPubKeyBytes = getRandomBytes(32);
private final byte[] bobCommit = getRandomBytes(COMMIT_LENGTH); private final byte[] bobCommit = getRandomBytes(COMMIT_LENGTH);
private final byte[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19); private final byte[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19);
private final byte[] bobConfirm = getRandomBytes(SecretKey.LENGTH); private final byte[] bobConfirm = getRandomBytes(SecretKey.LENGTH);
private final PublicKey badPubKey = getAgreementPublicKey(); private final PublicKey badPubKey = context.mock(PublicKey.class, "bad");
private final byte[] badPubKeyBytes = getRandomBytes(32);
private final byte[] badCommit = getRandomBytes(COMMIT_LENGTH); private final byte[] badCommit = getRandomBytes(COMMIT_LENGTH);
private final byte[] badConfirm = getRandomBytes(SecretKey.LENGTH); private final byte[] badConfirm = getRandomBytes(SecretKey.LENGTH);
@@ -63,13 +64,15 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
PayloadEncoder payloadEncoder; PayloadEncoder payloadEncoder;
@Mock @Mock
KeyAgreementTransport transport; KeyAgreementTransport transport;
@Mock
PublicKey ourPubKey;
@Test @Test
public void testAliceProtocol() throws Exception { public void testAliceProtocol() throws Exception {
// set up // set up
Payload theirPayload = new Payload(bobCommit, emptyList()); Payload theirPayload = new Payload(bobCommit, null);
Payload ourPayload = new Payload(aliceCommit, emptyList()); Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(alicePubKey, getAgreementPrivateKey()); KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey(); SecretKey sharedSecret = getSecretKey();
SecretKey masterKey = getSecretKey(); SecretKey masterKey = getSecretKey();
@@ -84,18 +87,24 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(alicePayload)); will(returnValue(alicePayload));
allowing(payloadEncoder).encode(theirPayload); allowing(payloadEncoder).encode(theirPayload);
will(returnValue(bobPayload)); will(returnValue(bobPayload));
allowing(ourPubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
allowing(crypto).getAgreementKeyParser(); allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser)); will(returnValue(keyParser));
allowing(alicePubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
allowing(bobPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
// Alice sends her public key // Alice sends her public key
oneOf(transport).sendKey(alicePubKey.getEncoded()); oneOf(transport).sendKey(alicePubKeyBytes);
// Alice receives Bob's public key // Alice receives Bob's public key
oneOf(callbacks).connectionWaiting(); oneOf(callbacks).connectionWaiting();
oneOf(transport).receiveKey(); oneOf(transport).receiveKey();
will(returnValue(bobPubKey.getEncoded())); will(returnValue(bobPubKeyBytes));
oneOf(callbacks).initialRecordReceived(); oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(bobPubKey.getEncoded()); oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
will(returnValue(bobPubKey)); will(returnValue(bobPubKey));
// Alice verifies Bob's public key // Alice verifies Bob's public key
@@ -105,7 +114,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// Alice computes shared secret // Alice computes shared secret
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey, oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
ourKeyPair, new byte[] {PROTOCOL_VERSION}, ourKeyPair, new byte[] {PROTOCOL_VERSION},
alicePubKey.getEncoded(), bobPubKey.getEncoded()); alicePubKeyBytes, bobPubKeyBytes);
will(returnValue(sharedSecret)); will(returnValue(sharedSecret));
// Alice sends her confirmation record // Alice sends her confirmation record
@@ -137,9 +146,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test @Test
public void testBobProtocol() throws Exception { public void testBobProtocol() throws Exception {
// set up // set up
Payload theirPayload = new Payload(aliceCommit, emptyList()); Payload theirPayload = new Payload(aliceCommit, null);
Payload ourPayload = new Payload(bobCommit, emptyList()); Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(bobPubKey, getAgreementPrivateKey()); KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey(); SecretKey sharedSecret = getSecretKey();
SecretKey masterKey = getSecretKey(); SecretKey masterKey = getSecretKey();
@@ -154,14 +163,20 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(bobPayload)); will(returnValue(bobPayload));
allowing(payloadEncoder).encode(theirPayload); allowing(payloadEncoder).encode(theirPayload);
will(returnValue(alicePayload)); will(returnValue(alicePayload));
allowing(ourPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
allowing(crypto).getAgreementKeyParser(); allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser)); will(returnValue(keyParser));
allowing(alicePubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
allowing(bobPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
// Bob receives Alice's public key // Bob receives Alice's public key
oneOf(transport).receiveKey(); oneOf(transport).receiveKey();
will(returnValue(alicePubKey.getEncoded())); will(returnValue(alicePubKeyBytes));
oneOf(callbacks).initialRecordReceived(); oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(alicePubKey.getEncoded()); oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
will(returnValue(alicePubKey)); will(returnValue(alicePubKey));
// Bob verifies Alice's public key // Bob verifies Alice's public key
@@ -169,12 +184,12 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(aliceCommit)); will(returnValue(aliceCommit));
// Bob sends his public key // Bob sends his public key
oneOf(transport).sendKey(bobPubKey.getEncoded()); oneOf(transport).sendKey(bobPubKeyBytes);
// Bob computes shared secret // Bob computes shared secret
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey, oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
ourKeyPair, new byte[] {PROTOCOL_VERSION}, ourKeyPair, new byte[] {PROTOCOL_VERSION},
alicePubKey.getEncoded(), bobPubKey.getEncoded()); alicePubKeyBytes, bobPubKeyBytes);
will(returnValue(sharedSecret)); will(returnValue(sharedSecret));
// Bob receives Alices's confirmation record // Bob receives Alices's confirmation record
@@ -206,9 +221,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class) @Test(expected = AbortException.class)
public void testAliceProtocolAbortOnBadKey() throws Exception { public void testAliceProtocolAbortOnBadKey() throws Exception {
// set up // set up
Payload theirPayload = new Payload(bobCommit, emptyList()); Payload theirPayload = new Payload(bobCommit, null);
Payload ourPayload = new Payload(aliceCommit, emptyList()); Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(alicePubKey, getAgreementPrivateKey()); KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport, crypto, keyAgreementCrypto, payloadEncoder, transport,
@@ -217,18 +232,20 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// expectations // expectations
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Helpers // Helpers
allowing(ourPubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
allowing(crypto).getAgreementKeyParser(); allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser)); will(returnValue(keyParser));
// Alice sends her public key // Alice sends her public key
oneOf(transport).sendKey(alicePubKey.getEncoded()); oneOf(transport).sendKey(alicePubKeyBytes);
// Alice receives a bad public key // Alice receives a bad public key
oneOf(callbacks).connectionWaiting(); oneOf(callbacks).connectionWaiting();
oneOf(transport).receiveKey(); oneOf(transport).receiveKey();
will(returnValue(badPubKey.getEncoded())); will(returnValue(badPubKeyBytes));
oneOf(callbacks).initialRecordReceived(); oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(badPubKey.getEncoded()); oneOf(keyParser).parsePublicKey(badPubKeyBytes);
will(returnValue(badPubKey)); will(returnValue(badPubKey));
// Alice verifies Bob's public key // Alice verifies Bob's public key
@@ -241,7 +258,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// Alice never computes shared secret // Alice never computes shared secret
never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey, never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey,
ourKeyPair, new byte[] {PROTOCOL_VERSION}, ourKeyPair, new byte[] {PROTOCOL_VERSION},
alicePubKey.getEncoded(), bobPubKey.getEncoded()); alicePubKeyBytes, bobPubKeyBytes);
}}); }});
// execute // execute
@@ -251,9 +268,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class) @Test(expected = AbortException.class)
public void testBobProtocolAbortOnBadKey() throws Exception { public void testBobProtocolAbortOnBadKey() throws Exception {
// set up // set up
Payload theirPayload = new Payload(aliceCommit, emptyList()); Payload theirPayload = new Payload(aliceCommit, null);
Payload ourPayload = new Payload(bobCommit, emptyList()); Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(bobPubKey, getAgreementPrivateKey()); KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport, crypto, keyAgreementCrypto, payloadEncoder, transport,
@@ -262,14 +279,16 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// expectations // expectations
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Helpers // Helpers
allowing(ourPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
allowing(crypto).getAgreementKeyParser(); allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser)); will(returnValue(keyParser));
// Bob receives a bad public key // Bob receives a bad public key
oneOf(transport).receiveKey(); oneOf(transport).receiveKey();
will(returnValue(badPubKey.getEncoded())); will(returnValue(badPubKeyBytes));
oneOf(callbacks).initialRecordReceived(); oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(badPubKey.getEncoded()); oneOf(keyParser).parsePublicKey(badPubKeyBytes);
will(returnValue(badPubKey)); will(returnValue(badPubKey));
// Bob verifies Alice's public key // Bob verifies Alice's public key
@@ -280,7 +299,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
oneOf(transport).sendAbort(false); oneOf(transport).sendAbort(false);
// Bob never sends his public key // Bob never sends his public key
never(transport).sendKey(bobPubKey.getEncoded()); never(transport).sendKey(bobPubKeyBytes);
}}); }});
// execute // execute
@@ -290,9 +309,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class) @Test(expected = AbortException.class)
public void testAliceProtocolAbortOnBadConfirm() throws Exception { public void testAliceProtocolAbortOnBadConfirm() throws Exception {
// set up // set up
Payload theirPayload = new Payload(bobCommit, emptyList()); Payload theirPayload = new Payload(bobCommit, null);
Payload ourPayload = new Payload(aliceCommit, emptyList()); Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(alicePubKey, getAgreementPrivateKey()); KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey(); SecretKey sharedSecret = getSecretKey();
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
@@ -306,18 +325,22 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(alicePayload)); will(returnValue(alicePayload));
allowing(payloadEncoder).encode(theirPayload); allowing(payloadEncoder).encode(theirPayload);
will(returnValue(bobPayload)); will(returnValue(bobPayload));
allowing(ourPubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
allowing(crypto).getAgreementKeyParser(); allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser)); will(returnValue(keyParser));
allowing(bobPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
// Alice sends her public key // Alice sends her public key
oneOf(transport).sendKey(alicePubKey.getEncoded()); oneOf(transport).sendKey(alicePubKeyBytes);
// Alice receives Bob's public key // Alice receives Bob's public key
oneOf(callbacks).connectionWaiting(); oneOf(callbacks).connectionWaiting();
oneOf(transport).receiveKey(); oneOf(transport).receiveKey();
will(returnValue(bobPubKey.getEncoded())); will(returnValue(bobPubKeyBytes));
oneOf(callbacks).initialRecordReceived(); oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(bobPubKey.getEncoded()); oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
will(returnValue(bobPubKey)); will(returnValue(bobPubKey));
// Alice verifies Bob's public key // Alice verifies Bob's public key
@@ -327,7 +350,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// Alice computes shared secret // Alice computes shared secret
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey, oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
ourKeyPair, new byte[] {PROTOCOL_VERSION}, ourKeyPair, new byte[] {PROTOCOL_VERSION},
alicePubKey.getEncoded(), bobPubKey.getEncoded()); alicePubKeyBytes, bobPubKeyBytes);
will(returnValue(sharedSecret)); will(returnValue(sharedSecret));
// Alice sends her confirmation record // Alice sends her confirmation record
@@ -361,9 +384,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class) @Test(expected = AbortException.class)
public void testBobProtocolAbortOnBadConfirm() throws Exception { public void testBobProtocolAbortOnBadConfirm() throws Exception {
// set up // set up
Payload theirPayload = new Payload(aliceCommit, emptyList()); Payload theirPayload = new Payload(aliceCommit, null);
Payload ourPayload = new Payload(bobCommit, emptyList()); Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(bobPubKey, getAgreementPrivateKey()); KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey(); SecretKey sharedSecret = getSecretKey();
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
@@ -377,14 +400,18 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(bobPayload)); will(returnValue(bobPayload));
allowing(payloadEncoder).encode(theirPayload); allowing(payloadEncoder).encode(theirPayload);
will(returnValue(alicePayload)); will(returnValue(alicePayload));
allowing(ourPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
allowing(crypto).getAgreementKeyParser(); allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser)); will(returnValue(keyParser));
allowing(alicePubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
// Bob receives Alice's public key // Bob receives Alice's public key
oneOf(transport).receiveKey(); oneOf(transport).receiveKey();
will(returnValue(alicePubKey.getEncoded())); will(returnValue(alicePubKeyBytes));
oneOf(callbacks).initialRecordReceived(); oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(alicePubKey.getEncoded()); oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
will(returnValue(alicePubKey)); will(returnValue(alicePubKey));
// Bob verifies Alice's public key // Bob verifies Alice's public key
@@ -392,12 +419,12 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(aliceCommit)); will(returnValue(aliceCommit));
// Bob sends his public key // Bob sends his public key
oneOf(transport).sendKey(bobPubKey.getEncoded()); oneOf(transport).sendKey(bobPubKeyBytes);
// Bob computes shared secret // Bob computes shared secret
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey, oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
ourKeyPair, new byte[] {PROTOCOL_VERSION}, ourKeyPair, new byte[] {PROTOCOL_VERSION},
alicePubKey.getEncoded(), bobPubKey.getEncoded()); alicePubKeyBytes, bobPubKeyBytes);
will(returnValue(sharedSecret)); will(returnValue(sharedSecret));
// Bob receives a bad confirmation record // Bob receives a bad confirmation record

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -14,18 +13,15 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.jmock.lib.concurrent.Synchroniser; import org.jmock.lib.concurrent.Synchroniser;
import org.junit.Test; import org.junit.Test;
import java.security.SecureRandom;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -37,16 +33,10 @@ public class PluginManagerImplTest extends BrambleTestCase {
setThreadingPolicy(new Synchroniser()); setThreadingPolicy(new Synchroniser());
}}; }};
Executor ioExecutor = Executors.newSingleThreadExecutor(); Executor ioExecutor = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
SecureRandom random = new SecureRandom();
Clock clock = context.mock(Clock.class);
EventBus eventBus = context.mock(EventBus.class); EventBus eventBus = context.mock(EventBus.class);
PluginConfig pluginConfig = context.mock(PluginConfig.class); PluginConfig pluginConfig = context.mock(PluginConfig.class);
ConnectionManager connectionManager = ConnectionManager connectionManager =
context.mock(ConnectionManager.class); context.mock(ConnectionManager.class);
ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
SettingsManager settingsManager = SettingsManager settingsManager =
context.mock(SettingsManager.class); context.mock(SettingsManager.class);
TransportPropertyManager transportPropertyManager = TransportPropertyManager transportPropertyManager =
@@ -122,9 +112,9 @@ public class PluginManagerImplTest extends BrambleTestCase {
oneOf(duplexPlugin).stop(); oneOf(duplexPlugin).stop();
}}); }});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, scheduler, PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
eventBus, pluginConfig, connectionManager, connectionRegistry, pluginConfig, connectionManager, settingsManager,
settingsManager, transportPropertyManager, random, clock); transportPropertyManager);
// Two plugins should be started and stopped // Two plugins should be started and stopped
p.startService(); p.startService();

View File

@@ -23,6 +23,7 @@ import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.RunAction; import org.briarproject.bramble.test.RunAction;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.lib.legacy.ClassImposteriser; import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.security.SecureRandom; import java.security.SecureRandom;
@@ -39,7 +40,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.test.TestUtils.getContactId; import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
public class PollerTest extends BrambleMockTestCase { public class PollerImplTest extends BrambleMockTestCase {
private final ScheduledExecutorService scheduler = private final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class); context.mock(ScheduledExecutorService.class);
@@ -62,11 +63,20 @@ public class PollerTest extends BrambleMockTestCase {
private final int pollingInterval = 60 * 1000; private final int pollingInterval = 60 * 1000;
private final long now = System.currentTimeMillis(); private final long now = System.currentTimeMillis();
public PollerTest() { private PollerImpl poller;
public PollerImplTest() {
context.setImposteriser(ClassImposteriser.INSTANCE); context.setImposteriser(ClassImposteriser.INSTANCE);
random = context.mock(SecureRandom.class); random = context.mock(SecureRandom.class);
} }
@Before
public void setUp() {
poller = new PollerImpl(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
}
@Test @Test
public void testConnectOnContactAdded() throws Exception { public void testConnectOnContactAdded() throws Exception {
// Two simplex plugins: one supports polling, the other doesn't // Two simplex plugins: one supports polling, the other doesn't
@@ -140,11 +150,7 @@ public class PollerTest extends BrambleMockTestCase {
will(returnValue(false)); will(returnValue(false));
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, poller.eventOccurred(new ContactAddedEvent(contactId));
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ContactAddedEvent(contactId));
} }
@Test @Test
@@ -194,11 +200,7 @@ public class PollerTest extends BrambleMockTestCase {
transportId, duplexConnection); transportId, duplexConnection);
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
false)); false));
} }
@@ -225,11 +227,7 @@ public class PollerTest extends BrambleMockTestCase {
will(returnValue(future)); will(returnValue(future));
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
} }
@@ -269,13 +267,9 @@ public class PollerTest extends BrambleMockTestCase {
will(returnValue(now + 1)); will(returnValue(now + 1));
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
} }
@@ -318,13 +312,9 @@ public class PollerTest extends BrambleMockTestCase {
with((long) pollingInterval - 2), with(MILLISECONDS)); with((long) pollingInterval - 2), with(MILLISECONDS));
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
} }
@@ -367,11 +357,7 @@ public class PollerTest extends BrambleMockTestCase {
oneOf(plugin).poll(singletonMap(contactId, properties)); oneOf(plugin).poll(singletonMap(contactId, properties));
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, poller.eventOccurred(new TransportEnabledEvent(transportId));
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId));
} }
@Test @Test
@@ -412,11 +398,7 @@ public class PollerTest extends BrambleMockTestCase {
// All contacts are connected, so don't poll the plugin // All contacts are connected, so don't poll the plugin
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, poller.eventOccurred(new TransportEnabledEvent(transportId));
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId));
} }
@Test @Test
@@ -442,11 +424,7 @@ public class PollerTest extends BrambleMockTestCase {
oneOf(future).cancel(false); oneOf(future).cancel(false);
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, poller.eventOccurred(new TransportEnabledEvent(transportId));
connectionRegistry, pluginManager, transportPropertyManager, poller.eventOccurred(new TransportDisabledEvent(transportId));
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId));
p.eventOccurred(new TransportDisabledEvent(transportId));
} }
} }

View File

@@ -101,8 +101,8 @@ public class SyncIntegrationTest extends BrambleTestCase {
private byte[] write() throws Exception { private byte[] write() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamContext ctx = new StreamContext(contactId, null, transportId, StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
tagKey, headerKey, streamNumber, false); headerKey, streamNumber);
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out, StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out,
ctx); ctx);
SyncRecordWriter recordWriter = recordWriterFactory.createRecordWriter( SyncRecordWriter recordWriter = recordWriterFactory.createRecordWriter(
@@ -131,8 +131,8 @@ public class SyncIntegrationTest extends BrambleTestCase {
assertArrayEquals(expectedTag, tag); assertArrayEquals(expectedTag, tag);
// Create the readers // Create the readers
StreamContext ctx = new StreamContext(contactId, null, transportId, StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
tagKey, headerKey, streamNumber, false); headerKey, streamNumber);
InputStream streamReader = streamReaderFactory.createStreamReader(in, InputStream streamReader = streamReaderFactory.createStreamReader(in,
ctx); ctx);
SyncRecordReader recordReader = recordReaderFactory.createRecordReader( SyncRecordReader recordReader = recordReaderFactory.createRecordReader(

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -9,8 +8,8 @@ import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations; import org.jmock.Expectations;
@@ -27,7 +26,6 @@ import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getContactId; import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -45,17 +43,11 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
private final DeterministicExecutor executor = new DeterministicExecutor(); private final DeterministicExecutor executor = new DeterministicExecutor();
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
private final ContactId contactId = getContactId(); private final ContactId contactId = getContactId();
private final PendingContactId pendingContactId = private final TransportKeySetId keySetId = new TransportKeySetId(345);
new PendingContactId(getRandomId());
private final KeySetId keySetId = new KeySetId(345);
private final TransportId transportId = getTransportId(); private final TransportId transportId = getTransportId();
private final TransportId unknownTransportId = getTransportId(); private final TransportId unknownTransportId = getTransportId();
private final StreamContext contactStreamContext = private final StreamContext streamContext = new StreamContext(contactId,
new StreamContext(contactId, null, transportId, getSecretKey(), transportId, getSecretKey(), getSecretKey(), 1);
getSecretKey(), 1, false);
private final StreamContext pendingContactStreamContext =
new StreamContext(null, pendingContactId, transportId,
getSecretKey(), getSecretKey(), 1, true);
private final byte[] tag = getRandomBytes(TAG_LENGTH); private final byte[] tag = getRandomBytes(TAG_LENGTH);
private final Random random = new Random(); private final Random random = new Random();
@@ -91,94 +83,41 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testAddContactWithRotationModeKeys() throws Exception { public void testAddContact() throws Exception {
SecretKey secretKey = getSecretKey(); SecretKey secretKey = getSecretKey();
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
boolean active = random.nextBoolean(); boolean active = random.nextBoolean();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportKeyManager).addContactWithRotationKeys(txn, oneOf(transportKeyManager).addContact(txn, contactId, secretKey,
contactId, secretKey, timestamp, alice, active); timestamp, alice, active);
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
Map<TransportId, KeySetId> ids = keyManager.addContactWithRotationKeys( Map<TransportId, TransportKeySetId> ids = keyManager.addContact(txn,
txn, contactId, secretKey, timestamp, alice, active); contactId, secretKey, timestamp, alice, active);
assertEquals(singletonMap(transportId, keySetId), ids); assertEquals(singletonMap(transportId, keySetId), ids);
} }
@Test @Test
public void testAddContactWithHandshakeModeKeys() throws Exception { public void testGetStreamContextForUnknownTransport() throws Exception {
SecretKey secretKey = getSecretKey();
boolean alice = random.nextBoolean();
context.checking(new Expectations() {{
oneOf(transportKeyManager).addContactWithHandshakeKeys(
txn, contactId, secretKey, alice);
will(returnValue(keySetId));
}});
Map<TransportId, KeySetId> ids = keyManager.addContactWithHandshakeKeys(
txn, contactId, secretKey, alice);
assertEquals(singletonMap(transportId, keySetId), ids);
}
@Test
public void testAddPendingContact() throws Exception {
SecretKey secretKey = getSecretKey();
boolean alice = random.nextBoolean();
context.checking(new Expectations() {{
oneOf(transportKeyManager).addPendingContact(txn, pendingContactId,
secretKey, alice);
will(returnValue(keySetId));
}});
Map<TransportId, KeySetId> ids = keyManager.addPendingContact(txn,
pendingContactId, secretKey, alice);
assertEquals(singletonMap(transportId, keySetId), ids);
}
@Test
public void testGetStreamContextForContactWithUnknownTransport()
throws Exception {
assertNull(keyManager.getStreamContext(contactId, unknownTransportId)); assertNull(keyManager.getStreamContext(contactId, unknownTransportId));
} }
@Test
public void testGetStreamContextForPendingContactWithUnknownTransport()
throws Exception {
assertNull(keyManager.getStreamContext(pendingContactId,
unknownTransportId));
}
@Test @Test
public void testGetStreamContextForContact() throws Exception { public void testGetStreamContextForContact() throws Exception {
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithNullableResult(with(false), oneOf(db).transactionWithNullableResult(with(false),
withNullableDbCallable(txn)); withNullableDbCallable(txn));
oneOf(transportKeyManager).getStreamContext(txn, contactId); oneOf(transportKeyManager).getStreamContext(txn, contactId);
will(returnValue(contactStreamContext)); will(returnValue(streamContext));
}}); }});
assertEquals(contactStreamContext, assertEquals(streamContext,
keyManager.getStreamContext(contactId, transportId)); keyManager.getStreamContext(contactId, transportId));
} }
@Test
public void testGetStreamContextForPendingContact() throws Exception {
context.checking(new DbExpectations() {{
oneOf(db).transactionWithNullableResult(with(false),
withNullableDbCallable(txn));
oneOf(transportKeyManager).getStreamContext(txn, pendingContactId);
will(returnValue(pendingContactStreamContext));
}});
assertEquals(pendingContactStreamContext,
keyManager.getStreamContext(pendingContactId, transportId));
}
@Test @Test
public void testGetStreamContextForTagAndUnknownTransport() public void testGetStreamContextForTagAndUnknownTransport()
throws Exception { throws Exception {
@@ -191,10 +130,10 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
oneOf(db).transactionWithNullableResult(with(false), oneOf(db).transactionWithNullableResult(with(false),
withNullableDbCallable(txn)); withNullableDbCallable(txn));
oneOf(transportKeyManager).getStreamContext(txn, tag); oneOf(transportKeyManager).getStreamContext(txn, tag);
will(returnValue(contactStreamContext)); will(returnValue(streamContext));
}}); }});
assertEquals(contactStreamContext, assertEquals(streamContext,
keyManager.getStreamContext(transportId, tag)); keyManager.getStreamContext(transportId, tag));
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -9,10 +8,10 @@ import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
@@ -38,7 +37,6 @@ import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE; import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getContactId; import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
@@ -63,26 +61,22 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
private final long timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE; private final long timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
private final ContactId contactId = getContactId(); private final ContactId contactId = getContactId();
private final ContactId contactId1 = getContactId(); private final ContactId contactId1 = getContactId();
private final PendingContactId pendingContactId = private final TransportKeySetId keySetId = new TransportKeySetId(345);
new PendingContactId(getRandomId()); private final TransportKeySetId keySetId1 = new TransportKeySetId(456);
private final KeySetId keySetId = new KeySetId(345);
private final KeySetId keySetId1 = new KeySetId(456);
private final SecretKey tagKey = getSecretKey(); private final SecretKey tagKey = getSecretKey();
private final SecretKey headerKey = getSecretKey(); private final SecretKey headerKey = getSecretKey();
private final SecretKey rootKey = getSecretKey(); private final SecretKey rootKey = getSecretKey();
private final Random random = new Random(); private final Random random = new Random();
@Test @Test
public void testKeysAreUpdatedAtStartup() throws Exception { public void testKeysAreRotatedAtStartup() throws Exception {
boolean active = random.nextBoolean(); TransportKeys shouldRotate = createTransportKeys(900, 0, true);
TransportKeys shouldUpdate = createTransportKeys(900, 0, active); TransportKeys shouldNotRotate = createTransportKeys(1000, 0, true);
TransportKeys shouldNotUpdate = createTransportKeys(1000, 0, active);
Collection<TransportKeySet> loaded = asList( Collection<TransportKeySet> loaded = asList(
new TransportKeySet(keySetId, contactId, null, shouldUpdate), new TransportKeySet(keySetId, contactId, shouldRotate),
new TransportKeySet(keySetId1, contactId1, null, new TransportKeySet(keySetId1, contactId1, shouldNotRotate)
shouldNotUpdate)
); );
TransportKeys updated = createTransportKeys(1000, 0, active); TransportKeys rotated = createTransportKeys(1000, 0, true);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -92,11 +86,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
// Load the transport keys // Load the transport keys
oneOf(db).getTransportKeys(txn, transportId); oneOf(db).getTransportKeys(txn, transportId);
will(returnValue(loaded)); will(returnValue(loaded));
// Update the transport keys // Rotate the transport keys
oneOf(transportCrypto).updateTransportKeys(shouldUpdate, 1000); oneOf(transportCrypto).rotateTransportKeys(shouldRotate, 1000);
will(returnValue(updated)); will(returnValue(rotated));
oneOf(transportCrypto).updateTransportKeys(shouldNotUpdate, 1000); oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
will(returnValue(shouldNotUpdate)); will(returnValue(shouldNotRotate));
// Encode the tags (3 sets per contact) // Encode the tags (3 sets per contact)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(6).of(transportCrypto).encodeTag( exactly(6).of(transportCrypto).encodeTag(
@@ -104,10 +98,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(PROTOCOL_VERSION), with(i)); with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were updated // Save the keys that were rotated
oneOf(db).updateTransportKeys(txn, singletonList( oneOf(db).updateTransportKeys(txn, singletonList(
new TransportKeySet(keySetId, contactId, null, updated))); new TransportKeySet(keySetId, contactId, rotated)));
// Schedule a key update at the start of the next time period // Schedule key rotation at the start of the next time period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(timePeriodLength - 1), with(MILLISECONDS)); with(timePeriodLength - 1), with(MILLISECONDS));
}}); }});
@@ -116,29 +110,26 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
transportKeyManager.start(txn); transportKeyManager.start(txn);
assertEquals(active, assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test @Test
public void testRotationKeysAreDerivedAndUpdatedWhenAddingContact() public void testKeysAreRotatedWhenAddingContact() throws Exception {
throws Exception {
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
boolean active = random.nextBoolean(); TransportKeys transportKeys = createTransportKeys(999, 0, true);
TransportKeys transportKeys = createTransportKeys(999, 0, active); TransportKeys rotated = createTransportKeys(1000, 0, true);
TransportKeys updated = createTransportKeys(1000, 0, active);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey, oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
999, alice, active); 999, alice, true);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Get the current time (1 ms after start of time period 1000) // Get the current time (1 ms after start of time period 1000)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(timePeriodLength * 1000 + 1)); will(returnValue(timePeriodLength * 1000 + 1));
// Update the transport keys // Rotate the transport keys
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000); oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(updated)); will(returnValue(rotated));
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag( exactly(3).of(transportCrypto).encodeTag(
@@ -147,7 +138,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys // Save the keys
oneOf(db).addTransportKeys(txn, contactId, updated); oneOf(db).addTransportKeys(txn, contactId, rotated);
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
@@ -156,83 +147,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is 1 ms before the start of time period 1000 // The timestamp is 1 ms before the start of time period 1000
long timestamp = timePeriodLength * 1000 - 1; long timestamp = timePeriodLength * 1000 - 1;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
txn, contactId, rootKey, timestamp, alice, active)); rootKey, timestamp, alice, true));
assertEquals(active,
transportKeyManager.canSendOutgoingStreams(contactId));
}
@Test
public void testHandshakeKeysAreDerivedWhenAddingContact()
throws Exception {
boolean alice = random.nextBoolean();
TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice);
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
// Get the current time (1 ms after start of time period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(timePeriodLength * 1000 + 1));
// Derive the transport keys
oneOf(transportCrypto).deriveHandshakeKeys(transportId, rootKey,
1000, alice);
will(returnValue(transportKeys));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction());
}
// Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
will(returnValue(keySetId));
}});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
assertEquals(keySetId, transportKeyManager.addContactWithHandshakeKeys(
txn, contactId, rootKey, alice));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test
public void testHandshakeKeysAreDerivedWhenAddingPendingContact()
throws Exception {
boolean alice = random.nextBoolean();
TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice);
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
// Get the current time (1 ms after start of time period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(timePeriodLength * 1000 + 1));
// Derive the transport keys
oneOf(transportCrypto).deriveHandshakeKeys(transportId, rootKey,
1000, alice);
will(returnValue(transportKeys));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction());
}
// Save the keys
oneOf(db).addTransportKeys(txn, pendingContactId, transportKeys);
will(returnValue(keySetId));
}});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
assertEquals(keySetId, transportKeyManager.addPendingContact(txn,
pendingContactId, rootKey, alice));
assertTrue(transportKeyManager.canSendOutgoingStreams(
pendingContactId));
}
@Test @Test
public void testOutgoingStreamContextIsNullIfContactIsNotFound() public void testOutgoingStreamContextIsNullIfContactIsNotFound()
throws Exception { throws Exception {
@@ -245,19 +164,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test
public void testOutgoingStreamContextIsNullIfPendingContactIsNotFound()
throws Exception {
Transaction txn = new Transaction(null, false);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
assertNull(transportKeyManager.getStreamContext(txn, pendingContactId));
assertFalse(transportKeyManager.canSendOutgoingStreams(
pendingContactId));
}
@Test @Test
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted() public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
throws Exception { throws Exception {
@@ -267,15 +173,15 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
MAX_32_BIT_UNSIGNED + 1, true); MAX_32_BIT_UNSIGNED + 1, true);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
expectAddContactKeysNotUpdated(alice, true, transportKeys, txn); expectAddContactNoRotation(alice, true, transportKeys, txn);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
txn, contactId, rootKey, timestamp, alice, true)); rootKey, timestamp, alice, true));
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
} }
@@ -288,7 +194,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
MAX_32_BIT_UNSIGNED, true); MAX_32_BIT_UNSIGNED, true);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
expectAddContactKeysNotUpdated(alice, true, transportKeys, txn); expectAddContactNoRotation(alice, true, transportKeys, txn);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Increment the stream counter // Increment the stream counter
@@ -300,8 +206,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
txn, contactId, rootKey, timestamp, alice, true)); rootKey, timestamp, alice, true));
// The first request should return a stream context // The first request should return a stream context
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
StreamContext ctx = transportKeyManager.getStreamContext(txn, StreamContext ctx = transportKeyManager.getStreamContext(txn,
@@ -325,15 +231,15 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
TransportKeys transportKeys = createTransportKeys(1000, 0, active); TransportKeys transportKeys = createTransportKeys(1000, 0, active);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
expectAddContactKeysNotUpdated(alice, active, transportKeys, txn); expectAddContactNoRotation(alice, active, transportKeys, txn);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
txn, contactId, rootKey, timestamp, alice, active)); rootKey, timestamp, alice, active));
assertEquals(active, assertEquals(active,
transportKeyManager.canSendOutgoingStreams(contactId)); transportKeyManager.canSendOutgoingStreams(contactId));
// The tag should not be recognised // The tag should not be recognised
@@ -351,7 +257,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
List<byte[]> tags = new ArrayList<>(); List<byte[]> tags = new ArrayList<>();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey, oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
1000, alice, true); 1000, alice, true);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Get the current time (the start of time period 1000) // Get the current time (the start of time period 1000)
@@ -364,8 +270,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(PROTOCOL_VERSION), with(i)); with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction(tags)); will(new EncodeTagAction(tags));
} }
// Updated the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000); oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Save the keys // Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys); oneOf(db).addTransportKeys(txn, contactId, transportKeys);
@@ -385,8 +291,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
txn, contactId, rootKey, timestamp, alice, true)); rootKey, timestamp, alice, true));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
// Use the first tag (previous time period, stream number 0) // Use the first tag (previous time period, stream number 0)
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size()); assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
@@ -406,11 +312,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testKeysAreUpdatedToCurrentPeriod() throws Exception { public void testKeysAreRotatedToCurrentPeriod() throws Exception {
TransportKeys transportKeys = createTransportKeys(1000, 0, true); TransportKeys transportKeys = createTransportKeys(1000, 0, true);
Collection<TransportKeySet> loaded = singletonList( Collection<TransportKeySet> loaded = singletonList(
new TransportKeySet(keySetId, contactId, null, transportKeys)); new TransportKeySet(keySetId, contactId, transportKeys));
TransportKeys updated = createTransportKeys(1001, 0, true); TransportKeys rotated = createTransportKeys(1001, 0, true);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Transaction txn1 = new Transaction(null, false); Transaction txn1 = new Transaction(null, false);
@@ -421,8 +327,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
// Load the transport keys // Load the transport keys
oneOf(db).getTransportKeys(txn, transportId); oneOf(db).getTransportKeys(txn, transportId);
will(returnValue(loaded)); will(returnValue(loaded));
// Update the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000); oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
@@ -431,21 +337,21 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(PROTOCOL_VERSION), with(i)); with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Schedule a key update at the start of the next time period // Schedule key rotation at the start of the next time period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(timePeriodLength), with(MILLISECONDS)); with(timePeriodLength), with(MILLISECONDS));
will(new RunAction()); will(new RunAction());
oneOf(dbExecutor).execute(with(any(Runnable.class))); oneOf(dbExecutor).execute(with(any(Runnable.class)));
will(new RunAction()); will(new RunAction());
// Start a transaction for updating keys // Start a transaction for key rotation
oneOf(db).transaction(with(false), withDbRunnable(txn1)); oneOf(db).transaction(with(false), withDbRunnable(txn1));
// Get the current time (the start of time period 1001) // Get the current time (the start of time period 1001)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(timePeriodLength * 1001)); will(returnValue(timePeriodLength * 1001));
// Update the transport keys // Rotate the transport keys
oneOf(transportCrypto).updateTransportKeys( oneOf(transportCrypto).rotateTransportKeys(
with(any(TransportKeys.class)), with(1001L)); with(any(TransportKeys.class)), with(1001L));
will(returnValue(updated)); will(returnValue(rotated));
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag( exactly(3).of(transportCrypto).encodeTag(
@@ -453,10 +359,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(PROTOCOL_VERSION), with(i)); with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were updated // Save the keys that were rotated
oneOf(db).updateTransportKeys(txn1, singletonList( oneOf(db).updateTransportKeys(txn1, singletonList(
new TransportKeySet(keySetId, contactId, null, updated))); new TransportKeySet(keySetId, contactId, rotated)));
// Schedule a key update at the start of the next time period // Schedule key rotation at the start of the next time period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(timePeriodLength), with(MILLISECONDS)); with(timePeriodLength), with(MILLISECONDS));
}}); }});
@@ -474,7 +380,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
TransportKeys transportKeys = createTransportKeys(1000, 0, false); TransportKeys transportKeys = createTransportKeys(1000, 0, false);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
expectAddContactKeysNotUpdated(alice, false, transportKeys, txn); expectAddContactNoRotation(alice, false, transportKeys, txn);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Activate the keys // Activate the keys
@@ -488,8 +394,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
txn, contactId, rootKey, timestamp, alice, false)); rootKey, timestamp, alice, false));
// The keys are inactive so no stream context should be returned // The keys are inactive so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -516,7 +422,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
List<byte[]> tags = new ArrayList<>(); List<byte[]> tags = new ArrayList<>();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey, oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
1000, alice, false); 1000, alice, false);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Get the current time (the start of time period 1000) // Get the current time (the start of time period 1000)
@@ -529,8 +435,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(PROTOCOL_VERSION), with(i)); with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction(tags)); will(new EncodeTagAction(tags));
} }
// Update the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000); oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Save the keys // Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys); oneOf(db).addTransportKeys(txn, contactId, transportKeys);
@@ -554,8 +460,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is at the start of time period 1000 // The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000; long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys( assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
txn, contactId, rootKey, timestamp, alice, false)); rootKey, timestamp, alice, false));
// The keys are inactive so no stream context should be returned // The keys are inactive so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -580,10 +486,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
assertEquals(0L, ctx.getStreamNumber()); assertEquals(0L, ctx.getStreamNumber());
} }
private void expectAddContactKeysNotUpdated(boolean alice, boolean active, private void expectAddContactNoRotation(boolean alice, boolean active,
TransportKeys transportKeys, Transaction txn) throws Exception { TransportKeys transportKeys, Transaction txn) throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey, oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
1000, alice, active); 1000, alice, active);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Get the current time (the start of time period 1000) // Get the current time (the start of time period 1000)
@@ -596,8 +502,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
with(PROTOCOL_VERSION), with(i)); with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Upate the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000); oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Save the keys // Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys); oneOf(db).addTransportKeys(txn, contactId, transportKeys);
@@ -618,21 +524,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr); return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
} }
@SuppressWarnings("SameParameterValue")
private TransportKeys createHandshakeKeys(long timePeriod,
long streamCounter, boolean alice) {
IncomingKeys inPrev = new IncomingKeys(tagKey, headerKey,
timePeriod - 1);
IncomingKeys inCurr = new IncomingKeys(tagKey, headerKey,
timePeriod);
IncomingKeys inNext = new IncomingKeys(tagKey, headerKey,
timePeriod + 1);
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
timePeriod, streamCounter, true);
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr,
rootKey, alice);
}
private class EncodeTagAction implements Action { private class EncodeTagAction implements Action {
private final Collection<byte[]> tags; private final Collection<byte[]> tags;

View File

@@ -22,8 +22,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 26 targetSdkVersion 26
versionCode 10107 versionCode 10106
versionName "1.1.7" versionName "1.1.6"
applicationId "org.briarproject.briar.android" applicationId "org.briarproject.briar.android"
buildConfigField "String", "GitHash", buildConfigField "String", "GitHash",
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\"" "\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""

View File

@@ -73,12 +73,13 @@
</activity> </activity>
<activity <activity
android:name="org.briarproject.briar.android.login.StartupActivity" android:name="org.briarproject.briar.android.login.PasswordActivity"
android:label="@string/app_name"> android:label="@string/app_name"
android:windowSoftInputMode="stateVisible">
</activity> </activity>
<activity <activity
android:name="org.briarproject.briar.android.account.SetupActivity" android:name="org.briarproject.briar.android.login.SetupActivity"
android:label="@string/setup_title" android:label="@string/setup_title"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
</activity> </activity>
@@ -93,6 +94,11 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".android.login.OpenDatabaseActivity"
android:label="@string/app_name"
android:launchMode="singleTop"/>
<activity <activity
android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity" android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
@@ -414,7 +420,7 @@
</activity> </activity>
<activity <activity
android:name=".android.account.UnlockActivity" android:name=".android.login.UnlockActivity"
android:label="@string/lock_unlock" android:label="@string/lock_unlock"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/BriarTheme.NoActionBar"/> android:theme="@style/BriarTheme.NoActionBar"/>

View File

@@ -1,10 +1,7 @@
package org.briarproject.briar.android; package org.briarproject.briar.android;
import android.app.Activity;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import java.util.Collection; import java.util.Collection;
import java.util.logging.LogRecord; import java.util.logging.LogRecord;
@@ -14,8 +11,6 @@ import java.util.logging.LogRecord;
*/ */
public interface BriarApplication { public interface BriarApplication {
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
Collection<LogRecord> getRecentLogRecords(); Collection<LogRecord> getRecentLogRecords();
AndroidComponent getApplicationComponent(); AndroidComponent getApplicationComponent();

View File

@@ -22,6 +22,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.logout.HideUiActivity; import org.briarproject.briar.android.logout.HideUiActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.LockManager; import org.briarproject.briar.api.android.LockManager;
@@ -47,7 +48,6 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID;
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_ID;
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID;
@@ -182,7 +182,7 @@ public class BriarService extends Service {
NotificationManager nm = (NotificationManager) o; NotificationManager nm = (NotificationManager) o;
nm.notify(FAILURE_NOTIFICATION_ID, b.build()); nm.notify(FAILURE_NOTIFICATION_ID, b.build());
// Bring the dashboard to the front to clear the back stack // Bring the dashboard to the front to clear the back stack
i = new Intent(BriarService.this, ENTRY_ACTIVITY); i = new Intent(BriarService.this, NavDrawerActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP); i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
i.putExtra(EXTRA_STARTUP_FAILED, true); i.putExtra(EXTRA_STARTUP_FAILED, true);
startActivity(i); startActivity(i);

View File

@@ -74,7 +74,7 @@ public class StartupFailureActivity extends BaseActivity implements
@Override @Override
public void runOnDbThread(@NonNull Runnable runnable) { public void runOnDbThread(@NonNull Runnable runnable) {
throw new UnsupportedOperationException(); throw new AssertionError("Deprecated and should not be used");
} }
} }

View File

@@ -1,131 +0,0 @@
package org.briarproject.briar.android.account;
import android.os.Bundle;
import android.os.IBinder;
import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.ProgressBar;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.login.StrengthMeter;
import javax.annotation.Nullable;
import static android.content.Context.INPUT_METHOD_SERVICE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
import static java.util.Objects.requireNonNull;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
import static org.briarproject.briar.android.util.UiUtils.setError;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class SetPasswordFragment extends SetupFragment {
private final static String TAG = SetPasswordFragment.class.getName();
private TextInputLayout passwordEntryWrapper;
private TextInputLayout passwordConfirmationWrapper;
private TextInputEditText passwordEntry;
private TextInputEditText passwordConfirmation;
private StrengthMeter strengthMeter;
private Button nextButton;
private ProgressBar progressBar;
public static SetPasswordFragment newInstance() {
return new SetPasswordFragment();
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
requireNonNull(getActivity()).setTitle(getString(R.string.setup_password_intro));
View v = inflater.inflate(R.layout.fragment_setup_password, container,
false);
strengthMeter = v.findViewById(R.id.strength_meter);
passwordEntryWrapper = v.findViewById(R.id.password_entry_wrapper);
passwordEntry = v.findViewById(R.id.password_entry);
passwordEntry.requestFocus();
passwordConfirmationWrapper =
v.findViewById(R.id.password_confirm_wrapper);
passwordConfirmation = v.findViewById(R.id.password_confirm);
nextButton = v.findViewById(R.id.next);
progressBar = v.findViewById(R.id.progress);
passwordEntry.addTextChangedListener(this);
passwordConfirmation.addTextChangedListener(this);
nextButton.setOnClickListener(this);
if (!setupController.needToShowDozeFragment()) {
nextButton.setText(R.string.create_account_button);
passwordConfirmation.setImeOptions(IME_ACTION_DONE);
}
return v;
}
@Override
public String getUniqueTag() {
return TAG;
}
@Override
protected String getHelpText() {
return getString(R.string.setup_password_explanation);
}
@Override
public void onTextChanged(CharSequence authorName, int i, int i1, int i2) {
String password1 = passwordEntry.getText().toString();
String password2 = passwordConfirmation.getText().toString();
boolean passwordsMatch = password1.equals(password2);
strengthMeter
.setVisibility(password1.length() > 0 ? VISIBLE : INVISIBLE);
float strength = setupController.estimatePasswordStrength(password1);
strengthMeter.setStrength(strength);
boolean strongEnough = strength >= QUITE_WEAK;
setError(passwordEntryWrapper, getString(R.string.password_too_weak),
password1.length() > 0 && !strongEnough);
setError(passwordConfirmationWrapper,
getString(R.string.passwords_do_not_match),
password2.length() > 0 && !passwordsMatch);
boolean enabled = passwordsMatch && strongEnough;
nextButton.setEnabled(enabled);
passwordConfirmation.setOnEditorActionListener(enabled ? this : null);
}
@Override
public void onClick(View view) {
IBinder token = passwordEntry.getWindowToken();
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
setupController.setPassword(passwordEntry.getText().toString());
if (setupController.needToShowDozeFragment()) {
setupController.showDozeFragment();
} else {
nextButton.setVisibility(INVISIBLE);
progressBar.setVisibility(VISIBLE);
setupController.createAccount();
}
}
}

View File

@@ -4,11 +4,6 @@ import android.app.Activity;
import org.briarproject.briar.android.AndroidComponent; import org.briarproject.briar.android.AndroidComponent;
import org.briarproject.briar.android.StartupFailureActivity; import org.briarproject.briar.android.StartupFailureActivity;
import org.briarproject.briar.android.account.AuthorNameFragment;
import org.briarproject.briar.android.account.DozeFragment;
import org.briarproject.briar.android.account.SetPasswordFragment;
import org.briarproject.briar.android.account.SetupActivity;
import org.briarproject.briar.android.account.UnlockActivity;
import org.briarproject.briar.android.blog.BlogActivity; import org.briarproject.briar.android.blog.BlogActivity;
import org.briarproject.briar.android.blog.BlogFragment; import org.briarproject.briar.android.blog.BlogFragment;
import org.briarproject.briar.android.blog.BlogModule; import org.briarproject.briar.android.blog.BlogModule;
@@ -42,10 +37,14 @@ import org.briarproject.briar.android.keyagreement.ContactExchangeActivity;
import org.briarproject.briar.android.keyagreement.ContactExchangeErrorFragment; import org.briarproject.briar.android.keyagreement.ContactExchangeErrorFragment;
import org.briarproject.briar.android.keyagreement.KeyAgreementActivity; import org.briarproject.briar.android.keyagreement.KeyAgreementActivity;
import org.briarproject.briar.android.keyagreement.KeyAgreementFragment; import org.briarproject.briar.android.keyagreement.KeyAgreementFragment;
import org.briarproject.briar.android.login.AuthorNameFragment;
import org.briarproject.briar.android.login.ChangePasswordActivity; import org.briarproject.briar.android.login.ChangePasswordActivity;
import org.briarproject.briar.android.login.OpenDatabaseFragment; import org.briarproject.briar.android.login.DozeFragment;
import org.briarproject.briar.android.login.OpenDatabaseActivity;
import org.briarproject.briar.android.login.PasswordActivity;
import org.briarproject.briar.android.login.PasswordFragment; import org.briarproject.briar.android.login.PasswordFragment;
import org.briarproject.briar.android.login.StartupActivity; import org.briarproject.briar.android.login.SetupActivity;
import org.briarproject.briar.android.login.UnlockActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.panic.PanicPreferencesActivity; import org.briarproject.briar.android.panic.PanicPreferencesActivity;
import org.briarproject.briar.android.panic.PanicResponderActivity; import org.briarproject.briar.android.panic.PanicResponderActivity;
@@ -95,12 +94,14 @@ public interface ActivityComponent {
void inject(SplashScreenActivity activity); void inject(SplashScreenActivity activity);
void inject(StartupActivity activity);
void inject(SetupActivity activity); void inject(SetupActivity activity);
void inject(OpenDatabaseActivity activity);
void inject(NavDrawerActivity activity); void inject(NavDrawerActivity activity);
void inject(PasswordActivity activity);
void inject(PanicResponderActivity activity); void inject(PanicResponderActivity activity);
void inject(PanicPreferencesActivity activity); void inject(PanicPreferencesActivity activity);
@@ -176,17 +177,12 @@ public interface ActivityComponent {
void inject(PendingContactListActivity activity); void inject(PendingContactListActivity activity);
// Fragments // Fragments
void inject(AuthorNameFragment fragment); void inject(AuthorNameFragment fragment);
void inject(SetPasswordFragment fragment); void inject(PasswordFragment fragment);
void inject(DozeFragment fragment); void inject(DozeFragment fragment);
void inject(PasswordFragment imageFragment);
void inject(OpenDatabaseFragment activity);
void inject(ContactListFragment fragment); void inject(ContactListFragment fragment);
void inject(CreateGroupFragment fragment); void inject(CreateGroupFragment fragment);

View File

@@ -6,10 +6,10 @@ import org.briarproject.briar.android.controller.BriarController;
import org.briarproject.briar.android.controller.BriarControllerImpl; import org.briarproject.briar.android.controller.BriarControllerImpl;
import org.briarproject.briar.android.controller.DbController; import org.briarproject.briar.android.controller.DbController;
import org.briarproject.briar.android.controller.DbControllerImpl; import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.login.ChangePasswordController; import org.briarproject.briar.android.login.PasswordController;
import org.briarproject.briar.android.login.ChangePasswordControllerImpl; import org.briarproject.briar.android.login.PasswordControllerImpl;
import org.briarproject.briar.android.account.SetupController; import org.briarproject.briar.android.login.SetupController;
import org.briarproject.briar.android.account.SetupControllerImpl; import org.briarproject.briar.android.login.SetupControllerImpl;
import org.briarproject.briar.android.navdrawer.NavDrawerController; import org.briarproject.briar.android.navdrawer.NavDrawerController;
import org.briarproject.briar.android.navdrawer.NavDrawerControllerImpl; import org.briarproject.briar.android.navdrawer.NavDrawerControllerImpl;
@@ -48,8 +48,8 @@ public class ActivityModule {
@ActivityScope @ActivityScope
@Provides @Provides
ChangePasswordController providePasswordController( PasswordController providePasswordController(
ChangePasswordControllerImpl passwordController) { PasswordControllerImpl passwordController) {
return passwordController; return passwordController;
} }

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