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 {
minSdkVersion 14
targetSdkVersion 26
versionCode 10107
versionName "1.1.7"
versionCode 10106
versionName "1.1.6"
consumerProguardFiles 'proguard-rules.txt'
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.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.util.IoUtils;
import java.io.File;
import java.util.HashSet;
@@ -19,9 +20,6 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
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
implements AccountManager {
@@ -43,16 +41,6 @@ class AndroidAccountManager extends AccountManagerImpl
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
@Override
@Nullable
@@ -86,17 +74,9 @@ class AndroidAccountManager extends AccountManagerImpl
@Override
public void deleteAccount() {
synchronized (stateChangeLock) {
if (LOG.isLoggable(INFO)) {
LOG.info("Contents of account directory before deleting:");
logFileOrDir(LOG, INFO, getDataDir());
}
super.deleteAccount();
SharedPreferences defaultPrefs = getDefaultSharedPreferences();
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
Set<File> files = new HashSet<>();
File dataDir = getDataDir();
File dataDir = new File(appContext.getApplicationInfo().dataDir);
@Nullable
File[] fileArray = dataDir.listFiles();
if (fileArray == null) {
@@ -141,17 +121,13 @@ class AndroidAccountManager extends AccountManagerImpl
}
}
for (File file : files) {
deleteFileOrDir(file);
IoUtils.deleteFileOrDir(file);
}
// Recreate the cache dir as some OpenGL drivers expect it to exist
if (!new File(dataDir, "cache").mkdirs())
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) {
if (file != null) files.add(file);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@@ -10,12 +9,12 @@ import javax.annotation.concurrent.Immutable;
public class PendingContact {
private final PendingContactId id;
private final PublicKey publicKey;
private final byte[] publicKey;
private final String alias;
private final PendingContactState state;
private final long timestamp;
public PendingContact(PendingContactId id, PublicKey publicKey,
public PendingContact(PendingContactId id, byte[] publicKey,
String alias, PendingContactState state, long timestamp) {
this.id = id;
this.publicKey = publicKey;
@@ -28,7 +27,7 @@ public class PendingContact {
return id;
}
public PublicKey getPublicKey() {
public byte[] getPublicKey() {
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.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
/**
@@ -18,8 +17,9 @@ public class PendingContactId extends UniqueId {
super(id);
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object 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 created for another purpose
*/
byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException;
/**
@@ -68,7 +68,7 @@ public interface CryptoComponent {
* @return true if the signature was valid, false otherwise.
*/
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

View File

@@ -7,21 +7,11 @@ public interface CryptoConstants {
*/
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.
*/
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.
*/

View File

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

View File

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

View File

@@ -8,11 +8,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface PublicKey {
/**
* Returns the type of this key pair.
*/
String getKeyType();
/**
* 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;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeys;
/**
@@ -10,29 +11,35 @@ import org.briarproject.bramble.api.transport.TransportKeys;
public interface TransportCrypto {
/**
* Derives initial rotation mode transport keys for the given transport in
* the given time period from the given root key.
* Derives initial transport keys for the given transport in the given
* time period from the given root key.
*
* @param alice Whether the keys are for use by Alice or Bob
* @param active Whether the keys are usable for outgoing streams
* @param alice whether the keys are for use by Alice or Bob.
* @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);
/**
* 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
* 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);
/**
* 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.
*/
TransportKeys updateTransportKeys(TransportKeys k, long timePeriod);
HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod);
/**
* 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.PendingContact;
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.identity.Author;
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.Request;
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.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection;
@@ -112,6 +113,20 @@ public interface DatabaseComponent {
*/
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.
*/
@@ -139,14 +154,7 @@ public interface DatabaseComponent {
* Stores the given transport keys for the given contact and returns a
* key set ID.
*/
KeySetId addTransportKeys(Transaction 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(Transaction txn, PendingContactId p,
TransportKeySetId addTransportKeys(Transaction txn, ContactId c,
TransportKeys k) throws DbException;
/**
@@ -266,7 +274,7 @@ public interface DatabaseComponent {
* <p/>
* Read-only.
*/
Collection<ContactId> getContacts(Transaction txn, AuthorId local)
Collection<ContactId> getContacts(Transaction txn, AuthorId a)
throws DbException;
/**
@@ -300,6 +308,14 @@ public interface DatabaseComponent {
Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g)
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.
* <p/>
@@ -471,11 +487,17 @@ public interface DatabaseComponent {
Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t)
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.
*/
void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k)
throws DbException;
void incrementStreamCounter(Transaction txn, TransportId t,
TransportKeySetId k) throws DbException;
/**
* 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;
/**
* 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.
*/
@@ -554,8 +582,8 @@ public interface DatabaseComponent {
/**
* Removes the given transport keys from the database.
*/
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
throws DbException;
void removeTransportKeys(Transaction txn, TransportId t,
TransportKeySetId k) throws DbException;
/**
* 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.
*/
void setHandshakeKeyPair(Transaction txn, AuthorId local,
PublicKey publicKey, PrivateKey privateKey) throws DbException;
void setHandshakeKeyPair(Transaction txn, AuthorId local, byte[] publicKey,
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.
*/
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t,
long timePeriod, long base, byte[] bitmap) throws DbException;
void setReorderingWindow(Transaction 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(Transaction txn, HandshakeKeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException;
/**
* 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;
/**

View File

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

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
@@ -10,12 +9,12 @@ public interface AuthorFactory {
* Creates an author with the current format version and the given name and
* 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.
*/
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

View File

@@ -1,13 +1,13 @@
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 java.util.Arrays;
import javax.annotation.Nullable;
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
@NotNullByDefault
@@ -15,24 +15,15 @@ public class Identity {
private final LocalAuthor localAuthor;
@Nullable
private final PublicKey handshakePublicKey;
@Nullable
private final PrivateKey handshakePrivateKey;
private final byte[] handshakePublicKey, handshakePrivateKey;
private final long created;
public Identity(LocalAuthor localAuthor,
@Nullable PublicKey handshakePublicKey,
@Nullable PrivateKey handshakePrivateKey, long created) {
@Nullable byte[] handshakePublicKey,
@Nullable byte[] handshakePrivateKey, long created) {
if (handshakePublicKey != null) {
if (handshakePrivateKey == null)
throw new IllegalArgumentException();
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))
int keyLength = handshakePublicKey.length;
if (keyLength == 0 || keyLength > MAX_AGREEMENT_PUBLIC_KEY_BYTES)
throw new IllegalArgumentException();
}
this.localAuthor = localAuthor;
@@ -66,7 +57,7 @@ public class Identity {
* Returns the public key used for handshaking, or null if no key exists.
*/
@Nullable
public PublicKey getHandshakePublicKey() {
public byte[] getHandshakePublicKey() {
return handshakePublicKey;
}
@@ -74,7 +65,7 @@ public class Identity {
* Returns the private key used for handshaking, or null if no key exists.
*/
@Nullable
public PrivateKey getHandshakePrivateKey() {
public byte[] getHandshakePrivateKey() {
return handshakePrivateKey;
}
@@ -85,4 +76,21 @@ public class Identity {
public long getTimeCreated() {
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;
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.db.DbException;
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.
* <p/>
* 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;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
/**
* A pseudonym for the local user.
*/
@@ -15,20 +11,18 @@ import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNA
@NotNullByDefault
public class LocalAuthor extends Author {
private final PrivateKey privateKey;
private final byte[] privateKey;
public LocalAuthor(AuthorId id, int formatVersion, String name,
PublicKey publicKey, PrivateKey privateKey) {
byte[] publicKey, byte[] privateKey) {
super(id, formatVersion, name, publicKey);
if (!privateKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
throw new IllegalArgumentException();
this.privateKey = privateKey;
}
/**
* Returns the private key used to generate the pseudonym's signatures.
*/
public PrivateKey getPrivateKey() {
public byte[] getPrivateKey() {
return privateKey;
}
}

View File

@@ -6,20 +6,10 @@ import javax.annotation.Nullable;
public class NullSafety {
/**
* Stand-in for {@code Objects.requireNonNull()}.
* Stand-in for `Objects.requireNonNull()`.
*/
public static <T> T requireNonNull(@Nullable T t) {
if (t == null) throw new NullPointerException();
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;
/**
* Type-safe wrapper for an integer that uniquely identifies a set of
* {@link TransportKeySet transport keys} within the scope of the local device.
* Type-safe wrapper for an integer that uniquely identifies a
* {@link HandshakeKeySet set of handshake keys} within the scope of the local
* device.
*/
@Immutable
@NotNullByDefault
public class KeySetId {
public class HandshakeKeySetId {
private final int id;
public KeySetId(int id) {
public HandshakeKeySetId(int id) {
this.id = id;
}
@@ -29,6 +30,7 @@ public class KeySetId {
@Override
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;
/**
* Contains transport keys for receiving streams from a given contact or
* pending contact over a given transport in a given time period.
* Contains transport keys for receiving streams from a given contact over a
* given transport in a given time period.
*/
@Immutable
@NotNullByDefault

View File

@@ -1,7 +1,6 @@
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.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException;
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
* stores a set of rotation mode transport keys for communicating with the
* contact over each transport and returns the key set IDs.
* stores a set of 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
* @param active Whether the derived keys can be used for outgoing streams
* @param alice true if the local party is Alice
* @param active whether the derived keys can be used for outgoing streams
*/
Map<TransportId, KeySetId> addContactWithRotationKeys(Transaction txn,
ContactId c, SecretKey rootKey, long timestamp, boolean alice,
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)
Map<TransportId, TransportKeySetId> addContact(Transaction txn, ContactId c,
SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException;
/**
* 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;
/**
@@ -72,28 +43,15 @@ public interface KeyManager {
*/
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
* 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
StreamContext getStreamContext(ContactId c, TransportId t)
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
* 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;
/**
* Contains transport keys for sending streams to a given contact or pending
* contact over a given transport in a given time period.
* Contains transport keys for sending streams to a given contact over a given
* transport in a given time period.
*/
@Immutable
@NotNullByDefault

View File

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

View File

@@ -1,52 +1,37 @@
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;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
/**
* A set of keys for communicating with a given contact or pending contact
* over a given transport.
* A set of keys for communicating with a given contact over a given transport.
* Unlike a {@link HandshakeKeySet} these keys provide forward secrecy.
*/
@Immutable
@NotNullByDefault
public class TransportKeySet {
private final KeySetId keySetId;
@Nullable
private final TransportKeySetId keySetId;
private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final TransportKeys keys;
public TransportKeySet(KeySetId keySetId, @Nullable ContactId contactId,
@Nullable PendingContactId pendingContactId, TransportKeys keys) {
requireExactlyOneNull(contactId, pendingContactId);
public TransportKeySet(TransportKeySetId keySetId, ContactId contactId,
TransportKeys keys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.pendingContactId = pendingContactId;
this.keys = keys;
}
public KeySetId getKeySetId() {
public TransportKeySetId getKeySetId() {
return keySetId;
}
@Nullable
public ContactId getContactId() {
return contactId;
}
@Nullable
public PendingContactId getPendingContactId() {
return pendingContactId;
}
public TransportKeys getKeys() {
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;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* Keys for communicating with a given contact or pending contact over a given
* transport.
* Keys for communicating with a given contact over a given transport. Unlike
* {@link HandshakeKeys} these keys provide forward secrecy.
*/
@Immutable
@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,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
this(transportId, inPrev, inCurr, inNext, outCurr, null, false);
}
/**
* 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;
super(transportId, inPrev, inCurr, inNext, outCurr);
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.util;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -20,7 +19,6 @@ public class LogUtils {
/**
* Logs the duration of a task.
*
* @param logger the logger to use
* @param task a description of the task
* @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) {
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.PendingContact;
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.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.Identity;
@@ -37,10 +30,11 @@ import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
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_SIGNATURE_PUBLIC_KEY_BYTES;
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_PUBLIC_KEY_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.sync.ClientId.MAX_CLIENT_ID_LENGTH;
@@ -107,28 +101,10 @@ public class TestUtils {
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() {
LocalAuthor localAuthor = getLocalAuthor();
PublicKey handshakePub = getAgreementPublicKey();
PrivateKey handshakePriv = getAgreementPrivateKey();
byte[] handshakePub = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
byte[] handshakePriv = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
return new Identity(localAuthor, handshakePub, handshakePriv,
timestamp);
}
@@ -137,8 +113,8 @@ public class TestUtils {
AuthorId id = new AuthorId(getRandomId());
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
String name = getRandomString(nameLength);
PublicKey publicKey = getSignaturePublicKey();
PrivateKey privateKey = getSignaturePrivateKey();
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
byte[] privateKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
}
@@ -146,7 +122,7 @@ public class TestUtils {
AuthorId id = new AuthorId(getRandomId());
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
String name = getRandomString(nameLength);
PublicKey publicKey = getSignaturePublicKey();
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
return new Author(id, FORMAT_VERSION, name, publicKey);
}
@@ -179,12 +155,10 @@ public class TestUtils {
public static PendingContact getPendingContact(int nameLength) {
PendingContactId id = new PendingContactId(getRandomId());
PublicKey publicKey = getAgreementPublicKey();
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
String alias = getRandomString(nameLength);
int stateIndex =
random.nextInt(PendingContactState.values().length - 1);
PendingContactState state = PendingContactState.values()[stateIndex];
return new PendingContact(id, publicKey, alias, state, timestamp);
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
timestamp);
}
public static ContactId getContactId() {
@@ -205,7 +179,7 @@ public class TestUtils {
boolean verified) {
return new Contact(c, remote, local,
getRandomString(MAX_AUTHOR_NAME_LENGTH),
getAgreementPublicKey(), verified);
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), verified);
}
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.client.ClientHelper;
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.BdfList;
import org.briarproject.bramble.api.data.BdfReader;
@@ -308,15 +305,14 @@ class ClientHelperImpl implements ClientHelper {
}
@Override
public byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
public byte[] sign(String label, BdfList toSign, byte[] privateKey)
throws FormatException, GeneralSecurityException {
return crypto.sign(label, toByteArray(toSign), privateKey);
}
@Override
public void verifySignature(byte[] signature, String label, BdfList signed,
PublicKey publicKey)
throws FormatException, GeneralSecurityException {
byte[] publicKey) throws FormatException, GeneralSecurityException {
if (!crypto.verifySignature(signature, label, toByteArray(signed),
publicKey)) {
throw new GeneralSecurityException("Invalid signature");
@@ -331,29 +327,11 @@ class ClientHelperImpl implements ClientHelper {
if (formatVersion != FORMAT_VERSION) throw new FormatException();
String name = author.getString(1);
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
byte[] publicKeyBytes = author.getRaw(2);
checkLength(publicKeyBytes, 1, MAX_PUBLIC_KEY_LENGTH);
KeyParser parser = crypto.getSignatureKeyParser();
PublicKey publicKey;
try {
publicKey = parser.parsePublicKey(publicKeyBytes);
} catch (GeneralSecurityException e) {
throw new FormatException();
}
byte[] publicKey = author.getRaw(2);
checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH);
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
public TransportProperties parseAndValidateTransportProperties(
BdfDictionary properties) throws FormatException {

View File

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

View File

@@ -39,8 +39,8 @@ class PendingContactFactoryImpl implements PendingContactFactory {
PublicKey publicKey = parseHandshakeLink(link);
PendingContactId id = getPendingContactId(publicKey);
long timestamp = clock.currentTimeMillis();
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
timestamp);
return new PendingContact(id, publicKey.getEncoded(), alias,
WAITING_FOR_CONNECTION, timestamp);
}
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.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.KeyPair;
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.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.system.SecureRandomProvider;
import org.briarproject.bramble.util.ByteUtils;
@@ -35,8 +31,6 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
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.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.now;
@@ -86,8 +80,8 @@ class CryptoComponentImpl implements CryptoComponent {
signatureKeyPairGenerator = new KeyPairGenerator();
signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS,
secureRandom);
agreementKeyParser = new AgreementKeyParser();
signatureKeyParser = new SignatureKeyParser();
agreementKeyParser = new Curve25519KeyParser();
signatureKeyParser = new EdKeyParser();
messageEncrypter = new MessageEncrypter(secureRandom);
}
@@ -131,9 +125,9 @@ class CryptoComponentImpl implements CryptoComponent {
// Package access for testing
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
throws GeneralSecurityException {
if (!priv.getKeyType().equals(KEY_TYPE_AGREEMENT))
if (!(priv instanceof Curve25519PrivateKey))
throw new IllegalArgumentException();
if (!pub.getKeyType().equals(KEY_TYPE_AGREEMENT))
if (!(pub instanceof Curve25519PublicKey))
throw new IllegalArgumentException();
long start = now();
byte[] secret = curve25519.calculateAgreement(pub.getEncoded(),
@@ -149,8 +143,8 @@ class CryptoComponentImpl implements CryptoComponent {
@Override
public KeyPair generateAgreementKeyPair() {
Curve25519KeyPair keyPair = curve25519.generateKeyPair();
PublicKey pub = new AgreementPublicKey(keyPair.getPublicKey());
PrivateKey priv = new AgreementPrivateKey(keyPair.getPrivateKey());
PublicKey pub = new Curve25519PublicKey(keyPair.getPublicKey());
PrivateKey priv = new Curve25519PrivateKey(keyPair.getPrivateKey());
return new KeyPair(pub, priv);
}
@@ -164,9 +158,9 @@ class CryptoComponentImpl implements CryptoComponent {
java.security.KeyPair keyPair =
signatureKeyPairGenerator.generateKeyPair();
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
PublicKey publicKey = new SignaturePublicKey(edPublicKey.getAbyte());
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
PrivateKey privateKey = new SignaturePrivateKey(edPrivateKey.getSeed());
PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed());
return new KeyPair(publicKey, privateKey);
}
@@ -201,22 +195,21 @@ class CryptoComponentImpl implements CryptoComponent {
}
@Override
public byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException {
PrivateKey key = signatureKeyParser.parsePrivateKey(privateKey);
Signature sig = new EdSignature();
sig.initSign(privateKey);
sig.initSign(key);
updateSignature(sig, label, toSign);
return sig.sign();
}
@Override
public boolean verifySignature(byte[] signature, String label,
byte[] signed, PublicKey publicKey)
throws GeneralSecurityException {
if (!publicKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
throw new IllegalArgumentException();
byte[] signed, byte[] publicKey) throws GeneralSecurityException {
PublicKey key = signatureKeyParser.parsePublicKey(publicKey);
Signature sig = new EdSignature();
sig.initVerify(publicKey);
sig.initVerify(key);
updateSignature(sig, label, signed);
return sig.verify(signature);
}

View File

@@ -1,7 +1,5 @@
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.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
@@ -9,24 +7,21 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class AgreementKeyParser implements KeyParser {
class Curve25519KeyParser implements KeyParser {
@Override
public PublicKey parsePublicKey(byte[] encodedKey)
throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException();
return new AgreementPublicKey(encodedKey);
return new Curve25519PublicKey(encodedKey);
}
@Override
public PrivateKey parsePrivateKey(byte[] encodedKey)
throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException();
return new AgreementPrivateKey(clamp(encodedKey));
return new Curve25519PrivateKey(clamp(encodedKey));
}
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.PrivateKey;
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 java.security.GeneralSecurityException;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class SignatureKeyParser implements KeyParser {
class EdKeyParser implements KeyParser {
@Override
public PublicKey parsePublicKey(byte[] encodedKey)
throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException();
return new SignaturePublicKey(encodedKey);
return new EdPublicKey(encodedKey);
}
@Override
public PrivateKey parsePrivateKey(byte[] encodedKey)
throws 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 static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
@NotNullByDefault
class EdSignature implements Signature {
@@ -40,7 +39,7 @@ class EdSignature implements Signature {
@Override
public void initSign(PrivateKey k) throws GeneralSecurityException {
if (!k.getKeyType().equals(KEY_TYPE_SIGNATURE))
if (!(k instanceof EdPrivateKey))
throw new IllegalArgumentException();
EdDSAPrivateKey privateKey = new EdDSAPrivateKey(
new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC));
@@ -49,7 +48,7 @@ class EdSignature implements Signature {
@Override
public void initVerify(PublicKey k) throws GeneralSecurityException {
if (!k.getKeyType().equals(KEY_TYPE_SIGNATURE))
if (!(k instanceof EdPublicKey))
throw new IllegalArgumentException();
EdDSAPublicKey publicKey = new EdDSAPublicKey(
new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC));

View File

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

View File

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

View File

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

View File

@@ -15,19 +15,12 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault
class Sec1PublicKey implements PublicKey {
private final String keyType;
private final ECPublicKeyParameters key;
Sec1PublicKey(String keyType, ECPublicKeyParameters key) {
this.keyType = keyType;
Sec1PublicKey(ECPublicKeyParameters key) {
this.key = key;
}
@Override
public String getKeyType() {
return keyType;
}
@Override
public byte[] getEncoded() {
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.TransportCrypto;
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.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys;
@@ -41,7 +42,7 @@ class TransportCryptoImpl implements TransportCrypto {
}
@Override
public TransportKeys deriveRotationKeys(TransportId t,
public TransportKeys deriveTransportKeys(TransportId t,
SecretKey rootKey, long timePeriod, boolean weAreAlice,
boolean active) {
// 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);
}
@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) {
byte[] period = new byte[INT_64_BYTES];
writeUint64(timePeriod, period, 0);
@@ -91,7 +117,7 @@ class TransportCryptoImpl implements TransportCrypto {
}
@Override
public TransportKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
public HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean weAreAlice) {
if (timePeriod < 1) throw new IllegalArgumentException();
IncomingKeys inPrev = deriveIncomingHandshakeKeys(t, rootKey,
@@ -102,7 +128,7 @@ class TransportCryptoImpl implements TransportCrypto {
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr, rootKey,
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr, rootKey,
weAreAlice);
}
@@ -145,13 +171,7 @@ class TransportCryptoImpl implements TransportCrypto {
}
@Override
public TransportKeys updateTransportKeys(TransportKeys k, long timePeriod) {
if (k.isHandshakeMode()) return updateHandshakeKeys(k, timePeriod);
else return updateRotationKeys(k, timePeriod);
}
private TransportKeys updateHandshakeKeys(TransportKeys k,
long timePeriod) {
public HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod) {
long elapsed = timePeriod - k.getTimePeriod();
TransportId t = k.getTransportId();
SecretKey rootKey = k.getRootKey();
@@ -168,7 +188,7 @@ class TransportCryptoImpl implements TransportCrypto {
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr,
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
rootKey, weAreAlice);
} else if (elapsed == 2) {
// The keys are two periods old - shift by two periods, keeping
@@ -180,7 +200,7 @@ class TransportCryptoImpl implements TransportCrypto {
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr,
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
rootKey, weAreAlice);
} else {
// 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
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
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.PendingContactId;
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.db.DataTooNewException;
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.MessageStatus;
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.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection;
@@ -104,6 +105,20 @@ interface Database<T> {
void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
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.
*/
@@ -145,14 +160,7 @@ interface Database<T> {
* Stores the given transport keys for the given contact and returns a
* key set ID.
*/
KeySetId 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)
TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
throws DbException;
/**
@@ -265,7 +273,7 @@ interface Database<T> {
* <p/>
* 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.
@@ -307,6 +315,14 @@ interface Database<T> {
Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
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.
* <p/>
@@ -529,10 +545,16 @@ interface Database<T> {
Collection<TransportKeySet> getTransportKeys(T txn, TransportId t)
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.
*/
void incrementStreamCounter(T txn, TransportId t, KeySetId k)
void incrementStreamCounter(T txn, TransportId t, TransportKeySetId k)
throws DbException;
/**
@@ -601,6 +623,12 @@ interface Database<T> {
void removeGroupVisibility(T txn, ContactId c, GroupId g)
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.
*/
@@ -631,7 +659,8 @@ interface Database<T> {
/**
* 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
@@ -660,8 +689,8 @@ interface Database<T> {
/**
* Sets the handshake key pair for the identity with the given ID.
*/
void setHandshakeKeyPair(T txn, AuthorId local, PublicKey publicKey,
PrivateKey privateKey) throws DbException;
void setHandshakeKeyPair(T txn, AuthorId local, byte[] publicKey,
byte[] privateKey) throws DbException;
/**
* Marks the given message as shared.
@@ -681,16 +710,23 @@ interface Database<T> {
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.
*/
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;
/**
* 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;
/**
@@ -702,7 +738,12 @@ interface Database<T> {
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;
}

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.ContactRemovedEvent;
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.db.CommitAction;
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.MessagesSentEvent;
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.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
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
public void addIdentity(Transaction transaction, Identity i)
throws DbException {
@@ -295,8 +318,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (db.containsPendingContact(txn, p.getId()))
throw new PendingContactExistsException();
db.addPendingContact(txn, p);
transaction.attach(new PendingContactStateChangedEvent(p.getId(),
p.getState()));
}
@Override
@@ -309,8 +330,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public KeySetId addTransportKeys(Transaction transaction, ContactId c,
TransportKeys k) throws DbException {
public TransportKeySetId addTransportKeys(Transaction transaction,
ContactId c, TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
@@ -320,18 +341,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public boolean containsContact(Transaction transaction, AuthorId remote,
AuthorId local) throws DbException {
@@ -494,11 +503,11 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override
public Collection<ContactId> getContacts(Transaction transaction,
AuthorId local) throws DbException {
AuthorId a) throws DbException {
T txn = unbox(transaction);
if (!db.containsIdentity(txn, local))
if (!db.containsIdentity(txn, a))
throw new NoSuchIdentityException();
return db.getContacts(txn, local);
return db.getContacts(txn, a);
}
@Override
@@ -535,6 +544,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public Identity getIdentity(Transaction transaction, AuthorId a)
throws DbException {
@@ -717,7 +735,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override
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();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
@@ -866,6 +894,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public void removeIdentity(Transaction transaction, AuthorId a)
throws DbException {
@@ -896,7 +934,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
db.removePendingContact(txn, p);
transaction.attach(new PendingContactRemovedEvent(p));
}
@Override
@@ -910,8 +947,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public void removeTransportKeys(Transaction transaction, TransportId t,
KeySetId k) throws DbException {
public void removeTransportKeys(Transaction transaction,
TransportId t, TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
@@ -1000,7 +1037,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override
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();
T txn = unbox(transaction);
if (!db.containsIdentity(txn, local))
@@ -1009,9 +1046,20 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public void setReorderingWindow(Transaction transaction, KeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException {
public void setReorderingWindow(Transaction transaction,
TransportKeySetId k, TransportId t, long timePeriod, long base,
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();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
@@ -1021,7 +1069,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override
public void setTransportKeysActive(Transaction transaction, TransportId t,
KeySetId k) throws DbException {
TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
@@ -1029,6 +1077,18 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public void updateTransportKeys(Transaction transaction,
Collection<TransportKeySet> keys) throws DbException {

View File

@@ -20,11 +20,9 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
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.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
/**
* 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)
throws DbException {
this.key = key;
File dir = config.getDatabaseDirectory();
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);
boolean reopen = !config.getDatabaseDirectory().mkdirs();
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;
}

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.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.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
import org.briarproject.bramble.api.db.DataTooNewException;
import org.briarproject.bramble.api.db.DataTooOldException;
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.validation.MessageState;
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.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.sql.Connection;
@@ -65,13 +62,11 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.sql.Types.BINARY;
import static java.sql.Types.BOOLEAN;
import static java.sql.Types.INTEGER;
import static java.sql.Types.VARCHAR;
import static java.util.Arrays.asList;
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.api.db.Metadata.REMOVE;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
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> {
// 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
private static final int OFFSET_PREV = -1;
@@ -259,28 +254,16 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " maxLatency INT NOT NULL,"
+ " 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 =
"CREATE TABLE outgoingKeys"
+ " (transportId _STRING NOT NULL,"
+ " keySetId _COUNTER,"
+ " timePeriod BIGINT NOT NULL,"
+ " contactId INT," // Null if contact is pending
+ " pendingContactId _HASH," // Null if not pending
+ " contactId INT NOT NULL,"
+ " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL,"
+ " stream BIGINT NOT NULL,"
+ " active BOOLEAN NOT NULL,"
+ " rootKey _SECRET," // Null for rotation keys
+ " alice BOOLEAN," // Null for rotation keys
+ " PRIMARY KEY (transportId, keySetId),"
+ " FOREIGN KEY (transportId)"
+ " REFERENCES transports (transportId)"
@@ -288,9 +271,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " 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_KEYS =
@@ -311,6 +291,57 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " REFERENCES outgoingKeys (keySetId)"
+ " 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 =
"CREATE INDEX IF NOT EXISTS contactsByAuthorId"
+ " ON contacts (authorId)";
@@ -336,7 +367,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " ON statuses (contactId, timestamp)";
private static final Logger LOG =
getLogger(JdbcDatabase.class.getName());
Logger.getLogger(JdbcDatabase.class.getName());
// Different database libraries use different names for certain types
private final MessageFactory messageFactory;
@@ -456,8 +487,7 @@ abstract class JdbcDatabase implements Database<Connection> {
new Migration39_40(),
new Migration40_41(dbTypes),
new Migration41_42(dbTypes),
new Migration42_43(dbTypes),
new Migration43_44(dbTypes)
new Migration42_43(dbTypes)
);
}
@@ -505,9 +535,13 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(dbTypes.replaceTypes(CREATE_OFFERS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_STATUSES));
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_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();
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
@@ -643,7 +677,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(1, remote.getId().getBytes());
ps.setInt(2, remote.getFormatVersion());
ps.setString(3, remote.getName());
ps.setBytes(4, remote.getPublicKey().getEncoded());
ps.setBytes(4, remote.getPublicKey());
ps.setBytes(5, local.getBytes());
ps.setBoolean(6, verified);
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
public void addIdentity(Connection txn, Identity i) throws DbException {
PreparedStatement ps = null;
@@ -756,12 +887,12 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(1, local.getId().getBytes());
ps.setInt(2, local.getFormatVersion());
ps.setString(3, local.getName());
ps.setBytes(4, local.getPublicKey().getEncoded());
ps.setBytes(5, local.getPrivateKey().getEncoded());
ps.setBytes(4, local.getPublicKey());
ps.setBytes(5, local.getPrivateKey());
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);
else ps.setBytes(7, i.getHandshakePrivateKey().getEncoded());
else ps.setBytes(7, i.getHandshakePrivateKey());
ps.setLong(8, i.getTimeCreated());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
@@ -937,7 +1068,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " VALUES (?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, p.getId().getBytes());
ps.setBytes(2, p.getPublicKey().getEncoded());
ps.setBytes(2, p.getPublicKey());
ps.setString(3, p.getAlias());
ps.setInt(4, p.getState().getValue());
ps.setLong(5, p.getTimestamp());
@@ -970,47 +1101,24 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public KeySetId 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,
public TransportKeySetId addTransportKeys(Connection txn, ContactId c,
TransportKeys k) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Store the outgoing keys
String sql = "INSERT INTO outgoingKeys (transportId, timePeriod,"
+ " contactId, pendingContactId, tagKey, headerKey,"
+ " stream, active, rootKey, alice)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
String sql = "INSERT INTO outgoingKeys (contactId, transportId,"
+ " timePeriod, tagKey, headerKey, stream, active)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setString(1, k.getTransportId().getString());
ps.setLong(2, k.getTimePeriod());
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());
ps.setInt(1, c.getInt());
ps.setString(2, k.getTransportId().getString());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
ps.setBytes(5, outCurr.getTagKey().getBytes());
ps.setBytes(6, outCurr.getHeaderKey().getBytes());
ps.setLong(7, outCurr.getStreamCounter());
ps.setBoolean(8, outCurr.isActive());
if (k.isHandshakeMode()) {
ps.setBytes(9, k.getRootKey().getBytes());
ps.setBoolean(10, k.isAlice());
} else {
ps.setNull(9, BINARY);
ps.setNull(10, BOOLEAN);
}
ps.setLong(3, outCurr.getTimePeriod());
ps.setBytes(4, outCurr.getTagKey().getBytes());
ps.setBytes(5, outCurr.getHeaderKey().getBytes());
ps.setLong(6, outCurr.getStreamCounter());
ps.setBoolean(7, outCurr.isActive());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
@@ -1020,18 +1128,18 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
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();
rs.close();
ps.close();
// Store the incoming keys
sql = "INSERT INTO incomingKeys (transportId, keySetId,"
sql = "INSERT INTO incomingKeys (keySetId, transportId,"
+ " timePeriod, tagKey, headerKey, base, bitmap,"
+ " periodOffset)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setString(1, k.getTransportId().getString());
ps.setInt(2, keySetId.getInt());
ps.setInt(1, keySetId.getInt());
ps.setString(2, k.getTransportId().getString());
// Previous time period
IncomingKeys inPrev = k.getPreviousIncomingKeys();
ps.setLong(3, inPrev.getTimePeriod());
@@ -1336,16 +1444,14 @@ abstract class JdbcDatabase implements Database<Connection> {
int formatVersion = rs.getInt(2);
String name = rs.getString(3);
String alias = rs.getString(4);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(5));
byte[] handshakePub = rs.getBytes(6);
byte[] publicKey = rs.getBytes(5);
byte[] handshakePublicKey = rs.getBytes(6);
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
boolean verified = rs.getBoolean(8);
rs.close();
ps.close();
Author author =
new Author(authorId, formatVersion, name, publicKey);
PublicKey handshakePublicKey = handshakePub == null ?
null : new AgreementPublicKey(handshakePub);
return new Contact(c, author, localAuthorId, alias,
handshakePublicKey, verified);
} catch (SQLException e) {
@@ -1373,14 +1479,12 @@ abstract class JdbcDatabase implements Database<Connection> {
int formatVersion = rs.getInt(3);
String name = rs.getString(4);
String alias = rs.getString(5);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(6));
byte[] handshakePub = rs.getBytes(7);
byte[] publicKey = rs.getBytes(6);
byte[] handshakePublicKey = rs.getBytes(7);
AuthorId localAuthorId = new AuthorId(rs.getBytes(8));
boolean verified = rs.getBoolean(9);
Author author =
new Author(authorId, formatVersion, name, publicKey);
PublicKey handshakePublicKey = handshakePub == null ?
null : new AgreementPublicKey(handshakePub);
contacts.add(new Contact(contactId, author, localAuthorId,
alias, handshakePublicKey, verified));
}
@@ -1436,14 +1540,12 @@ abstract class JdbcDatabase implements Database<Connection> {
int formatVersion = rs.getInt(2);
String name = rs.getString(3);
String alias = rs.getString(4);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(5));
byte[] handshakePub = rs.getBytes(6);
byte[] publicKey = rs.getBytes(5);
byte[] handshakePublicKey = rs.getBytes(6);
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
boolean verified = rs.getBoolean(8);
Author author =
new Author(remote, formatVersion, name, publicKey);
PublicKey handshakePublicKey = handshakePub == null ?
null : new AgreementPublicKey(handshakePub);
contacts.add(new Contact(contactId, author, localAuthorId,
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
public Identity getIdentity(Connection txn, AuthorId a) throws DbException {
PreparedStatement ps = null;
@@ -1574,20 +1756,16 @@ abstract class JdbcDatabase implements Database<Connection> {
if (!rs.next()) throw new DbStateException();
int formatVersion = rs.getInt(1);
String name = rs.getString(2);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(3));
PrivateKey privateKey = new SignaturePrivateKey(rs.getBytes(4));
byte[] handshakePub = rs.getBytes(5);
byte[] handshakePriv = rs.getBytes(6);
byte[] publicKey = rs.getBytes(3);
byte[] privateKey = rs.getBytes(4);
byte[] handshakePublicKey = rs.getBytes(5);
byte[] handshakePrivateKey = rs.getBytes(6);
long created = rs.getLong(7);
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
LocalAuthor local = new LocalAuthor(a, formatVersion, name,
publicKey, privateKey);
PublicKey handshakePublicKey = handshakePub == null ?
null : new AgreementPublicKey(handshakePub);
PrivateKey handshakePrivateKey = handshakePriv == null ?
null : new AgreementPrivateKey(handshakePriv);
return new Identity(local, handshakePublicKey, handshakePrivateKey,
created);
} catch (SQLException e) {
@@ -1614,17 +1792,13 @@ abstract class JdbcDatabase implements Database<Connection> {
AuthorId authorId = new AuthorId(rs.getBytes(1));
int formatVersion = rs.getInt(2);
String name = rs.getString(3);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(4));
PrivateKey privateKey = new SignaturePrivateKey(rs.getBytes(5));
byte[] handshakePub = rs.getBytes(6);
byte[] handshakePriv = rs.getBytes(7);
byte[] publicKey = rs.getBytes(4);
byte[] privateKey = rs.getBytes(5);
byte[] handshakePublicKey = rs.getBytes(6);
byte[] handshakePrivateKey = rs.getBytes(7);
long created = rs.getLong(8);
LocalAuthor local = new LocalAuthor(authorId, formatVersion,
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,
handshakePrivateKey, created));
}
@@ -2221,7 +2395,7 @@ abstract class JdbcDatabase implements Database<Connection> {
List<PendingContact> pendingContacts = new ArrayList<>();
while (rs.next()) {
PendingContactId id = new PendingContactId(rs.getBytes(1));
PublicKey publicKey = new AgreementPublicKey(rs.getBytes(2));
byte[] publicKey = rs.getBytes(2);
String alias = rs.getString(3);
PendingContactState state =
PendingContactState.fromValue(rs.getInt(4));
@@ -2328,8 +2502,8 @@ abstract class JdbcDatabase implements Database<Connection> {
rs.close();
ps.close();
// Retrieve the outgoing keys in the same order
sql = "SELECT keySetId, timePeriod, contactId, pendingContactId,"
+ " tagKey, headerKey, stream, active, rootKey, alice"
sql = "SELECT keySetId, contactId, timePeriod,"
+ " tagKey, headerKey, stream, active"
+ " FROM outgoingKeys"
+ " WHERE transportId = ?"
+ " ORDER BY keySetId";
@@ -2340,34 +2514,23 @@ abstract class JdbcDatabase implements Database<Connection> {
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();
KeySetId keySetId = new KeySetId(rs.getInt(1));
long timePeriod = rs.getLong(2);
int cId = rs.getInt(3);
ContactId contactId = rs.wasNull() ? null : new ContactId(cId);
byte[] pId = rs.getBytes(4);
PendingContactId pendingContactId = pId == null ?
null : new PendingContactId(pId);
SecretKey tagKey = new SecretKey(rs.getBytes(5));
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);
TransportKeySetId keySetId =
new TransportKeySetId(rs.getInt(1));
ContactId contactId = new ContactId(rs.getInt(2));
long timePeriod = rs.getLong(3);
SecretKey tagKey = new SecretKey(rs.getBytes(4));
SecretKey headerKey = new SecretKey(rs.getBytes(5));
long streamCounter = rs.getLong(6);
boolean active = rs.getBoolean(7);
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
timePeriod, streamCounter, active);
IncomingKeys inPrev = inKeys.get(i * 3);
IncomingKeys inCurr = inKeys.get(i * 3 + 1);
IncomingKeys inNext = inKeys.get(i * 3 + 2);
TransportKeys transportKeys;
if (rootKey == null) {
transportKeys = new TransportKeys(t, inPrev, inCurr,
inNext, outCurr);
} else {
transportKeys = new TransportKeys(t, inPrev, inCurr,
inNext, outCurr, new SecretKey(rootKey), alice);
}
TransportKeys transportKeys = new TransportKeys(t, inPrev,
inCurr, inNext, outCurr);
keys.add(new TransportKeySet(keySetId, contactId,
pendingContactId, transportKeys));
transportKeys));
}
rs.close();
ps.close();
@@ -2381,7 +2544,26 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override
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;
try {
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
public void removeIdentity(Connection txn, AuthorId a) throws DbException {
PreparedStatement ps = null;
@@ -2871,8 +3074,8 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public void removeTransportKeys(Connection txn, TransportId t, KeySetId k)
throws DbException {
public void removeTransportKeys(Connection txn, TransportId t,
TransportKeySetId k) throws DbException {
PreparedStatement ps = null;
try {
// Delete any existing outgoing keys - this will also remove any
@@ -2979,15 +3182,15 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override
public void setHandshakeKeyPair(Connection txn, AuthorId local,
PublicKey publicKey, PrivateKey privateKey) throws DbException {
byte[] publicKey, byte[] privateKey) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE localAuthors"
+ " SET handshakePublicKey = ?, handshakePrivateKey = ?"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, publicKey.getEncoded());
ps.setBytes(2, privateKey.getEncoded());
ps.setBytes(1, publicKey);
ps.setBytes(2, privateKey);
ps.setBytes(3, local.getBytes());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
@@ -3088,7 +3291,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(1, state.getValue());
ps.setBytes(2, p.getBytes());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
@@ -3097,7 +3300,7 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public void setReorderingWindow(Connection txn, KeySetId k,
public void setReorderingWindow(Connection txn, TransportKeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException {
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
public void setTransportKeysActive(Connection txn, TransportId t,
KeySetId k) throws DbException {
TransportKeySetId k) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE outgoingKeys SET active = true"
@@ -3242,4 +3469,71 @@ abstract class JdbcDatabase implements Database<Connection> {
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.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.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor;
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.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.AuthorId.LABEL;
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
@NotNullByDefault
@@ -31,13 +29,13 @@ class AuthorFactoryImpl implements AuthorFactory {
}
@Override
public Author createAuthor(String name, PublicKey publicKey) {
public Author createAuthor(String name, byte[] publicKey) {
return createAuthor(FORMAT_VERSION, name, publicKey);
}
@Override
public Author createAuthor(int formatVersion, String name,
PublicKey publicKey) {
byte[] publicKey) {
AuthorId id = getId(formatVersion, name, publicKey);
return new Author(id, formatVersion, name, publicKey);
}
@@ -45,17 +43,16 @@ class AuthorFactoryImpl implements AuthorFactory {
@Override
public LocalAuthor createLocalAuthor(String name) {
KeyPair signatureKeyPair = crypto.generateSignatureKeyPair();
PublicKey publicKey = signatureKeyPair.getPublic();
PrivateKey privateKey = signatureKeyPair.getPrivate();
byte[] publicKey = signatureKeyPair.getPublic().getEncoded();
byte[] privateKey = signatureKeyPair.getPrivate().getEncoded();
AuthorId id = getId(FORMAT_VERSION, name, publicKey);
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
}
private AuthorId getId(int formatVersion, String name,
PublicKey publicKey) {
private AuthorId getId(int formatVersion, String name, byte[] publicKey) {
byte[] formatVersionBytes = new byte[INT_32_BYTES];
writeUint32(formatVersion, formatVersionBytes, 0);
ByteUtils.writeUint32(formatVersion, formatVersionBytes, 0);
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.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.DbException;
import org.briarproject.bramble.api.db.Transaction;
@@ -75,8 +73,8 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
long start = now();
LocalAuthor localAuthor = authorFactory.createLocalAuthor(name);
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
PublicKey handshakePub = handshakeKeyPair.getPublic();
PrivateKey handshakePriv = handshakeKeyPair.getPrivate();
byte[] handshakePub = handshakeKeyPair.getPublic().getEncoded();
byte[] handshakePriv = handshakeKeyPair.getPrivate().getEncoded();
logDuration(LOG, "Creating identity", start);
return new Identity(localAuthor, handshakePub, handshakePriv,
clock.currentTimeMillis());
@@ -100,9 +98,9 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
} else if (shouldStoreKeys) {
// Handshake keys were generated when loading the identity -
// store them
PublicKey handshakePub =
byte[] handshakePub =
requireNonNull(cached.getHandshakePublicKey());
PrivateKey handshakePriv =
byte[] handshakePriv =
requireNonNull(cached.getHandshakePrivateKey());
db.setHandshakeKeyPair(txn, cached.getId(), handshakePub,
handshakePriv);
@@ -124,12 +122,12 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
}
@Override
public KeyPair getHandshakeKeys(Transaction txn) throws DbException {
public byte[][] getHandshakeKeys(Transaction txn) throws DbException {
Identity cached = getCachedIdentity(txn);
PublicKey handshakePub = requireNonNull(cached.getHandshakePublicKey());
PrivateKey handshakePriv =
requireNonNull(cached.getHandshakePrivateKey());
return new KeyPair(handshakePub, handshakePriv);
return new byte[][] {
cached.getHandshakePublicKey(),
cached.getHandshakePrivateKey()
};
}
/**
@@ -161,8 +159,8 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
LOG.info("Identity loaded");
if (i.hasHandshakeKeyPair()) return i;
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
PublicKey handshakePub = handshakeKeyPair.getPublic();
PrivateKey handshakePriv = handshakeKeyPair.getPrivate();
byte[] handshakePub = handshakeKeyPair.getPublic().getEncoded();
byte[] handshakePriv = handshakeKeyPair.getPrivate().getEncoded();
LOG.info("Handshake key pair generated");
shouldStoreKeys = true;
return new Identity(i.getLocalAuthor(), handshakePub, handshakePriv,

View File

@@ -96,7 +96,6 @@ class ConnectionManagerImpl implements ConnectionManager {
TransportConnectionReader r) throws IOException {
InputStream streamReader = streamReaderFactory.createStreamReader(
r.getInputStream(), ctx);
// TODO: Pending contacts, handshake mode
return syncSessionFactory.createIncomingSession(ctx.getContactId(),
streamReader);
}
@@ -105,7 +104,6 @@ class ConnectionManagerImpl implements ConnectionManager {
TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
// TODO: Pending contacts, handshake mode
return syncSessionFactory.createSimplexOutgoingSession(
ctx.getContactId(), w.getMaxLatency(), streamWriter);
}
@@ -114,7 +112,6 @@ class ConnectionManagerImpl implements ConnectionManager {
TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
// TODO: Pending contacts, handshake mode
return syncSessionFactory.createDuplexOutgoingSession(
ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(),
streamWriter);
@@ -148,7 +145,6 @@ class ConnectionManagerImpl implements ConnectionManager {
disposeReader(false, false);
return;
}
// TODO: Pending contacts
ContactId contactId = ctx.getContactId();
connectionRegistry.registerConnection(contactId, transportId, true);
try {
@@ -392,7 +388,7 @@ class ConnectionManagerImpl implements ConnectionManager {
return;
}
// 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");
disposeReader(true, true);
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.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.PluginCallback;
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.settings.Settings;
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.Collection;
import java.util.List;
@@ -42,7 +38,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@@ -64,15 +59,11 @@ class PluginManagerImpl implements PluginManager, Service {
Logger.getLogger(PluginManagerImpl.class.getName());
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final EventBus eventBus;
private final PluginConfig pluginConfig;
private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final SettingsManager settingsManager;
private final TransportPropertyManager transportPropertyManager;
private final SecureRandom random;
private final Clock clock;
private final Map<TransportId, Plugin> plugins;
private final List<SimplexPlugin> simplexPlugins;
private final List<DuplexPlugin> duplexPlugins;
@@ -80,41 +71,25 @@ class PluginManagerImpl implements PluginManager, Service {
private final AtomicBoolean used = new AtomicBoolean(false);
@Inject
PluginManagerImpl(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler, EventBus eventBus,
PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus,
PluginConfig pluginConfig, ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry,
SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock) {
TransportPropertyManager transportPropertyManager) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.eventBus = eventBus;
this.pluginConfig = pluginConfig;
this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.settingsManager = settingsManager;
this.transportPropertyManager = transportPropertyManager;
this.random = random;
this.clock = clock;
plugins = new ConcurrentHashMap<>();
simplexPlugins = new CopyOnWriteArrayList<>();
duplexPlugins = new CopyOnWriteArrayList<>();
startLatches = new ConcurrentHashMap<>();
}
@Override
public void startService() {
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
LOG.info("Starting simplex plugins");
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) {

View File

@@ -1,9 +1,11 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.BackoffFactory;
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.PluginManager;
import javax.inject.Inject;
@@ -18,6 +20,8 @@ public class PluginModule {
public static class EagerSingletons {
@Inject
PluginManager pluginManager;
@Inject
Poller poller;
}
@Provides
@@ -46,4 +50,12 @@ public class PluginModule {
lifecycleManager.registerService(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;
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.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);
}
}
/**
* Empty interface used for injecting the poller.
*/
interface Poller {
}

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;
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.crypto.SecretKey;
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.simplex.SimplexPluginFactory;
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.TransportKeySetId;
import java.util.HashMap;
import java.util.Map;
@@ -29,7 +28,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
@@ -90,53 +88,29 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
}
@Override
public Map<TransportId, KeySetId> addContactWithRotationKeys(
Transaction txn, ContactId c, SecretKey rootKey, long timestamp,
boolean alice, boolean active) throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>();
public Map<TransportId, TransportKeySetId> addContact(Transaction txn,
ContactId c, SecretKey rootKey, long timestamp, boolean alice,
boolean active) throws DbException {
Map<TransportId, TransportKeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey();
TransportKeyManager m = e.getValue();
ids.put(t, m.addContactWithRotationKeys(txn, c, rootKey, timestamp,
alice, active));
ids.put(t, m.addContact(txn, c, rootKey, timestamp, alice, active));
}
return ids;
}
@Override
public Map<TransportId, KeySetId> addContactWithHandshakeKeys(
Transaction txn, ContactId c, SecretKey rootKey, boolean alice)
throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
public void activateKeys(Transaction txn, Map<TransportId,
TransportKeySetId> keys) throws DbException {
for (Entry<TransportId, TransportKeySetId> e : keys.entrySet()) {
TransportId t = e.getKey();
TransportKeyManager m = e.getValue();
ids.put(t, m.addContactWithHandshakeKeys(txn, c, rootKey, alice));
}
return ids;
}
@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 -> {
TransportKeyManager m = managers.get(t);
if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
} else {
m.activateKeys(txn, e.getValue());
return null;
});
}
}
}
@@ -146,34 +120,28 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
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
public StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException {
return withManager(t, m ->
db.transactionWithNullableResult(false, txn ->
m.getStreamContext(txn, c)));
}
@Override
public StreamContext getStreamContext(PendingContactId p, TransportId t)
throws DbException {
return withManager(t, m ->
db.transactionWithNullableResult(false, txn ->
m.getStreamContext(txn, p)));
TransportKeyManager m = managers.get(t);
if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
return null;
}
return db.transactionWithNullableResult(false, txn ->
m.getStreamContext(txn, c));
}
@Override
public StreamContext getStreamContext(TransportId t, byte[] tag)
throws DbException {
return withManager(t, m ->
db.transactionWithNullableResult(false, txn ->
m.getStreamContext(txn, tag)));
TransportKeyManager m = managers.get(t);
if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
return null;
}
return db.transactionWithNullableResult(false, txn ->
m.getStreamContext(txn, tag));
}
@Override
@@ -188,20 +156,4 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
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;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.TransportKeys;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
@@ -15,9 +13,6 @@ class MutableTransportKeys {
private final TransportId transportId;
private final MutableIncomingKeys inPrev, inCurr, inNext;
private final MutableOutgoingKeys outCurr;
@Nullable
private final SecretKey rootKey;
private final boolean alice;
MutableTransportKeys(TransportKeys k) {
transportId = k.getTransportId();
@@ -25,24 +20,11 @@ class MutableTransportKeys {
inCurr = new MutableIncomingKeys(k.getCurrentIncomingKeys());
inNext = new MutableIncomingKeys(k.getNextIncomingKeys());
outCurr = new MutableOutgoingKeys(k.getCurrentOutgoingKeys());
if (k.isHandshakeMode()) {
rootKey = k.getRootKey();
alice = k.isAlice();
} else {
rootKey = null;
alice = false;
}
}
TransportKeys snapshot() {
if (rootKey == null) {
return new TransportKeys(transportId, inPrev.snapshot(),
inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot());
} else {
return new TransportKeys(transportId, inPrev.snapshot(),
inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot(),
rootKey, alice);
}
return new TransportKeys(transportId, inPrev.snapshot(),
inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot());
}
TransportId getTransportId() {
@@ -64,18 +46,4 @@ class MutableTransportKeys {
MutableOutgoingKeys getCurrentOutgoingKeys() {
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;
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.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
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.TransportKeySetId;
import javax.annotation.Nullable;
@@ -16,34 +15,20 @@ interface TransportKeyManager {
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)
throws DbException;
KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c,
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 activateKeys(Transaction txn, TransportKeySetId k) throws DbException;
void removeContact(ContactId c);
void removePendingContact(PendingContactId p);
boolean canSendOutgoingStreams(ContactId c);
boolean canSendOutgoingStreams(PendingContactId p);
@Nullable
StreamContext getStreamContext(Transaction txn, ContactId c)
throws DbException;
@Nullable
StreamContext getStreamContext(Transaction txn, PendingContactId p)
throws DbException;
@Nullable
StreamContext getStreamContext(Transaction txn, byte[] tag)
throws DbException;

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.Bytes;
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.TransportCrypto;
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.system.Clock;
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.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
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.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
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.PROTOCOL_VERSION;
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 {
private static final Logger LOG =
getLogger(TransportKeyManagerImpl.class.getName());
Logger.getLogger(TransportKeyManagerImpl.class.getName());
private final DatabaseComponent db;
private final TransportCrypto transportCrypto;
@@ -60,16 +55,10 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final AtomicBoolean used = new AtomicBoolean(false);
private final ReentrantLock lock = new ReentrantLock();
@GuardedBy("lock")
private final Map<KeySetId, MutableTransportKeySet> keys = new HashMap<>();
@GuardedBy("lock")
// The following are locking: lock
private final Map<TransportKeySetId, MutableKeySet> keys = new HashMap<>();
private final Map<Bytes, TagContext> inContexts = new HashMap<>();
@GuardedBy("lock")
private final Map<ContactId, MutableTransportKeySet>
contactOutContexts = new HashMap<>();
@GuardedBy("lock")
private final Map<PendingContactId, MutableTransportKeySet>
pendingContactOutContexts = new HashMap<>();
private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>();
TransportKeyManagerImpl(DatabaseComponent db,
TransportCrypto transportCrypto, Executor dbExecutor,
@@ -93,71 +82,62 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Load the transport keys from the DB
Collection<TransportKeySet> loaded =
db.getTransportKeys(txn, transportId);
// Update the keys to the current time period
UpdateResult updateResult = updateKeys(loaded, now);
// Rotate the keys to the current time period
RotationResult rotationResult = rotateKeys(loaded, now);
// Initialise mutable state for all contacts
addKeys(updateResult.current);
// Write any updated keys back to the DB
if (!updateResult.updated.isEmpty())
db.updateTransportKeys(txn, updateResult.updated);
addKeys(rotationResult.current);
// Write any rotated keys back to the DB
if (!rotationResult.rotated.isEmpty())
db.updateTransportKeys(txn, rotationResult.rotated);
} finally {
lock.unlock();
}
// Schedule the next key update
scheduleKeyUpdate(now);
// Schedule the next key rotation
scheduleKeyRotation(now);
}
private UpdateResult updateKeys(Collection<TransportKeySet> keys,
private RotationResult rotateKeys(Collection<TransportKeySet> keys,
long now) {
UpdateResult updateResult = new UpdateResult();
RotationResult rotationResult = new RotationResult();
long timePeriod = now / timePeriodLength;
for (TransportKeySet ks : keys) {
TransportKeys k = ks.getKeys();
TransportKeys k1 = transportCrypto.updateTransportKeys(k,
TransportKeys k1 = transportCrypto.rotateTransportKeys(k,
timePeriod);
TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
ks.getContactId(), null, k1);
ks.getContactId(), k1);
if (k1.getTimePeriod() > k.getTimePeriod())
updateResult.updated.add(ks1);
updateResult.current.add(ks1);
rotationResult.rotated.add(ks1);
rotationResult.current.add(ks1);
}
return updateResult;
return rotationResult;
}
@GuardedBy("lock")
// Locking: lock
private void addKeys(Collection<TransportKeySet> keys) {
for (TransportKeySet ks : keys) {
addKeys(ks.getKeySetId(), ks.getContactId(),
ks.getPendingContactId(),
new MutableTransportKeys(ks.getKeys()));
}
}
@GuardedBy("lock")
private void addKeys(KeySetId keySetId, @Nullable ContactId contactId,
@Nullable PendingContactId pendingContactId,
MutableTransportKeys keys) {
requireExactlyOneNull(contactId, pendingContactId);
MutableTransportKeySet ks = new MutableTransportKeySet(keySetId,
contactId, pendingContactId, keys);
this.keys.put(keySetId, ks);
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);
// Locking: lock
private void addKeys(TransportKeySetId keySetId, ContactId contactId,
MutableTransportKeys m) {
MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
keys.put(keySetId, ks);
encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
encodeTags(keySetId, contactId, m.getNextIncomingKeys());
considerReplacingOutgoingKeys(ks);
}
@GuardedBy("lock")
private void encodeTags(KeySetId keySetId, @Nullable ContactId contactId,
@Nullable PendingContactId pendingContactId,
MutableIncomingKeys inKeys, boolean handshakeMode) {
// Locking: lock
private void encodeTags(TransportKeySetId keySetId, ContactId contactId,
MutableIncomingKeys inKeys) {
for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = new TagContext(keySetId, contactId,
pendingContactId, inKeys, streamNumber, handshakeMode);
TagContext tagCtx =
new TagContext(keySetId, contactId, inKeys, streamNumber);
byte[] tag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
@@ -165,41 +145,27 @@ class TransportKeyManagerImpl implements TransportKeyManager {
}
}
@GuardedBy("lock")
private void considerReplacingOutgoingKeys(MutableTransportKeySet ks) {
// Use the active outgoing keys with the highest key set ID, preferring
// rotation keys to handshake keys
if (ks.getKeys().getCurrentOutgoingKeys().isActive()) {
MutableTransportKeySet old = getOutgoingKeySet(ks.getContactId(),
ks.getPendingContactId());
if (old == null || (old.getKeys().isHandshakeMode() &&
!ks.getKeys().isHandshakeMode()) ||
// Locking: lock
private void considerReplacingOutgoingKeys(MutableKeySet ks) {
// Use the active outgoing keys with the highest key set ID
if (ks.getTransportKeys().getCurrentOutgoingKeys().isActive()) {
MutableKeySet old = outContexts.get(ks.getContactId());
if (old == null ||
old.getKeySetId().getInt() < ks.getKeySetId().getInt()) {
if (ks.getContactId() == null)
pendingContactOutContexts.put(ks.getPendingContactId(), ks);
else contactOutContexts.put(ks.getContactId(), ks);
outContexts.put(ks.getContactId(), ks);
}
}
}
@GuardedBy("lock")
@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) {
private void scheduleKeyRotation(long now) {
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(() -> {
try {
db.transaction(false, this::updateKeys);
db.transaction(false, this::rotateKeys);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
@@ -207,7 +173,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
}
@Override
public KeySetId addContactWithRotationKeys(Transaction txn, ContactId c,
public TransportKeySetId addContact(Transaction txn, ContactId c,
SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException {
lock.lock();
@@ -215,15 +181,15 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Work out what time period the timestamp belongs to
long timePeriod = timestamp / timePeriodLength;
// Derive the transport keys
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
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;
k = transportCrypto.updateTransportKeys(k, timePeriod);
k = transportCrypto.rotateTransportKeys(k, timePeriod);
// 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
addKeys(keySetId, c, null, new MutableTransportKeys(k));
addKeys(keySetId, c, new MutableTransportKeys(k));
return keySetId;
} finally {
lock.unlock();
@@ -231,52 +197,13 @@ class TransportKeyManagerImpl implements TransportKeyManager {
}
@Override
public KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c,
SecretKey rootKey, boolean alice) throws DbException {
public void activateKeys(Transaction txn, TransportKeySetId k)
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, 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);
MutableKeySet ks = keys.get(k);
if (ks == null) throw new IllegalArgumentException();
MutableTransportKeys m = ks.getKeys();
MutableTransportKeys m = ks.getTransportKeys();
m.getCurrentOutgoingKeys().activate();
considerReplacingOutgoingKeys(ks);
db.setTransportKeysActive(txn, m.getTransportId(), k);
@@ -291,29 +218,13 @@ class TransportKeyManagerImpl implements TransportKeyManager {
try {
// Remove mutable state for the contact
Iterator<TagContext> it = inContexts.values().iterator();
while (it.hasNext())
if (c.equals(it.next().contactId)) it.remove();
contactOutContexts.remove(c);
Iterator<MutableTransportKeySet> it1 = keys.values().iterator();
while (it1.hasNext())
if (c.equals(it1.next().getContactId())) 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();
while (it.hasNext()) if (it.next().contactId.equals(c)) it.remove();
outContexts.remove(c);
Iterator<MutableKeySet> it1 = keys.values().iterator();
while (it1.hasNext()) {
ContactId c1 = it1.next().getContactId();
if (c1 != null && c1.equals(c)) it1.remove();
}
} finally {
lock.unlock();
}
@@ -321,21 +232,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
@Override
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();
try {
MutableTransportKeySet ks = getOutgoingKeySet(c, p);
MutableKeySet ks = outContexts.get(c);
if (ks == null) return false;
MutableOutgoingKeys outKeys = ks.getKeys().getCurrentOutgoingKeys();
MutableOutgoingKeys outKeys =
ks.getTransportKeys().getCurrentOutgoingKeys();
if (!outKeys.isActive()) throw new AssertionError();
return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED;
} finally {
@@ -346,32 +248,19 @@ class TransportKeyManagerImpl implements TransportKeyManager {
@Override
public StreamContext getStreamContext(Transaction txn, ContactId c)
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();
try {
// Look up the outgoing keys for the contact
MutableTransportKeySet ks = getOutgoingKeySet(c, p);
MutableKeySet ks = outContexts.get(c);
if (ks == null) return null;
MutableTransportKeys keys = ks.getKeys();
MutableOutgoingKeys outKeys = keys.getCurrentOutgoingKeys();
MutableOutgoingKeys outKeys =
ks.getTransportKeys().getCurrentOutgoingKeys();
if (!outKeys.isActive()) throw new AssertionError();
if (outKeys.getStreamCounter() > MAX_32_BIT_UNSIGNED) return null;
// Create a stream context
StreamContext ctx = new StreamContext(c, p, transportId,
StreamContext ctx = new StreamContext(c, transportId,
outKeys.getTagKey(), outKeys.getHeaderKey(),
outKeys.getStreamCounter(), keys.isHandshakeMode());
outKeys.getStreamCounter());
// Increment the stream counter and write it back to the DB
outKeys.incrementStreamCounter();
db.incrementStreamCounter(txn, transportId, ks.getKeySetId());
@@ -391,10 +280,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
if (tagCtx == null) return null;
MutableIncomingKeys inKeys = tagCtx.inKeys;
// Create a stream context
StreamContext ctx = new StreamContext(tagCtx.contactId,
tagCtx.pendingContactId, transportId,
StreamContext ctx = new StreamContext(tagCtx.contactId, transportId,
inKeys.getTagKey(), inKeys.getHeaderKey(),
tagCtx.streamNumber, tagCtx.handshakeMode);
tagCtx.streamNumber);
// Update the reordering window
ReorderingWindow window = inKeys.getWindow();
Change change = window.setSeen(tagCtx.streamNumber);
@@ -404,8 +292,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
TagContext tagCtx1 = new TagContext(tagCtx.keySetId,
tagCtx.contactId, tagCtx.pendingContactId, inKeys,
streamNumber, tagCtx.handshakeMode);
tagCtx.contactId, inKeys, streamNumber);
inContexts.put(new Bytes(addTag), tagCtx1);
}
// Remove tags for any stream numbers removed from the window
@@ -421,9 +308,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
inKeys.getTimePeriod(), window.getBase(),
window.getBitmap());
// If the outgoing keys are inactive, activate them
MutableTransportKeySet ks = keys.get(tagCtx.keySetId);
MutableKeySet ks = keys.get(tagCtx.keySetId);
MutableOutgoingKeys outKeys =
ks.getKeys().getCurrentOutgoingKeys();
ks.getTransportKeys().getCurrentOutgoingKeys();
if (!outKeys.isActive()) {
LOG.info("Activating outgoing keys");
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();
lock.lock();
try {
// Update the keys to the current time period
// Rotate the keys to the current time period
Collection<TransportKeySet> snapshot = new ArrayList<>(keys.size());
for (MutableTransportKeySet ks : keys.values()) {
for (MutableKeySet ks : keys.values()) {
snapshot.add(new TransportKeySet(ks.getKeySetId(),
ks.getContactId(), ks.getPendingContactId(),
ks.getKeys().snapshot()));
ks.getContactId(), ks.getTransportKeys().snapshot()));
}
UpdateResult updateResult = updateKeys(snapshot, now);
RotationResult rotationResult = rotateKeys(snapshot, now);
// Rebuild the mutable state for all contacts
inContexts.clear();
contactOutContexts.clear();
pendingContactOutContexts.clear();
outContexts.clear();
keys.clear();
addKeys(updateResult.current);
// Write any updated keys back to the DB
if (!updateResult.updated.isEmpty())
db.updateTransportKeys(txn, updateResult.updated);
addKeys(rotationResult.current);
// Write any rotated keys back to the DB
if (!rotationResult.rotated.isEmpty())
db.updateTransportKeys(txn, rotationResult.rotated);
} finally {
lock.unlock();
}
// Schedule the next key update
scheduleKeyUpdate(now);
// Schedule the next key rotation
scheduleKeyRotation(now);
}
private static class TagContext {
private final KeySetId keySetId;
@Nullable
private final TransportKeySetId keySetId;
private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final MutableIncomingKeys inKeys;
private final long streamNumber;
private final boolean handshakeMode;
private TagContext(KeySetId keySetId, @Nullable ContactId contactId,
@Nullable PendingContactId pendingContactId,
MutableIncomingKeys inKeys, long streamNumber,
boolean handshakeMode) {
requireExactlyOneNull(contactId, pendingContactId);
private TagContext(TransportKeySetId keySetId, ContactId contactId,
MutableIncomingKeys inKeys, long streamNumber) {
this.keySetId = keySetId;
this.contactId = contactId;
this.pendingContactId = pendingContactId;
this.inKeys = inKeys;
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> 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.client.ClientHelper;
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.BdfEntry;
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.getRandomBytes;
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.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -71,7 +66,6 @@ public class ClientHelperImplTest extends BrambleTestCase {
context.mock(CryptoComponent.class);
private final AuthorFactory authorFactory =
context.mock(AuthorFactory.class);
private final KeyParser keyParser = context.mock(KeyParser.class);
private final GroupId groupId = new GroupId(getRandomId());
private final BdfDictionary dictionary = new BdfDictionary();
@@ -268,25 +262,24 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test
public void testSign() throws Exception {
PrivateKey privateKey = getSignaturePrivateKey();
byte[] signature = getRandomBytes(42);
byte[] privateKey = getRandomBytes(42);
byte[] signed = getRandomBytes(42);
byte[] bytes = expectToByteArray(list);
context.checking(new Expectations() {{
oneOf(cryptoComponent).sign(label, bytes, privateKey);
will(returnValue(signature));
will(returnValue(signed));
}});
assertArrayEquals(signature,
clientHelper.sign(label, list, privateKey));
assertArrayEquals(signed, clientHelper.sign(label, list, privateKey));
context.assertIsSatisfied();
}
@Test
public void testVerifySignature() throws Exception {
byte[] signature = getRandomBytes(MAX_SIGNATURE_LENGTH);
byte[] publicKey = getRandomBytes(42);
byte[] signed = expectToByteArray(list);
PublicKey publicKey = getSignaturePublicKey();
context.checking(new Expectations() {{
oneOf(cryptoComponent).verifySignature(signature, label, signed,
@@ -301,8 +294,8 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test
public void testVerifyWrongSignature() throws Exception {
byte[] signature = getRandomBytes(MAX_SIGNATURE_LENGTH);
byte[] publicKey = getRandomBytes(42);
byte[] signed = expectToByteArray(list);
PublicKey publicKey = getSignaturePublicKey();
context.checking(new Expectations() {{
oneOf(cryptoComponent).verifySignature(signature, label, signed,
@@ -322,10 +315,6 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test
public void testParsesAndEncodesAuthor() throws Exception {
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(),
author.getName(), author.getPublicKey());
will(returnValue(author));
@@ -340,14 +329,10 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList authorList = BdfList.of(
author.getFormatVersion(),
author.getName(),
author.getPublicKey().getEncoded()
author.getPublicKey()
);
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(),
author.getName(), author.getPublicKey());
will(returnValue(author));
@@ -370,7 +355,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of(
author.getFormatVersion(),
author.getName(),
author.getPublicKey().getEncoded(),
author.getPublicKey(),
"foo"
);
clientHelper.parseAndValidateAuthor(invalidAuthor);
@@ -381,7 +366,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of(
null,
author.getName(),
author.getPublicKey().getEncoded()
author.getPublicKey()
);
clientHelper.parseAndValidateAuthor(invalidAuthor);
}
@@ -392,7 +377,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of(
"foo",
author.getName(),
author.getPublicKey().getEncoded()
author.getPublicKey()
);
clientHelper.parseAndValidateAuthor(invalidAuthor);
}
@@ -402,7 +387,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of(
author.getFormatVersion() + 1,
author.getName(),
author.getPublicKey().getEncoded()
author.getPublicKey()
);
clientHelper.parseAndValidateAuthor(invalidAuthor);
}
@@ -412,7 +397,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of(
author.getFormatVersion(),
"",
author.getPublicKey().getEncoded()
author.getPublicKey()
);
clientHelper.parseAndValidateAuthor(invalidAuthor);
}
@@ -422,7 +407,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of(
author.getFormatVersion(),
getRandomString(MAX_AUTHOR_NAME_LENGTH + 1),
author.getPublicKey().getEncoded()
author.getPublicKey()
);
clientHelper.parseAndValidateAuthor(invalidAuthor);
}
@@ -432,7 +417,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
BdfList invalidAuthor = BdfList.of(
author.getFormatVersion(),
null,
author.getPublicKey().getEncoded()
author.getPublicKey()
);
clientHelper.parseAndValidateAuthor(invalidAuthor);
}
@@ -487,24 +472,6 @@ public class ClientHelperImplTest extends BrambleTestCase {
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 {
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).addContact(txn, remote, local, verified);
will(returnValue(contactId));
oneOf(keyManager).addContactWithRotationKeys(txn, contactId,
rootKey, timestamp, alice, active);
oneOf(keyManager).addContact(txn, contactId, rootKey, timestamp,
alice, active);
oneOf(db).getContact(txn, contactId);
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.PendingContactState.WAITING_FOR_CONNECTION;
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.util.StringUtils.getRandomString;
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 Clock clock = context.mock(Clock.class);
private final KeyParser keyParser = context.mock(KeyParser.class);
private final PublicKey publicKey = context.mock(PublicKey.class);
private final PendingContactFactory pendingContactFactory =
new PendingContactFactoryImpl(crypto, clock);
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 long timestamp = System.currentTimeMillis();
@@ -63,7 +64,7 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{
oneOf(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(publicKey.getEncoded());
oneOf(keyParser).parsePublicKey(with(equal(publicKeyBytes)));
will(throwException(new GeneralSecurityException()));
}});
@@ -94,9 +95,11 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{
oneOf(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(publicKey.getEncoded());
oneOf(keyParser).parsePublicKey(with(equal(publicKeyBytes)));
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));
oneOf(clock).currentTimeMillis();
will(returnValue(timestamp));
@@ -105,8 +108,7 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
PendingContact p =
pendingContactFactory.createPendingContact(link, alias);
assertArrayEquals(idBytes, p.getId().getBytes());
assertArrayEquals(publicKey.getEncoded(),
p.getPublicKey().getEncoded());
assertArrayEquals(publicKeyBytes, p.getPublicKey());
assertEquals(alias, p.getAlias());
assertEquals(WAITING_FOR_CONNECTION, p.getState());
assertEquals(timestamp, p.getTimestamp());
@@ -119,7 +121,6 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
private String encodeLink(int formatVersion) {
byte[] rawLink = new byte[RAW_LINK_BYTES];
rawLink[0] = (byte) formatVersion;
byte[] publicKeyBytes = publicKey.getEncoded();
arraycopy(publicKeyBytes, 0, rawLink, 1, publicKeyBytes.length);
String base32 = Base32.encode(rawLink).toLowerCase();
assertEquals(BASE32_LINK_BYTES, base32.length());

View File

@@ -1,10 +1,6 @@
package org.briarproject.bramble.crypto;
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 java.security.GeneralSecurityException;
@@ -141,14 +137,14 @@ public class EdSignatureTest extends SignatureTest {
}
@Override
protected byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
protected byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException {
return crypto.sign(label, toSign, privateKey);
}
@Override
protected boolean verify(byte[] signature, String label, byte[] signed,
PublicKey publicKey) throws GeneralSecurityException {
byte[] publicKey) throws GeneralSecurityException {
return crypto.verifySignature(signature, label, signed, publicKey);
}
@@ -161,11 +157,11 @@ public class EdSignatureTest extends SignatureTest {
byte[] signatureBytes = fromHexString(vector[3]);
EdSignature signature = new EdSignature();
signature.initSign(new SignaturePrivateKey(privateKeyBytes));
signature.initSign(new EdPrivateKey(privateKeyBytes));
signature.update(messageBytes);
assertArrayEquals(signatureBytes, signature.sign());
signature.initVerify(new SignaturePublicKey(publicKeyBytes));
signature.initVerify(new EdPublicKey(publicKeyBytes));
signature.update(messageBytes);
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;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey;
@@ -11,14 +10,12 @@ import org.junit.Test;
import org.whispersystems.curve25519.Curve25519;
import java.security.GeneralSecurityException;
import java.util.Arrays;
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.util.StringUtils.fromHexString;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
public class KeyAgreementTest extends BrambleTestCase {
@@ -38,7 +35,6 @@ public class KeyAgreementTest extends BrambleTestCase {
private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
private final String label = getRandomString(123);
private final byte[][] inputs;
public KeyAgreementTest() {
@@ -52,9 +48,9 @@ public class KeyAgreementTest extends BrambleTestCase {
public void testDerivesSharedSecret() throws Exception {
KeyPair aPair = crypto.generateAgreementKeyPair();
KeyPair bPair = crypto.generateAgreementKeyPair();
SecretKey aShared = crypto.deriveSharedSecret(label,
SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
bPair.getPublic(), aPair, inputs);
SecretKey bShared = crypto.deriveSharedSecret(label,
SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
aPair.getPublic(), bPair, inputs);
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
}
@@ -62,15 +58,18 @@ public class KeyAgreementTest extends BrambleTestCase {
@Test(expected = GeneralSecurityException.class)
public void testRejectsInvalidPublicKey() throws Exception {
KeyPair keyPair = crypto.generateAgreementKeyPair();
PublicKey invalid = new AgreementPublicKey(new byte[32]);
crypto.deriveSharedSecret(label, invalid, keyPair, inputs);
PublicKey invalid = new Curve25519PublicKey(new byte[32]);
crypto.deriveSharedSecret(SHARED_SECRET_LABEL, invalid, keyPair,
inputs);
}
@Test
public void testRfc7748TestVector() {
byte[] aPriv = parsePrivateKey(ALICE_PRIVATE);
public void testRfc7748TestVector() throws Exception {
// 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[] bPriv = parsePrivateKey(BOB_PRIVATE);
byte[] bPriv = Curve25519KeyParser.clamp(fromHexString(BOB_PRIVATE));
byte[] bPub = fromHexString(BOB_PUBLIC);
byte[] sharedSecret = fromHexString(SHARED_SECRET);
Curve25519 curve25519 = Curve25519.getInstance("java");
@@ -79,82 +78,4 @@ public class KeyAgreementTest extends BrambleTestCase {
assertArrayEquals(sharedSecret,
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);
@Test
public void testAgreementPublicKeyLength() {
public void testAgreementPublicKeyLength() throws Exception {
// Generate 10 agreement key pairs
for (int i = 0; i < 10; i++) {
KeyPair keyPair = crypto.generateAgreementKeyPair();
@@ -70,7 +70,7 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
}
@Test
public void testAgreementKeyParserByFuzzing() {
public void testAgreementKeyParserByFuzzing() throws Exception {
KeyParser parser = crypto.getAgreementKeyParser();
// Generate a key pair to get the proper public key length
KeyPair p = crypto.generateAgreementKeyPair();
@@ -92,7 +92,7 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
}
@Test
public void testSignaturePublicKeyLength() {
public void testSignaturePublicKeyLength() throws Exception {
// Generate 10 signature key pairs
for (int i = 0; i < 10; i++) {
KeyPair keyPair = crypto.generateSignatureKeyPair();
@@ -107,10 +107,10 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
// Generate 10 signature key pairs
for (int i = 0; i < 10; i++) {
KeyPair keyPair = crypto.generateSignatureKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
byte[] key = keyPair.getPrivate().getEncoded();
// Sign some random data and check the length of the signature
byte[] toBeSigned = getRandomBytes(1234);
byte[] signature = crypto.sign("label", toBeSigned, privateKey);
byte[] signature = crypto.sign("label", toBeSigned, key);
assertTrue(signature.length <= MAX_SIGNATURE_BYTES);
}
}
@@ -123,15 +123,16 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
byte[] message = getRandomBytes(123);
byte[] signature = crypto.sign("test", message, privateKey);
byte[] signature = crypto.sign("test", message,
privateKey.getEncoded());
// Verify the signature
assertTrue(crypto.verifySignature(signature, "test", message,
publicKey));
publicKey.getEncoded()));
// Encode and parse the public key - no exceptions should be thrown
publicKey = parser.parsePublicKey(publicKey.getEncoded());
// Verify the signature again
assertTrue(crypto.verifySignature(signature, "test", message,
publicKey));
publicKey.getEncoded()));
}
@Test
@@ -142,21 +143,23 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
byte[] message = getRandomBytes(123);
byte[] signature = crypto.sign("test", message, privateKey);
byte[] signature = crypto.sign("test", message,
privateKey.getEncoded());
// Verify the signature
assertTrue(crypto.verifySignature(signature, "test", message,
publicKey));
publicKey.getEncoded()));
// Encode and parse the private key - no exceptions should be thrown
privateKey = parser.parsePrivateKey(privateKey.getEncoded());
// 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,
publicKey));
publicKey.getEncoded()));
assertArrayEquals(signature, signature1);
}
@Test
public void testSignatureKeyParserByFuzzing() {
public void testSignatureKeyParserByFuzzing() throws Exception {
KeyParser parser = crypto.getSignatureKeyParser();
// Generate a key pair to get the proper public key length
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.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.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
@@ -21,24 +19,23 @@ public abstract class SignatureTest extends BrambleTestCase {
protected final CryptoComponent crypto;
private final PublicKey publicKey;
private final PrivateKey privateKey;
private final byte[] publicKey, privateKey;
private final String label = StringUtils.getRandomString(42);
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
protected abstract KeyPair generateKeyPair();
protected abstract byte[] sign(String label, byte[] toSign,
PrivateKey privateKey) throws GeneralSecurityException;
byte[] privateKey) throws GeneralSecurityException;
protected abstract boolean verify(byte[] signature, String label,
byte[] signed, PublicKey publicKey) throws GeneralSecurityException;
byte[] signed, byte[] publicKey) throws GeneralSecurityException;
SignatureTest() {
crypto = new CryptoComponentImpl(new TestSecureRandomProvider(), null);
KeyPair k = generateKeyPair();
publicKey = k.getPublic();
privateKey = k.getPrivate();
publicKey = k.getPublic().getEncoded();
privateKey = k.getPrivate().getEncoded();
}
@Test
@@ -54,7 +51,7 @@ public abstract class SignatureTest extends BrambleTestCase {
public void testDifferentKeysProduceDifferentSignatures() throws Exception {
// Generate second private key
KeyPair k2 = generateKeyPair();
PrivateKey privateKey2 = k2.getPrivate();
byte[] privateKey2 = k2.getPrivate().getEncoded();
// Calculate the signature with each key
byte[] sig1 = sign(label, inputBytes, privateKey);
byte[] sig2 = sign(label, inputBytes, privateKey2);
@@ -95,7 +92,7 @@ public abstract class SignatureTest extends BrambleTestCase {
public void testDifferentKeyFailsVerification() throws Exception {
// Generate second private key
KeyPair k2 = generateKeyPair();
PrivateKey privateKey2 = k2.getPrivate();
byte[] privateKey2 = k2.getPrivate().getEncoded();
// calculate the signature with different key, should fail to verify
byte[] sig = sign(label, inputBytes, privateKey2);
assertFalse(verify(sig, label, inputBytes, publicKey));

View File

@@ -1,30 +1,23 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
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.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.junit.Test;
import java.util.ArrayList;
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.getTransportId;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
public class TransportKeyDerivationTest extends BrambleTestCase {
@@ -36,70 +29,70 @@ public class TransportKeyDerivationTest extends BrambleTestCase {
private final SecretKey rootKey = getSecretKey();
@Test
public void testRotationKeysAreDistinct() {
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId,
public void testKeysAreDistinct() {
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId,
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
assertAllDifferent(kA);
assertAllDifferent(kB);
}
@Test
public void testRotationKeysAreNotRotatedToPreviousPeriod() {
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
public void testKeysAreNotRotatedToPreviousPeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 122);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 122);
assertSame(k, k1);
}
@Test
public void testRotationKeysAreNotRotatedToCurrentPeriod() {
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
public void testKeysAreNotRotatedToCurrentPeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 123);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 123);
assertSame(k, k1);
}
@Test
public void testRotationKeysAreRotatedByOnePeriod() {
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
public void testKeysAreRotatedByOnePeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 124);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 124);
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
}
@Test
public void testRotationKeysAreRotatedByTwoPeriods() {
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
public void testKeysAreRotatedByTwoPeriods() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 125);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 125);
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
}
@Test
public void testRotationKeysAreRotatedByThreePeriods() {
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
public void testKeysAreRotatedByThreePeriods() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 126);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 126);
assertAllDifferent(k, k1);
}
@Test
public void testCurrentRotationKeysMatchContact() {
public void testCurrentKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId,
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId,
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
// 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());
// Rotate into the future
kA = transportCrypto.updateTransportKeys(kA, 456);
kB = transportCrypto.updateTransportKeys(kB, 456);
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(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
@@ -107,23 +100,23 @@ public class TransportKeyDerivationTest extends BrambleTestCase {
}
@Test
public void testPreviousRotationKeysMatchContact() {
public void testPreviousKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId,
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId,
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
// 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);
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(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);
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Bob's previous incoming keys should equal Alice's current
// outgoing keys
assertMatches(kB.getPreviousIncomingKeys(),
@@ -131,208 +124,44 @@ public class TransportKeyDerivationTest extends BrambleTestCase {
}
@Test
public void testNextRotationKeysMatchContact() {
public void testNextKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId,
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId,
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
// 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);
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(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);
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's next incoming keys should equal Bob's current outgoing keys
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
}
@Test
public void testRootKeyAffectsRotationKeyDerivation() {
public void testRootKeyAffectsOutput() {
SecretKey rootKey1 = getSecretKey();
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.deriveRotationKeys(transportId,
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
rootKey1, 123, true, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsRotationKeyDerivation() {
public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId();
assertNotEquals(transportId.getString(), transportId1.getString());
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.deriveRotationKeys(transportId1,
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
rootKey, 123, true, true);
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.event.ContactAddedEvent;
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.db.ContactExistsException;
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.MessagesAckedEvent;
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.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys;
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.test.BrambleMockTestCase;
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.Collections.emptyMap;
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.SHARED;
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.transport.TransportConstants.REORDERING_WINDOW_SIZE;
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.getClientId;
import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getIdentity;
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.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -118,7 +117,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final int maxLatency;
private final ContactId contactId;
private final Contact contact;
private final KeySetId keySetId;
private final TransportKeySetId keySetId;
private final PendingContactId pendingContactId;
public DatabaseComponentImplTest() {
@@ -140,7 +139,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
contact = getContact(author, localAuthor.getId(), true);
contactId = contact.getId();
alias = contact.getAlias();
keySetId = new KeySetId(345);
keySetId = new TransportKeySetId(345);
pendingContactId = new PendingContactId(getRandomId());
}
@@ -285,15 +284,24 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception {
context.checking(new Expectations() {{
// 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));
exactly(16).of(database).containsContact(txn, contactId);
exactly(17).of(database).containsContact(txn, contactId);
will(returnValue(false));
exactly(16).of(database).abortTransaction(txn);
exactly(17).of(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addHandshakeKeys(transaction, contactId,
createHandshakeKeys()));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.transaction(false, transaction ->
db.addTransportKeys(transaction, contactId,
@@ -468,8 +476,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}
try {
PublicKey publicKey = getAgreementPublicKey();
PrivateKey privateKey = getAgreementPrivateKey();
byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
byte[] privateKey = getRandomBytes(123);
db.transaction(false, transaction ->
db.setHandshakeKeyPair(transaction, localAuthor.getId(),
publicKey, privateKey));
@@ -489,8 +497,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
exactly(8).of(database).containsGroup(txn, groupId);
will(returnValue(false));
exactly(8).of(database).abortTransaction(txn);
// Allow other checks to pass
allowing(database).containsContact(txn, contactId);
// This is needed for getMessageStatus() and setGroupVisibility()
exactly(2).of(database).containsContact(txn, contactId);
will(returnValue(true));
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
@@ -573,8 +581,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
exactly(11).of(database).containsMessage(txn, messageId);
will(returnValue(false));
exactly(11).of(database).abortTransaction(txn);
// Allow other checks to pass
allowing(database).containsContact(txn, contactId);
// This is needed for getMessageStatus() to proceed
exactly(1).of(database).containsContact(txn, contactId);
will(returnValue(true));
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
@@ -674,38 +682,15 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception {
context.checking(new Expectations() {{
// 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));
exactly(8).of(database).containsTransport(txn, transportId);
exactly(5).of(database).containsTransport(txn, transportId);
will(returnValue(false));
exactly(8).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));
exactly(5).of(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
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 {
db.transaction(false, transaction ->
db.getTransportKeys(transaction, transportId));
@@ -725,7 +710,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try {
db.transaction(false, transaction ->
db.removeTransportKeys(transaction, transportId, keySetId));
db.removeTransport(transaction, transportId));
fail();
} catch (NoSuchTransportException expected) {
// Expected
@@ -733,7 +718,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try {
db.transaction(false, transaction ->
db.removeTransport(transaction, transportId));
db.removeTransportKeys(transaction, transportId, keySetId));
fail();
} catch (NoSuchTransportException expected) {
// Expected
@@ -747,15 +732,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} catch (NoSuchTransportException expected) {
// Expected
}
try {
db.transaction(false, transaction ->
db.setTransportKeysActive(transaction, transportId,
keySetId));
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
}
@Test
@@ -775,7 +751,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try {
db.transaction(false, transaction ->
db.addTransportKeys(transaction, pendingContactId,
db.addHandshakeKeys(transaction, pendingContactId,
createHandshakeKeys()));
fail();
} catch (NoSuchPendingContactException expected) {
@@ -1191,7 +1167,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
public void testTransportKeys() throws Exception {
TransportKeys transportKeys = createTransportKeys();
TransportKeySet ks =
new TransportKeySet(keySetId, contactId, null, transportKeys);
new TransportKeySet(keySetId, contactId, transportKeys);
Collection<TransportKeySet> keys = singletonList(ks);
context.checking(new Expectations() {{
@@ -1319,7 +1295,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
});
}
private TransportKeys createHandshakeKeys() {
private HandshakeKeys createHandshakeKeys() {
SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
@@ -1336,7 +1312,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
2, 456, true);
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr,
return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
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.ContactId;
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.db.DatabaseConfig;
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.validation.MessageState;
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.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys;
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.system.SystemClock;
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.concurrent.TimeUnit.SECONDS;
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.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
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.MAX_COMPACTION_INTERVAL_MS;
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.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getIdentity;
import static org.briarproject.bramble.test.TestUtils.getMessage;
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.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
@@ -111,7 +112,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private final MessageId messageId;
private final TransportId transportId;
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 Random random = new Random();
@@ -127,8 +129,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
messageId = message.getId();
transportId = getTransportId();
contactId = new ContactId(1);
keySetId = new KeySetId(1);
keySetId1 = new KeySetId(2);
keySetId = new TransportKeySetId(1);
keySetId1 = new TransportKeySetId(2);
handshakeKeySetId = new HandshakeKeySetId(1);
handshakeKeySetId1 = new HandshakeKeySetId(2);
pendingContact = getPendingContact();
}
@@ -697,14 +701,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
}
}
// Update the transport keys
TransportKeys updated = createTransportKeys(timePeriod + 1, active);
TransportKeys updated1 =
// Rotate the transport keys
TransportKeys rotated = createTransportKeys(timePeriod + 1, active);
TransportKeys rotated1 =
createTransportKeys(timePeriod1 + 1, active);
db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId,
null, updated));
rotated));
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId,
null, updated1));
rotated1));
// Retrieve the transport keys again
allKeys = db.getTransportKeys(txn, transportId);
@@ -712,10 +716,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
for (TransportKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId());
if (ks.getKeySetId().equals(keySetId)) {
assertKeysEquals(updated, ks.getKeys());
assertKeysEquals(rotated, ks.getKeys());
} else {
assertEquals(keySetId1, ks.getKeySetId());
assertKeysEquals(updated1, ks.getKeys());
assertKeysEquals(rotated1, ks.getKeys());
}
}
@@ -739,14 +743,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
actual.getNextIncomingKeys());
assertKeysEquals(expected.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) {
@@ -775,135 +771,154 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
boolean alice = random.nextBoolean();
SecretKey rootKey = getSecretKey();
SecretKey rootKey1 = getSecretKey();
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
TransportKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// 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
db.addIdentity(txn, identity);
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys));
assertEquals(handshakeKeySetId1,
db.addHandshakeKeys(txn, contactId, keys1));
// Retrieve the handshake keys
Collection<TransportKeySet> allKeys =
db.getTransportKeys(txn, transportId);
Collection<HandshakeKeySet> allKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (TransportKeySet ks : allKeys) {
for (HandshakeKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId());
assertNull(ks.getPendingContactId());
if (ks.getKeySetId().equals(keySetId)) {
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(keys, ks.getKeys());
} else {
assertEquals(keySetId1, ks.getKeySetId());
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getKeys());
}
}
// Update the handshake keys
TransportKeys updated =
HandshakeKeys updated =
createHandshakeKeys(timePeriod + 1, rootKey, alice);
TransportKeys updated1 =
HandshakeKeys updated1 =
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId,
null, updated));
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId,
null, updated1));
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
contactId, updated));
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
contactId, updated1));
// Retrieve the handshake keys again
allKeys = db.getTransportKeys(txn, transportId);
allKeys = db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (TransportKeySet ks : allKeys) {
for (HandshakeKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId());
assertNull(ks.getPendingContactId());
if (ks.getKeySetId().equals(keySetId)) {
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(updated, ks.getKeys());
} else {
assertEquals(keySetId1, ks.getKeySetId());
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(updated1, ks.getKeys());
}
}
// Removing the contact should remove the handshake keys
db.removeContact(txn, contactId);
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
db.commitTransaction(txn);
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
public void testHandshakeKeysForPendingContact() throws Exception {
long timePeriod = 123, timePeriod1 = 234;
boolean alice = random.nextBoolean();
SecretKey rootKey = getSecretKey();
SecretKey rootKey1 = getSecretKey();
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
TransportKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// 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
db.addPendingContact(txn, pendingContact);
db.addTransport(txn, transportId, 123);
assertEquals(keySetId,
db.addTransportKeys(txn, pendingContact.getId(), keys));
assertEquals(keySetId1,
db.addTransportKeys(txn, pendingContact.getId(), keys1));
assertEquals(handshakeKeySetId, db.addHandshakeKeys(txn,
pendingContact.getId(), keys));
assertEquals(handshakeKeySetId1, db.addHandshakeKeys(txn,
pendingContact.getId(), keys1));
// Retrieve the handshake keys
Collection<TransportKeySet> allKeys =
db.getTransportKeys(txn, transportId);
Collection<HandshakeKeySet> allKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (TransportKeySet ks : allKeys) {
for (HandshakeKeySet ks : allKeys) {
assertNull(ks.getContactId());
assertEquals(pendingContact.getId(), ks.getPendingContactId());
if (ks.getKeySetId().equals(keySetId)) {
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(keys, ks.getKeys());
} else {
assertEquals(keySetId1, ks.getKeySetId());
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getKeys());
}
}
// Update the handshake keys
TransportKeys updated =
HandshakeKeys updated =
createHandshakeKeys(timePeriod + 1, rootKey, alice);
TransportKeys updated1 =
HandshakeKeys updated1 =
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
db.updateTransportKeys(txn, new TransportKeySet(keySetId, null,
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
pendingContact.getId(), updated));
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, null,
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
pendingContact.getId(), updated1));
// Retrieve the handshake keys again
allKeys = db.getTransportKeys(txn, transportId);
allKeys = db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (TransportKeySet ks : allKeys) {
for (HandshakeKeySet ks : allKeys) {
assertNull(ks.getContactId());
assertEquals(pendingContact.getId(), ks.getPendingContactId());
if (ks.getKeySetId().equals(keySetId)) {
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(updated, ks.getKeys());
} else {
assertEquals(keySetId1, ks.getKeySetId());
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(updated1, ks.getKeys());
}
}
// Removing the pending contact should remove the handshake keys
db.removePendingContact(txn, pendingContact.getId());
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
db.commitTransaction(txn);
db.close();
@@ -956,7 +971,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
long timePeriod = 123;
SecretKey rootKey = getSecretKey();
boolean alice = random.nextBoolean();
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
Database<Connection> db = open(false);
@@ -967,20 +982,20 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
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
db.incrementStreamCounter(txn, transportId, keySetId);
db.incrementStreamCounter(txn, transportId, keySetId);
Collection<TransportKeySet> newKeys =
db.getTransportKeys(txn, transportId);
db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
Collection<HandshakeKeySet> newKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(1, newKeys.size());
TransportKeySet ks = newKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId());
HandshakeKeySet ks = newKeys.iterator().next();
assertEquals(handshakeKeySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId());
TransportKeys k = ks.getKeys();
HandshakeKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId());
assertNotNull(k.getRootKey());
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
assertEquals(alice, k.isAlice());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
@@ -1049,7 +1064,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
long timePeriod = 123;
SecretKey rootKey = getSecretKey();
boolean alice = random.nextBoolean();
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
long base = keys.getCurrentIncomingKeys().getWindowBase();
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
@@ -1061,21 +1076,21 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
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
random.nextBytes(bitmap);
db.setReorderingWindow(txn, keySetId, transportId, timePeriod,
db.setReorderingWindow(txn, handshakeKeySetId, transportId, timePeriod,
base + 1, bitmap);
Collection<TransportKeySet> newKeys =
db.getTransportKeys(txn, transportId);
Collection<HandshakeKeySet> newKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(1, newKeys.size());
TransportKeySet ks = newKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId());
HandshakeKeySet ks = newKeys.iterator().next();
assertEquals(handshakeKeySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId());
TransportKeys k = ks.getKeys();
HandshakeKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId());
assertNotNull(k.getRootKey());
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
assertEquals(alice, k.isAlice());
IncomingKeys inCurr = k.getCurrentIncomingKeys();
@@ -2235,8 +2250,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Identity withoutKeys =
new Identity(localAuthor, null, null, identity.getTimeCreated());
assertFalse(withoutKeys.hasHandshakeKeyPair());
PublicKey publicKey = getAgreementPublicKey();
PrivateKey privateKey = getAgreementPrivateKey();
byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
byte[] privateKey = getRandomBytes(123);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
@@ -2247,12 +2262,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.setHandshakeKeyPair(txn, localAuthor.getId(), publicKey, privateKey);
retrieved = db.getIdentity(txn, localAuthor.getId());
assertTrue(retrieved.hasHandshakeKeyPair());
PublicKey handshakePub = retrieved.getHandshakePublicKey();
assertNotNull(handshakePub);
assertArrayEquals(publicKey.getEncoded(), handshakePub.getEncoded());
PrivateKey handshakePriv = retrieved.getHandshakePrivateKey();
assertNotNull(handshakePriv);
assertArrayEquals(privateKey.getEncoded(), handshakePriv.getEncoded());
assertArrayEquals(publicKey, retrieved.getHandshakePublicKey());
assertArrayEquals(privateKey, retrieved.getHandshakePrivateKey());
db.commitTransaction(txn);
db.close();
@@ -2291,7 +2302,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
}
private TransportKeys createHandshakeKeys(long timePeriod,
private HandshakeKeys createHandshakeKeys(long timePeriod,
SecretKey rootKey, boolean alice) {
SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey();
@@ -2309,7 +2320,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
timePeriod, 456, true);
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr,
return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
rootKey, alice);
}

View File

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

View File

@@ -16,13 +16,10 @@ import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Rule;
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.MASTER_KEY_LABEL;
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.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.getSecretKey;
import static org.hamcrest.Matchers.equalTo;
@@ -37,17 +34,21 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
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[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8);
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[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19);
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[] badConfirm = getRandomBytes(SecretKey.LENGTH);
@@ -63,13 +64,15 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
PayloadEncoder payloadEncoder;
@Mock
KeyAgreementTransport transport;
@Mock
PublicKey ourPubKey;
@Test
public void testAliceProtocol() throws Exception {
// set up
Payload theirPayload = new Payload(bobCommit, emptyList());
Payload ourPayload = new Payload(aliceCommit, emptyList());
KeyPair ourKeyPair = new KeyPair(alicePubKey, getAgreementPrivateKey());
Payload theirPayload = new Payload(bobCommit, null);
Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey();
SecretKey masterKey = getSecretKey();
@@ -84,18 +87,24 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(alicePayload));
allowing(payloadEncoder).encode(theirPayload);
will(returnValue(bobPayload));
allowing(ourPubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
allowing(alicePubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
allowing(bobPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
// Alice sends her public key
oneOf(transport).sendKey(alicePubKey.getEncoded());
oneOf(transport).sendKey(alicePubKeyBytes);
// Alice receives Bob's public key
oneOf(callbacks).connectionWaiting();
oneOf(transport).receiveKey();
will(returnValue(bobPubKey.getEncoded()));
will(returnValue(bobPubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(bobPubKey.getEncoded());
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
will(returnValue(bobPubKey));
// Alice verifies Bob's public key
@@ -105,7 +114,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// Alice computes shared secret
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
ourKeyPair, new byte[] {PROTOCOL_VERSION},
alicePubKey.getEncoded(), bobPubKey.getEncoded());
alicePubKeyBytes, bobPubKeyBytes);
will(returnValue(sharedSecret));
// Alice sends her confirmation record
@@ -137,9 +146,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test
public void testBobProtocol() throws Exception {
// set up
Payload theirPayload = new Payload(aliceCommit, emptyList());
Payload ourPayload = new Payload(bobCommit, emptyList());
KeyPair ourKeyPair = new KeyPair(bobPubKey, getAgreementPrivateKey());
Payload theirPayload = new Payload(aliceCommit, null);
Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey();
SecretKey masterKey = getSecretKey();
@@ -154,14 +163,20 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(bobPayload));
allowing(payloadEncoder).encode(theirPayload);
will(returnValue(alicePayload));
allowing(ourPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
allowing(alicePubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
allowing(bobPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
// Bob receives Alice's public key
oneOf(transport).receiveKey();
will(returnValue(alicePubKey.getEncoded()));
will(returnValue(alicePubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(alicePubKey.getEncoded());
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
will(returnValue(alicePubKey));
// Bob verifies Alice's public key
@@ -169,12 +184,12 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(aliceCommit));
// Bob sends his public key
oneOf(transport).sendKey(bobPubKey.getEncoded());
oneOf(transport).sendKey(bobPubKeyBytes);
// Bob computes shared secret
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
ourKeyPair, new byte[] {PROTOCOL_VERSION},
alicePubKey.getEncoded(), bobPubKey.getEncoded());
alicePubKeyBytes, bobPubKeyBytes);
will(returnValue(sharedSecret));
// Bob receives Alices's confirmation record
@@ -206,9 +221,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class)
public void testAliceProtocolAbortOnBadKey() throws Exception {
// set up
Payload theirPayload = new Payload(bobCommit, emptyList());
Payload ourPayload = new Payload(aliceCommit, emptyList());
KeyPair ourKeyPair = new KeyPair(alicePubKey, getAgreementPrivateKey());
Payload theirPayload = new Payload(bobCommit, null);
Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport,
@@ -217,18 +232,20 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// expectations
context.checking(new Expectations() {{
// Helpers
allowing(ourPubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
// Alice sends her public key
oneOf(transport).sendKey(alicePubKey.getEncoded());
oneOf(transport).sendKey(alicePubKeyBytes);
// Alice receives a bad public key
oneOf(callbacks).connectionWaiting();
oneOf(transport).receiveKey();
will(returnValue(badPubKey.getEncoded()));
will(returnValue(badPubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(badPubKey.getEncoded());
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
will(returnValue(badPubKey));
// Alice verifies Bob's public key
@@ -241,7 +258,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// Alice never computes shared secret
never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey,
ourKeyPair, new byte[] {PROTOCOL_VERSION},
alicePubKey.getEncoded(), bobPubKey.getEncoded());
alicePubKeyBytes, bobPubKeyBytes);
}});
// execute
@@ -251,9 +268,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class)
public void testBobProtocolAbortOnBadKey() throws Exception {
// set up
Payload theirPayload = new Payload(aliceCommit, emptyList());
Payload ourPayload = new Payload(bobCommit, emptyList());
KeyPair ourKeyPair = new KeyPair(bobPubKey, getAgreementPrivateKey());
Payload theirPayload = new Payload(aliceCommit, null);
Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport,
@@ -262,14 +279,16 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// expectations
context.checking(new Expectations() {{
// Helpers
allowing(ourPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
// Bob receives a bad public key
oneOf(transport).receiveKey();
will(returnValue(badPubKey.getEncoded()));
will(returnValue(badPubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(badPubKey.getEncoded());
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
will(returnValue(badPubKey));
// Bob verifies Alice's public key
@@ -280,7 +299,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
oneOf(transport).sendAbort(false);
// Bob never sends his public key
never(transport).sendKey(bobPubKey.getEncoded());
never(transport).sendKey(bobPubKeyBytes);
}});
// execute
@@ -290,9 +309,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class)
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
// set up
Payload theirPayload = new Payload(bobCommit, emptyList());
Payload ourPayload = new Payload(aliceCommit, emptyList());
KeyPair ourKeyPair = new KeyPair(alicePubKey, getAgreementPrivateKey());
Payload theirPayload = new Payload(bobCommit, null);
Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey();
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
@@ -306,18 +325,22 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(alicePayload));
allowing(payloadEncoder).encode(theirPayload);
will(returnValue(bobPayload));
allowing(ourPubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
allowing(bobPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
// Alice sends her public key
oneOf(transport).sendKey(alicePubKey.getEncoded());
oneOf(transport).sendKey(alicePubKeyBytes);
// Alice receives Bob's public key
oneOf(callbacks).connectionWaiting();
oneOf(transport).receiveKey();
will(returnValue(bobPubKey.getEncoded()));
will(returnValue(bobPubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(bobPubKey.getEncoded());
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
will(returnValue(bobPubKey));
// Alice verifies Bob's public key
@@ -327,7 +350,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// Alice computes shared secret
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
ourKeyPair, new byte[] {PROTOCOL_VERSION},
alicePubKey.getEncoded(), bobPubKey.getEncoded());
alicePubKeyBytes, bobPubKeyBytes);
will(returnValue(sharedSecret));
// Alice sends her confirmation record
@@ -361,9 +384,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Test(expected = AbortException.class)
public void testBobProtocolAbortOnBadConfirm() throws Exception {
// set up
Payload theirPayload = new Payload(aliceCommit, emptyList());
Payload ourPayload = new Payload(bobCommit, emptyList());
KeyPair ourKeyPair = new KeyPair(bobPubKey, getAgreementPrivateKey());
Payload theirPayload = new Payload(aliceCommit, null);
Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey();
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
@@ -377,14 +400,18 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(bobPayload));
allowing(payloadEncoder).encode(theirPayload);
will(returnValue(alicePayload));
allowing(ourPubKey).getEncoded();
will(returnValue(bobPubKeyBytes));
allowing(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
allowing(alicePubKey).getEncoded();
will(returnValue(alicePubKeyBytes));
// Bob receives Alice's public key
oneOf(transport).receiveKey();
will(returnValue(alicePubKey.getEncoded()));
will(returnValue(alicePubKeyBytes));
oneOf(callbacks).initialRecordReceived();
oneOf(keyParser).parsePublicKey(alicePubKey.getEncoded());
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
will(returnValue(alicePubKey));
// Bob verifies Alice's public key
@@ -392,12 +419,12 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(aliceCommit));
// Bob sends his public key
oneOf(transport).sendKey(bobPubKey.getEncoded());
oneOf(transport).sendKey(bobPubKeyBytes);
// Bob computes shared secret
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
ourKeyPair, new byte[] {PROTOCOL_VERSION},
alicePubKey.getEncoded(), bobPubKey.getEncoded());
alicePubKeyBytes, bobPubKeyBytes);
will(returnValue(sharedSecret));
// 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.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginException;
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.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.concurrent.Synchroniser;
import org.junit.Test;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -37,16 +33,10 @@ public class PluginManagerImplTest extends BrambleTestCase {
setThreadingPolicy(new Synchroniser());
}};
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);
PluginConfig pluginConfig = context.mock(PluginConfig.class);
ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
SettingsManager settingsManager =
context.mock(SettingsManager.class);
TransportPropertyManager transportPropertyManager =
@@ -122,9 +112,9 @@ public class PluginManagerImplTest extends BrambleTestCase {
oneOf(duplexPlugin).stop();
}});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, scheduler,
eventBus, pluginConfig, connectionManager, connectionRegistry,
settingsManager, transportPropertyManager, random, clock);
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
pluginConfig, connectionManager, settingsManager,
transportPropertyManager);
// Two plugins should be started and stopped
p.startService();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,7 @@
package org.briarproject.briar.android;
import android.app.Activity;
import android.content.SharedPreferences;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import java.util.Collection;
import java.util.logging.LogRecord;
@@ -14,8 +11,6 @@ import java.util.logging.LogRecord;
*/
public interface BriarApplication {
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
Collection<LogRecord> getRecentLogRecords();
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.briar.R;
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.LockManager;
@@ -47,7 +48,6 @@ import static java.util.logging.Level.INFO;
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.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_NOTIFICATION_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;
nm.notify(FAILURE_NOTIFICATION_ID, b.build());
// 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.putExtra(EXTRA_STARTUP_FAILED, true);
startActivity(i);

View File

@@ -74,7 +74,7 @@ public class StartupFailureActivity extends BaseActivity implements
@Override
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.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.BlogFragment;
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.KeyAgreementActivity;
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.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.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.panic.PanicPreferencesActivity;
import org.briarproject.briar.android.panic.PanicResponderActivity;
@@ -95,12 +94,14 @@ public interface ActivityComponent {
void inject(SplashScreenActivity activity);
void inject(StartupActivity activity);
void inject(SetupActivity activity);
void inject(OpenDatabaseActivity activity);
void inject(NavDrawerActivity activity);
void inject(PasswordActivity activity);
void inject(PanicResponderActivity activity);
void inject(PanicPreferencesActivity activity);
@@ -176,17 +177,12 @@ public interface ActivityComponent {
void inject(PendingContactListActivity activity);
// Fragments
void inject(AuthorNameFragment fragment);
void inject(SetPasswordFragment fragment);
void inject(PasswordFragment fragment);
void inject(DozeFragment fragment);
void inject(PasswordFragment imageFragment);
void inject(OpenDatabaseFragment activity);
void inject(ContactListFragment 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.DbController;
import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.login.ChangePasswordController;
import org.briarproject.briar.android.login.ChangePasswordControllerImpl;
import org.briarproject.briar.android.account.SetupController;
import org.briarproject.briar.android.account.SetupControllerImpl;
import org.briarproject.briar.android.login.PasswordController;
import org.briarproject.briar.android.login.PasswordControllerImpl;
import org.briarproject.briar.android.login.SetupController;
import org.briarproject.briar.android.login.SetupControllerImpl;
import org.briarproject.briar.android.navdrawer.NavDrawerController;
import org.briarproject.briar.android.navdrawer.NavDrawerControllerImpl;
@@ -48,8 +48,8 @@ public class ActivityModule {
@ActivityScope
@Provides
ChangePasswordController providePasswordController(
ChangePasswordControllerImpl passwordController) {
PasswordController providePasswordController(
PasswordControllerImpl passwordController) {
return passwordController;
}

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