mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
46 Commits
poller-ref
...
beta-1.1.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37d0b61d7b | ||
|
|
98a1ec89d7 | ||
|
|
a61860af85 | ||
|
|
90437f4fa4 | ||
|
|
06212453b2 | ||
|
|
ddc9b5b066 | ||
|
|
0aa6166afa | ||
|
|
60b91bc317 | ||
|
|
20481a3790 | ||
|
|
576934910e | ||
|
|
4abc26093e | ||
|
|
aed63746e7 | ||
|
|
816598b631 | ||
|
|
c062c16d27 | ||
|
|
435b43488a | ||
|
|
faa6a85148 | ||
|
|
3770a9f217 | ||
|
|
c6211be488 | ||
|
|
5a73e50248 | ||
|
|
dc6971734a | ||
|
|
69e57bee61 | ||
|
|
af8cabbb28 | ||
|
|
6f31a3c2ad | ||
|
|
d3469e3782 | ||
|
|
9d64b186ff | ||
|
|
ca591b5c7b | ||
|
|
2c4188caf5 | ||
|
|
0b30a0786e | ||
|
|
f9b928c12a | ||
|
|
afa0b96293 | ||
|
|
dd50f4bcd4 | ||
|
|
f42fc5213e | ||
|
|
84e2402404 | ||
|
|
5adc9d8dbd | ||
|
|
3f51ad6c07 | ||
|
|
1fd6d7a6d5 | ||
|
|
7dc4dc566f | ||
|
|
658c63d94e | ||
|
|
ee05c32871 | ||
|
|
d2951eb3cd | ||
|
|
de8a60ea21 | ||
|
|
0e77a47cc1 | ||
|
|
22ebdd8e42 | ||
|
|
e37ee7ee04 | ||
|
|
5676e18a22 | ||
|
|
5ece6505da |
@@ -11,8 +11,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
versionCode 10106
|
versionCode 10107
|
||||||
versionName "1.1.6"
|
versionName "1.1.7"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
|
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.account.AccountManager;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.util.IoUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -20,6 +19,9 @@ import javax.annotation.Nullable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
|
||||||
|
|
||||||
class AndroidAccountManager extends AccountManagerImpl
|
class AndroidAccountManager extends AccountManagerImpl
|
||||||
implements AccountManager {
|
implements AccountManager {
|
||||||
@@ -41,6 +43,16 @@ class AndroidAccountManager extends AccountManagerImpl
|
|||||||
appContext = app.getApplicationContext();
|
appContext = app.getApplicationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accountExists() {
|
||||||
|
boolean exists = super.accountExists();
|
||||||
|
if (!exists && LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Account does not exist. Contents of account directory:");
|
||||||
|
logFileOrDir(LOG, INFO, getDataDir());
|
||||||
|
}
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
// Locking: stateChangeLock
|
// Locking: stateChangeLock
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -74,9 +86,17 @@ class AndroidAccountManager extends AccountManagerImpl
|
|||||||
@Override
|
@Override
|
||||||
public void deleteAccount() {
|
public void deleteAccount() {
|
||||||
synchronized (stateChangeLock) {
|
synchronized (stateChangeLock) {
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Contents of account directory before deleting:");
|
||||||
|
logFileOrDir(LOG, INFO, getDataDir());
|
||||||
|
}
|
||||||
super.deleteAccount();
|
super.deleteAccount();
|
||||||
SharedPreferences defaultPrefs = getDefaultSharedPreferences();
|
SharedPreferences defaultPrefs = getDefaultSharedPreferences();
|
||||||
deleteAppData(prefs, defaultPrefs);
|
deleteAppData(prefs, defaultPrefs);
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Contents of account directory after deleting:");
|
||||||
|
logFileOrDir(LOG, INFO, getDataDir());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +114,7 @@ class AndroidAccountManager extends AccountManagerImpl
|
|||||||
}
|
}
|
||||||
// Delete files, except lib and shared_prefs directories
|
// Delete files, except lib and shared_prefs directories
|
||||||
Set<File> files = new HashSet<>();
|
Set<File> files = new HashSet<>();
|
||||||
File dataDir = new File(appContext.getApplicationInfo().dataDir);
|
File dataDir = getDataDir();
|
||||||
@Nullable
|
@Nullable
|
||||||
File[] fileArray = dataDir.listFiles();
|
File[] fileArray = dataDir.listFiles();
|
||||||
if (fileArray == null) {
|
if (fileArray == null) {
|
||||||
@@ -121,13 +141,17 @@ class AndroidAccountManager extends AccountManagerImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
IoUtils.deleteFileOrDir(file);
|
deleteFileOrDir(file);
|
||||||
}
|
}
|
||||||
// Recreate the cache dir as some OpenGL drivers expect it to exist
|
// Recreate the cache dir as some OpenGL drivers expect it to exist
|
||||||
if (!new File(dataDir, "cache").mkdirs())
|
if (!new File(dataDir, "cache").mkdirs())
|
||||||
LOG.warning("Could not recreate cache dir");
|
LOG.warning("Could not recreate cache dir");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private File getDataDir() {
|
||||||
|
return new File(appContext.getApplicationInfo().dataDir);
|
||||||
|
}
|
||||||
|
|
||||||
private void addIfNotNull(Set<File> files, @Nullable File file) {
|
private void addIfNotNull(Set<File> files, @Nullable File file) {
|
||||||
if (file != null) files.add(file);
|
if (file != null) files.add(file);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(editor));
|
will(returnValue(editor));
|
||||||
oneOf(editor).commit();
|
oneOf(editor).commit();
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
oneOf(app).getApplicationInfo();
|
allowing(app).getApplicationInfo();
|
||||||
will(returnValue(applicationInfo));
|
will(returnValue(applicationInfo));
|
||||||
oneOf(app).getFilesDir();
|
oneOf(app).getFilesDir();
|
||||||
will(returnValue(filesDir));
|
will(returnValue(filesDir));
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.util.StringUtils;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +39,7 @@ public class Bytes implements Comparable<Bytes> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(@Nullable Object o) {
|
||||||
return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes);
|
return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.briarproject.bramble.api.client;
|
package org.briarproject.bramble.api.client;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -96,14 +98,18 @@ public interface ClientHelper {
|
|||||||
|
|
||||||
BdfList toList(Author a);
|
BdfList toList(Author a);
|
||||||
|
|
||||||
byte[] sign(String label, BdfList toSign, byte[] privateKey)
|
byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
|
||||||
throws FormatException, GeneralSecurityException;
|
throws FormatException, GeneralSecurityException;
|
||||||
|
|
||||||
void verifySignature(byte[] signature, String label, BdfList signed,
|
void verifySignature(byte[] signature, String label, BdfList signed,
|
||||||
byte[] publicKey) throws FormatException, GeneralSecurityException;
|
PublicKey publicKey)
|
||||||
|
throws FormatException, GeneralSecurityException;
|
||||||
|
|
||||||
Author parseAndValidateAuthor(BdfList author) throws FormatException;
|
Author parseAndValidateAuthor(BdfList author) throws FormatException;
|
||||||
|
|
||||||
|
PublicKey parseAndValidateAgreementPublicKey(byte[] publicKeyBytes)
|
||||||
|
throws FormatException;
|
||||||
|
|
||||||
TransportProperties parseAndValidateTransportProperties(
|
TransportProperties parseAndValidateTransportProperties(
|
||||||
BdfDictionary properties) throws FormatException;
|
BdfDictionary properties) throws FormatException;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.contact;
|
package org.briarproject.bramble.api.contact;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
@@ -8,7 +9,6 @@ import javax.annotation.Nullable;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -21,21 +21,17 @@ public class Contact {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private final String alias;
|
private final String alias;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final byte[] handshakePublicKey;
|
private final PublicKey handshakePublicKey;
|
||||||
private final boolean verified;
|
private final boolean verified;
|
||||||
|
|
||||||
public Contact(ContactId id, Author author, AuthorId localAuthorId,
|
public Contact(ContactId id, Author author, AuthorId localAuthorId,
|
||||||
@Nullable String alias, @Nullable byte[] handshakePublicKey,
|
@Nullable String alias, @Nullable PublicKey handshakePublicKey,
|
||||||
boolean verified) {
|
boolean verified) {
|
||||||
if (alias != null) {
|
if (alias != null) {
|
||||||
int aliasLength = toUtf8(alias).length;
|
int aliasLength = toUtf8(alias).length;
|
||||||
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
|
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
if (handshakePublicKey != null && (handshakePublicKey.length == 0 ||
|
|
||||||
handshakePublicKey.length > MAX_PUBLIC_KEY_LENGTH)) {
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.author = author;
|
this.author = author;
|
||||||
this.localAuthorId = localAuthorId;
|
this.localAuthorId = localAuthorId;
|
||||||
@@ -62,7 +58,7 @@ public class Contact {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public byte[] getHandshakePublicKey() {
|
public PublicKey getHandshakePublicKey() {
|
||||||
return handshakePublicKey;
|
return handshakePublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.contact;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,7 +29,7 @@ public class ContactId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(@Nullable Object o) {
|
||||||
return o instanceof ContactId && id == ((ContactId) o).id;
|
return o instanceof ContactId && id == ((ContactId) o).id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.contact;
|
package org.briarproject.bramble.api.contact;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
@@ -9,12 +10,12 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
public class PendingContact {
|
public class PendingContact {
|
||||||
|
|
||||||
private final PendingContactId id;
|
private final PendingContactId id;
|
||||||
private final byte[] publicKey;
|
private final PublicKey publicKey;
|
||||||
private final String alias;
|
private final String alias;
|
||||||
private final PendingContactState state;
|
private final PendingContactState state;
|
||||||
private final long timestamp;
|
private final long timestamp;
|
||||||
|
|
||||||
public PendingContact(PendingContactId id, byte[] publicKey,
|
public PendingContact(PendingContactId id, PublicKey publicKey,
|
||||||
String alias, PendingContactState state, long timestamp) {
|
String alias, PendingContactState state, long timestamp) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
@@ -27,7 +28,7 @@ public class PendingContact {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getPublicKey() {
|
public PublicKey getPublicKey() {
|
||||||
return publicKey;
|
return publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.api.contact;
|
|||||||
import org.briarproject.bramble.api.UniqueId;
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,9 +18,8 @@ public class PendingContactId extends UniqueId {
|
|||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(@Nullable Object o) {
|
||||||
return o instanceof PendingContactId && super.equals(o);
|
return o instanceof PendingContactId && super.equals(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,7 +55,7 @@ public interface CryptoComponent {
|
|||||||
* signature, to prevent it from being repurposed or colliding with a
|
* signature, to prevent it from being repurposed or colliding with a
|
||||||
* signature created for another purpose
|
* signature created for another purpose
|
||||||
*/
|
*/
|
||||||
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
|
||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,7 +68,7 @@ public interface CryptoComponent {
|
|||||||
* @return true if the signature was valid, false otherwise.
|
* @return true if the signature was valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean verifySignature(byte[] signature, String label, byte[] signed,
|
boolean verifySignature(byte[] signature, String label, byte[] signed,
|
||||||
byte[] publicKey) throws GeneralSecurityException;
|
PublicKey publicKey) throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the hash of the given inputs. The inputs are unambiguously
|
* Returns the hash of the given inputs. The inputs are unambiguously
|
||||||
|
|||||||
@@ -7,11 +7,21 @@ public interface CryptoConstants {
|
|||||||
*/
|
*/
|
||||||
int MAX_AGREEMENT_PUBLIC_KEY_BYTES = 32;
|
int MAX_AGREEMENT_PUBLIC_KEY_BYTES = 32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key type for agreement key pairs.
|
||||||
|
*/
|
||||||
|
String KEY_TYPE_AGREEMENT = "Curve25519";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum length of a signature public key in bytes.
|
* The maximum length of a signature public key in bytes.
|
||||||
*/
|
*/
|
||||||
int MAX_SIGNATURE_PUBLIC_KEY_BYTES = 32;
|
int MAX_SIGNATURE_PUBLIC_KEY_BYTES = 32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key type for signature key pairs.
|
||||||
|
*/
|
||||||
|
String KEY_TYPE_SIGNATURE = "Ed25519";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum length of a signature in bytes.
|
* The maximum length of a signature in bytes.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ public class KeyPair {
|
|||||||
private final PrivateKey privateKey;
|
private final PrivateKey privateKey;
|
||||||
|
|
||||||
public KeyPair(PublicKey publicKey, PrivateKey privateKey) {
|
public KeyPair(PublicKey publicKey, PrivateKey privateKey) {
|
||||||
|
if (!publicKey.getKeyType().equals(privateKey.getKeyType()))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
this.privateKey = privateKey;
|
this.privateKey = privateKey;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface PrivateKey {
|
public interface PrivateKey {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of this key pair.
|
||||||
|
*/
|
||||||
|
String getKeyType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the encoded representation of this key.
|
* Returns the encoded representation of this key.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface PublicKey {
|
public interface PublicKey {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of this key pair.
|
||||||
|
*/
|
||||||
|
String getKeyType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the encoded representation of this key.
|
* Returns the encoded representation of this key.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.crypto;
|
package org.briarproject.bramble.api.crypto;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,35 +10,29 @@ import org.briarproject.bramble.api.transport.TransportKeys;
|
|||||||
public interface TransportCrypto {
|
public interface TransportCrypto {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives initial transport keys for the given transport in the given
|
* Derives initial rotation mode transport keys for the given transport in
|
||||||
* time period from the given root key.
|
* 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
|
||||||
* @param active whether the keys are usable for outgoing streams.
|
* @param active Whether the keys are usable for outgoing streams
|
||||||
*/
|
*/
|
||||||
TransportKeys deriveTransportKeys(TransportId t, SecretKey rootKey,
|
TransportKeys deriveRotationKeys(TransportId t, SecretKey rootKey,
|
||||||
long timePeriod, boolean alice, boolean active);
|
long timePeriod, boolean alice, boolean active);
|
||||||
|
|
||||||
/**
|
|
||||||
* Rotates the given transport keys to the given time period. If the keys
|
|
||||||
* are for the given period or any later period they are not rotated.
|
|
||||||
*/
|
|
||||||
TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives handshake keys for the given transport in the given time period
|
* Derives handshake keys for the given transport in the given time period
|
||||||
* from the given root key.
|
* from the given root key.
|
||||||
*
|
*
|
||||||
* @param alice whether the keys are for use by Alice or Bob.
|
* @param alice Whether the keys are for use by Alice or Bob
|
||||||
*/
|
*/
|
||||||
HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
|
TransportKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
|
||||||
long timePeriod, boolean alice);
|
long timePeriod, boolean alice);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the given handshake keys to the given time period. If the keys
|
* Updates the given transport keys to the given time period. If the keys
|
||||||
* are for the given period or any later period they are not updated.
|
* are for the given period or any later period they are not updated.
|
||||||
*/
|
*/
|
||||||
HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod);
|
TransportKeys updateTransportKeys(TransportKeys k, long timePeriod);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes the pseudo-random tag that is used to recognise a stream.
|
* Encodes the pseudo-random tag that is used to recognise a stream.
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import org.briarproject.bramble.api.contact.Contact;
|
|||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
@@ -22,11 +24,8 @@ import org.briarproject.bramble.api.sync.MessageStatus;
|
|||||||
import org.briarproject.bramble.api.sync.Offer;
|
import org.briarproject.bramble.api.sync.Offer;
|
||||||
import org.briarproject.bramble.api.sync.Request;
|
import org.briarproject.bramble.api.sync.Request;
|
||||||
import org.briarproject.bramble.api.sync.validation.MessageState;
|
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -113,20 +112,6 @@ public interface DatabaseComponent {
|
|||||||
*/
|
*/
|
||||||
void addGroup(Transaction txn, Group g) throws DbException;
|
void addGroup(Transaction txn, Group g) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the given handshake keys for the given contact and returns a
|
|
||||||
* key set ID.
|
|
||||||
*/
|
|
||||||
HandshakeKeySetId addHandshakeKeys(Transaction txn, ContactId c,
|
|
||||||
HandshakeKeys k) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the given handshake keys for the given pending contact and
|
|
||||||
* returns a key set ID.
|
|
||||||
*/
|
|
||||||
HandshakeKeySetId addHandshakeKeys(Transaction txn, PendingContactId p,
|
|
||||||
HandshakeKeys k) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores an identity.
|
* Stores an identity.
|
||||||
*/
|
*/
|
||||||
@@ -154,7 +139,14 @@ public interface DatabaseComponent {
|
|||||||
* Stores the given transport keys for the given contact and returns a
|
* Stores the given transport keys for the given contact and returns a
|
||||||
* key set ID.
|
* key set ID.
|
||||||
*/
|
*/
|
||||||
TransportKeySetId addTransportKeys(Transaction txn, ContactId c,
|
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,
|
||||||
TransportKeys k) throws DbException;
|
TransportKeys k) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -274,7 +266,7 @@ public interface DatabaseComponent {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<ContactId> getContacts(Transaction txn, AuthorId a)
|
Collection<ContactId> getContacts(Transaction txn, AuthorId local)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -308,14 +300,6 @@ public interface DatabaseComponent {
|
|||||||
Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g)
|
Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all handshake keys for the given transport.
|
|
||||||
* <p/>
|
|
||||||
* Read-only.
|
|
||||||
*/
|
|
||||||
Collection<HandshakeKeySet> getHandshakeKeys(Transaction txn, TransportId t)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the identity for the local pseudonym with the given ID.
|
* Returns the identity for the local pseudonym with the given ID.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -487,17 +471,11 @@ public interface DatabaseComponent {
|
|||||||
Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t)
|
Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Increments the outgoing stream counter for the given handshake keys.
|
|
||||||
*/
|
|
||||||
void incrementStreamCounter(Transaction txn, TransportId t,
|
|
||||||
HandshakeKeySetId k) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increments the outgoing stream counter for the given transport keys.
|
* Increments the outgoing stream counter for the given transport keys.
|
||||||
*/
|
*/
|
||||||
void incrementStreamCounter(Transaction txn, TransportId t,
|
void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k)
|
||||||
TransportKeySetId k) throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the given metadata with the existing metadata for the given
|
* Merges the given metadata with the existing metadata for the given
|
||||||
@@ -552,12 +530,6 @@ public interface DatabaseComponent {
|
|||||||
*/
|
*/
|
||||||
void removeGroup(Transaction txn, Group g) throws DbException;
|
void removeGroup(Transaction txn, Group g) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given handshake keys from the database.
|
|
||||||
*/
|
|
||||||
void removeHandshakeKeys(Transaction txn, TransportId t,
|
|
||||||
HandshakeKeySetId k) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an identity (and all associated state) from the database.
|
* Removes an identity (and all associated state) from the database.
|
||||||
*/
|
*/
|
||||||
@@ -582,8 +554,8 @@ public interface DatabaseComponent {
|
|||||||
/**
|
/**
|
||||||
* Removes the given transport keys from the database.
|
* Removes the given transport keys from the database.
|
||||||
*/
|
*/
|
||||||
void removeTransportKeys(Transaction txn, TransportId t,
|
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
|
||||||
TransportKeySetId k) throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given contact as verified.
|
* Marks the given contact as verified.
|
||||||
@@ -622,35 +594,20 @@ public interface DatabaseComponent {
|
|||||||
/**
|
/**
|
||||||
* Sets the handshake key pair for the identity with the given ID.
|
* Sets the handshake key pair for the identity with the given ID.
|
||||||
*/
|
*/
|
||||||
void setHandshakeKeyPair(Transaction txn, AuthorId local, byte[] publicKey,
|
void setHandshakeKeyPair(Transaction txn, AuthorId local,
|
||||||
byte[] privateKey) throws DbException;
|
PublicKey publicKey, PrivateKey privateKey) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the reordering window for the given transport key set in the given
|
* Sets the reordering window for the given transport keys in the given
|
||||||
* time period.
|
* time period.
|
||||||
*/
|
*/
|
||||||
void setReorderingWindow(Transaction txn, TransportKeySetId k,
|
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t,
|
||||||
TransportId t, long timePeriod, long base, byte[] bitmap)
|
long timePeriod, long base, byte[] bitmap) throws DbException;
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the reordering window for the given handshake key set in the given
|
|
||||||
* time period.
|
|
||||||
*/
|
|
||||||
void setReorderingWindow(Transaction txn, HandshakeKeySetId k,
|
|
||||||
TransportId t, long timePeriod, long base, byte[] bitmap)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given transport keys as usable for outgoing streams.
|
* Marks the given transport keys as usable for outgoing streams.
|
||||||
*/
|
*/
|
||||||
void setTransportKeysActive(Transaction txn, TransportId t,
|
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
|
||||||
TransportKeySetId k) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the given handshake keys, deleting any keys they have replaced.
|
|
||||||
*/
|
|
||||||
void updateHandshakeKeys(Transaction txn, Collection<HandshakeKeySet> keys)
|
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package org.briarproject.bramble.api.identity;
|
package org.briarproject.bramble.api.identity;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Nameable;
|
import org.briarproject.bramble.api.Nameable;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pseudonym for a user.
|
* A pseudonym for a user.
|
||||||
@@ -24,14 +25,14 @@ public class Author implements Nameable {
|
|||||||
private final AuthorId id;
|
private final AuthorId id;
|
||||||
private final int formatVersion;
|
private final int formatVersion;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final byte[] publicKey;
|
private final PublicKey publicKey;
|
||||||
|
|
||||||
public Author(AuthorId id, int formatVersion, String name,
|
public Author(AuthorId id, int formatVersion, String name,
|
||||||
byte[] publicKey) {
|
PublicKey publicKey) {
|
||||||
int nameLength = StringUtils.toUtf8(name).length;
|
int nameLength = toUtf8(name).length;
|
||||||
if (nameLength == 0 || nameLength > MAX_AUTHOR_NAME_LENGTH)
|
if (nameLength == 0 || nameLength > MAX_AUTHOR_NAME_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (publicKey.length == 0 || publicKey.length > MAX_PUBLIC_KEY_LENGTH)
|
if (!publicKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.formatVersion = formatVersion;
|
this.formatVersion = formatVersion;
|
||||||
@@ -63,7 +64,7 @@ public class Author implements Nameable {
|
|||||||
/**
|
/**
|
||||||
* Returns the public key used to verify the pseudonym's signatures.
|
* Returns the public key used to verify the pseudonym's signatures.
|
||||||
*/
|
*/
|
||||||
public byte[] getPublicKey() {
|
public PublicKey getPublicKey() {
|
||||||
return publicKey;
|
return publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.identity;
|
package org.briarproject.bramble.api.identity;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -9,12 +10,12 @@ public interface AuthorFactory {
|
|||||||
* Creates an author with the current format version and the given name and
|
* Creates an author with the current format version and the given name and
|
||||||
* public key.
|
* public key.
|
||||||
*/
|
*/
|
||||||
Author createAuthor(String name, byte[] publicKey);
|
Author createAuthor(String name, PublicKey publicKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an author with the given format version, name and public key.
|
* Creates an author with the given format version, name and public key.
|
||||||
*/
|
*/
|
||||||
Author createAuthor(int formatVersion, String name, byte[] publicKey);
|
Author createAuthor(int formatVersion, String name, PublicKey publicKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a local author with the current format version and the given
|
* Creates a local author with the current format version and the given
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package org.briarproject.bramble.api.identity;
|
package org.briarproject.bramble.api.identity;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -15,15 +15,24 @@ public class Identity {
|
|||||||
|
|
||||||
private final LocalAuthor localAuthor;
|
private final LocalAuthor localAuthor;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final byte[] handshakePublicKey, handshakePrivateKey;
|
private final PublicKey handshakePublicKey;
|
||||||
|
@Nullable
|
||||||
|
private final PrivateKey handshakePrivateKey;
|
||||||
private final long created;
|
private final long created;
|
||||||
|
|
||||||
public Identity(LocalAuthor localAuthor,
|
public Identity(LocalAuthor localAuthor,
|
||||||
@Nullable byte[] handshakePublicKey,
|
@Nullable PublicKey handshakePublicKey,
|
||||||
@Nullable byte[] handshakePrivateKey, long created) {
|
@Nullable PrivateKey handshakePrivateKey, long created) {
|
||||||
if (handshakePublicKey != null) {
|
if (handshakePublicKey != null) {
|
||||||
int keyLength = handshakePublicKey.length;
|
if (handshakePrivateKey == null)
|
||||||
if (keyLength == 0 || keyLength > MAX_AGREEMENT_PUBLIC_KEY_BYTES)
|
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))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
this.localAuthor = localAuthor;
|
this.localAuthor = localAuthor;
|
||||||
@@ -57,7 +66,7 @@ public class Identity {
|
|||||||
* Returns the public key used for handshaking, or null if no key exists.
|
* Returns the public key used for handshaking, or null if no key exists.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public byte[] getHandshakePublicKey() {
|
public PublicKey getHandshakePublicKey() {
|
||||||
return handshakePublicKey;
|
return handshakePublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +74,7 @@ public class Identity {
|
|||||||
* Returns the private key used for handshaking, or null if no key exists.
|
* Returns the private key used for handshaking, or null if no key exists.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public byte[] getHandshakePrivateKey() {
|
public PrivateKey getHandshakePrivateKey() {
|
||||||
return handshakePrivateKey;
|
return handshakePrivateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,21 +85,4 @@ public class Identity {
|
|||||||
public long getTimeCreated() {
|
public long getTimeCreated() {
|
||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return localAuthor.getId().hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (o instanceof Identity) {
|
|
||||||
Identity i = (Identity) o;
|
|
||||||
return created == i.created &&
|
|
||||||
localAuthor.equals(i.localAuthor) &&
|
|
||||||
Arrays.equals(handshakePublicKey, i.handshakePublicKey) &&
|
|
||||||
Arrays.equals(handshakePrivateKey, i.handshakePrivateKey);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.api.identity;
|
package org.briarproject.bramble.api.identity;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
@@ -41,9 +42,6 @@ public interface IdentityManager {
|
|||||||
* Returns the cached handshake keys or loads them from the database.
|
* Returns the cached handshake keys or loads them from the database.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*
|
|
||||||
* @return A two-element array containing the public key in the first
|
|
||||||
* element and the private key in the second
|
|
||||||
*/
|
*/
|
||||||
byte[][] getHandshakeKeys(Transaction txn) throws DbException;
|
KeyPair getHandshakeKeys(Transaction txn) throws DbException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package org.briarproject.bramble.api.identity;
|
package org.briarproject.bramble.api.identity;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pseudonym for the local user.
|
* A pseudonym for the local user.
|
||||||
*/
|
*/
|
||||||
@@ -11,18 +15,20 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class LocalAuthor extends Author {
|
public class LocalAuthor extends Author {
|
||||||
|
|
||||||
private final byte[] privateKey;
|
private final PrivateKey privateKey;
|
||||||
|
|
||||||
public LocalAuthor(AuthorId id, int formatVersion, String name,
|
public LocalAuthor(AuthorId id, int formatVersion, String name,
|
||||||
byte[] publicKey, byte[] privateKey) {
|
PublicKey publicKey, PrivateKey privateKey) {
|
||||||
super(id, formatVersion, name, publicKey);
|
super(id, formatVersion, name, publicKey);
|
||||||
|
if (!privateKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
this.privateKey = privateKey;
|
this.privateKey = privateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the private key used to generate the pseudonym's signatures.
|
* Returns the private key used to generate the pseudonym's signatures.
|
||||||
*/
|
*/
|
||||||
public byte[] getPrivateKey() {
|
public PrivateKey getPrivateKey() {
|
||||||
return privateKey;
|
return privateKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,20 @@ import javax.annotation.Nullable;
|
|||||||
public class NullSafety {
|
public class NullSafety {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stand-in for `Objects.requireNonNull()`.
|
* Stand-in for {@code Objects.requireNonNull()}.
|
||||||
*/
|
*/
|
||||||
public static <T> T requireNonNull(@Nullable T t) {
|
public static <T> T requireNonNull(@Nullable T t) {
|
||||||
if (t == null) throw new NullPointerException();
|
if (t == null) throw new NullPointerException();
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that exactly one of the arguments is null.
|
||||||
|
*
|
||||||
|
* @throws AssertionError If both or neither of the arguments are null
|
||||||
|
*/
|
||||||
|
public static void requireExactlyOneNull(@Nullable Object a,
|
||||||
|
@Nullable Object b) {
|
||||||
|
if ((a == null) == (b == null)) throw new AssertionError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,8 +8,8 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains transport keys for receiving streams from a given contact over a
|
* Contains transport keys for receiving streams from a given contact or
|
||||||
* given transport in a given time period.
|
* pending contact over a given transport in a given time period.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.api.transport;
|
package org.briarproject.bramble.api.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
@@ -18,23 +19,51 @@ public interface KeyManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Informs the key manager that a new contact has been added. Derives and
|
* Informs the key manager that a new contact has been added. Derives and
|
||||||
* stores a set of transport keys for communicating with the contact over
|
* stores a set of rotation mode transport keys for communicating with the
|
||||||
* each transport and returns the key set IDs.
|
* contact over each transport and returns the key set IDs.
|
||||||
* <p/>
|
* <p/>
|
||||||
* {@link StreamContext StreamContexts} for the contact can be created
|
* {@link StreamContext StreamContexts} for the contact can be created
|
||||||
* after this method has returned.
|
* after this method has returned.
|
||||||
*
|
*
|
||||||
* @param alice true if the local party is Alice
|
* @param alice True if the local party is Alice
|
||||||
* @param active whether the derived keys can be used for outgoing streams
|
* @param active Whether the derived keys can be used for outgoing streams
|
||||||
*/
|
*/
|
||||||
Map<TransportId, TransportKeySetId> addContact(Transaction txn, ContactId c,
|
Map<TransportId, KeySetId> addContactWithRotationKeys(Transaction txn,
|
||||||
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
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)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given transport keys as usable for outgoing streams.
|
* Marks the given transport keys as usable for outgoing streams.
|
||||||
*/
|
*/
|
||||||
void activateKeys(Transaction txn, Map<TransportId, TransportKeySetId> keys)
|
void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,15 +72,28 @@ public interface KeyManager {
|
|||||||
*/
|
*/
|
||||||
boolean canSendOutgoingStreams(ContactId c, TransportId t);
|
boolean canSendOutgoingStreams(ContactId c, TransportId t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if we have keys that can be used for outgoing streams to
|
||||||
|
* the given pending contact over the given transport.
|
||||||
|
*/
|
||||||
|
boolean canSendOutgoingStreams(PendingContactId p, TransportId t);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link StreamContext} for sending a stream to the given
|
* Returns a {@link StreamContext} for sending a stream to the given
|
||||||
* contact over the given transport, or null if an error occurs or the
|
* contact over the given transport, or null if an error occurs.
|
||||||
* contact does not support the transport.
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
StreamContext getStreamContext(ContactId c, TransportId t)
|
StreamContext getStreamContext(ContactId c, TransportId t)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link StreamContext} for sending a stream to the given
|
||||||
|
* pending contact over the given transport, or null if an error occurs.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
StreamContext getStreamContext(PendingContactId p, TransportId t)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up the given tag and returns a {@link StreamContext} for reading
|
* Looks up the given tag and returns a {@link StreamContext} for reading
|
||||||
* from the corresponding stream, or null if an error occurs or the tag was
|
* from the corresponding stream, or null if an error occurs or the tag was
|
||||||
|
|||||||
@@ -5,17 +5,16 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type-safe wrapper for an integer that uniquely identifies a
|
* Type-safe wrapper for an integer that uniquely identifies a set of
|
||||||
* {@link HandshakeKeySet set of handshake keys} within the scope of the local
|
* {@link TransportKeySet transport keys} within the scope of the local device.
|
||||||
* device.
|
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class HandshakeKeySetId {
|
public class KeySetId {
|
||||||
|
|
||||||
private final int id;
|
private final int id;
|
||||||
|
|
||||||
public HandshakeKeySetId(int id) {
|
public KeySetId(int id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +29,6 @@ public class HandshakeKeySetId {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
return o instanceof HandshakeKeySetId &&
|
return o instanceof KeySetId && id == ((KeySetId) o).id;
|
||||||
id == ((HandshakeKeySetId) o).id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,8 +6,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains transport keys for sending streams to a given contact over a given
|
* Contains transport keys for sending streams to a given contact or pending
|
||||||
* transport in a given time period.
|
* contact over a given transport in a given time period.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
|
|||||||
@@ -1,34 +1,53 @@
|
|||||||
package org.briarproject.bramble.api.transport;
|
package org.briarproject.bramble.api.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class StreamContext {
|
public class StreamContext {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
|
@Nullable
|
||||||
|
private final PendingContactId pendingContactId;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final SecretKey tagKey, headerKey;
|
private final SecretKey tagKey, headerKey;
|
||||||
private final long streamNumber;
|
private final long streamNumber;
|
||||||
|
private final boolean handshakeMode;
|
||||||
|
|
||||||
public StreamContext(ContactId contactId, TransportId transportId,
|
public StreamContext(@Nullable ContactId contactId,
|
||||||
SecretKey tagKey, SecretKey headerKey, long streamNumber) {
|
@Nullable PendingContactId pendingContactId,
|
||||||
|
TransportId transportId, SecretKey tagKey, SecretKey headerKey,
|
||||||
|
long streamNumber, boolean handshakeMode) {
|
||||||
|
requireExactlyOneNull(contactId, pendingContactId);
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
|
this.pendingContactId = pendingContactId;
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
this.tagKey = tagKey;
|
this.tagKey = tagKey;
|
||||||
this.headerKey = headerKey;
|
this.headerKey = headerKey;
|
||||||
this.streamNumber = streamNumber;
|
this.streamNumber = streamNumber;
|
||||||
|
this.handshakeMode = handshakeMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public ContactId getContactId() {
|
public ContactId getContactId() {
|
||||||
return contactId;
|
return contactId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public PendingContactId getPendingContactId() {
|
||||||
|
return pendingContactId;
|
||||||
|
}
|
||||||
|
|
||||||
public TransportId getTransportId() {
|
public TransportId getTransportId() {
|
||||||
return transportId;
|
return transportId;
|
||||||
}
|
}
|
||||||
@@ -44,4 +63,8 @@ public class StreamContext {
|
|||||||
public long getStreamNumber() {
|
public long getStreamNumber() {
|
||||||
return streamNumber;
|
return streamNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHandshakeMode() {
|
||||||
|
return handshakeMode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,52 @@
|
|||||||
package org.briarproject.bramble.api.transport;
|
package org.briarproject.bramble.api.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of keys for communicating with a given contact over a given transport.
|
* A set of keys for communicating with a given contact or pending contact
|
||||||
* Unlike a {@link HandshakeKeySet} these keys provide forward secrecy.
|
* over a given transport.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class TransportKeySet {
|
public class TransportKeySet {
|
||||||
|
|
||||||
private final TransportKeySetId keySetId;
|
private final KeySetId keySetId;
|
||||||
|
@Nullable
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
|
@Nullable
|
||||||
|
private final PendingContactId pendingContactId;
|
||||||
private final TransportKeys keys;
|
private final TransportKeys keys;
|
||||||
|
|
||||||
public TransportKeySet(TransportKeySetId keySetId, ContactId contactId,
|
public TransportKeySet(KeySetId keySetId, @Nullable ContactId contactId,
|
||||||
TransportKeys keys) {
|
@Nullable PendingContactId pendingContactId, TransportKeys keys) {
|
||||||
|
requireExactlyOneNull(contactId, pendingContactId);
|
||||||
this.keySetId = keySetId;
|
this.keySetId = keySetId;
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
|
this.pendingContactId = pendingContactId;
|
||||||
this.keys = keys;
|
this.keys = keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransportKeySetId getKeySetId() {
|
public KeySetId getKeySetId() {
|
||||||
return keySetId;
|
return keySetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public ContactId getContactId() {
|
public ContactId getContactId() {
|
||||||
return contactId;
|
return contactId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public PendingContactId getPendingContactId() {
|
||||||
|
return pendingContactId;
|
||||||
|
}
|
||||||
|
|
||||||
public TransportKeys getKeys() {
|
public TransportKeys getKeys() {
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +1,108 @@
|
|||||||
package org.briarproject.bramble.api.transport;
|
package org.briarproject.bramble.api.transport;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keys for communicating with a given contact over a given transport. Unlike
|
* Keys for communicating with a given contact or pending contact over a given
|
||||||
* {@link HandshakeKeys} these keys provide forward secrecy.
|
* transport.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class TransportKeys extends AbstractTransportKeys {
|
public class TransportKeys {
|
||||||
|
|
||||||
|
private final TransportId transportId;
|
||||||
|
private final IncomingKeys inPrev, inCurr, inNext;
|
||||||
|
private final OutgoingKeys outCurr;
|
||||||
|
@Nullable
|
||||||
|
private final SecretKey rootKey;
|
||||||
|
private final boolean alice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for rotation mode.
|
||||||
|
*/
|
||||||
public TransportKeys(TransportId transportId, IncomingKeys inPrev,
|
public TransportKeys(TransportId transportId, IncomingKeys inPrev,
|
||||||
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
|
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
|
||||||
super(transportId, inPrev, inCurr, inNext, 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.util;
|
package org.briarproject.bramble.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ public class LogUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the duration of a task.
|
* Logs the duration of a task.
|
||||||
|
*
|
||||||
* @param logger the logger to use
|
* @param logger the logger to use
|
||||||
* @param task a description of the task
|
* @param task a description of the task
|
||||||
* @param start the start time of the task, as returned by {@link #now()}
|
* @param start the start time of the task, as returned by {@link #now()}
|
||||||
@@ -33,4 +35,26 @@ public class LogUtils {
|
|||||||
public static void logException(Logger logger, Level level, Throwable t) {
|
public static void logException(Logger logger, Level level, Throwable t) {
|
||||||
if (logger.isLoggable(level)) logger.log(level, t.toString(), t);
|
if (logger.isLoggable(level)) logger.log(level, t.toString(), t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void logFileOrDir(Logger logger, Level level, File f) {
|
||||||
|
if (logger.isLoggable(level)) {
|
||||||
|
if (f.isFile()) {
|
||||||
|
logWithType(logger, level, f, "F");
|
||||||
|
} else if (f.isDirectory()) {
|
||||||
|
logWithType(logger, level, f, "D");
|
||||||
|
File[] children = f.listFiles();
|
||||||
|
if (children != null) {
|
||||||
|
for (File child : children)
|
||||||
|
logFileOrDir(logger, level, child);
|
||||||
|
}
|
||||||
|
} else if (f.exists()) {
|
||||||
|
logWithType(logger, level, f, "?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logWithType(Logger logger, Level level, File f,
|
||||||
|
String type) {
|
||||||
|
logger.log(level, type + " " + f.getAbsolutePath() + " " + f.length());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,14 @@ import org.briarproject.bramble.api.contact.Contact;
|
|||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactState;
|
||||||
|
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.Identity;
|
import org.briarproject.bramble.api.identity.Identity;
|
||||||
@@ -30,11 +37,10 @@ import java.util.Random;
|
|||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
|
||||||
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
|
||||||
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
|
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
|
||||||
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
|
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
|
||||||
@@ -101,10 +107,28 @@ public class TestUtils {
|
|||||||
return new SecretKey(getRandomBytes(SecretKey.LENGTH));
|
return new SecretKey(getRandomBytes(SecretKey.LENGTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PublicKey getSignaturePublicKey() {
|
||||||
|
byte[] key = getRandomBytes(MAX_SIGNATURE_PUBLIC_KEY_BYTES);
|
||||||
|
return new SignaturePublicKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrivateKey getSignaturePrivateKey() {
|
||||||
|
return new SignaturePrivateKey(getRandomBytes(123));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicKey getAgreementPublicKey() {
|
||||||
|
byte[] key = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
|
||||||
|
return new AgreementPublicKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrivateKey getAgreementPrivateKey() {
|
||||||
|
return new AgreementPrivateKey(getRandomBytes(123));
|
||||||
|
}
|
||||||
|
|
||||||
public static Identity getIdentity() {
|
public static Identity getIdentity() {
|
||||||
LocalAuthor localAuthor = getLocalAuthor();
|
LocalAuthor localAuthor = getLocalAuthor();
|
||||||
byte[] handshakePub = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
|
PublicKey handshakePub = getAgreementPublicKey();
|
||||||
byte[] handshakePriv = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
|
PrivateKey handshakePriv = getAgreementPrivateKey();
|
||||||
return new Identity(localAuthor, handshakePub, handshakePriv,
|
return new Identity(localAuthor, handshakePub, handshakePriv,
|
||||||
timestamp);
|
timestamp);
|
||||||
}
|
}
|
||||||
@@ -113,8 +137,8 @@ public class TestUtils {
|
|||||||
AuthorId id = new AuthorId(getRandomId());
|
AuthorId id = new AuthorId(getRandomId());
|
||||||
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
|
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
|
||||||
String name = getRandomString(nameLength);
|
String name = getRandomString(nameLength);
|
||||||
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
PublicKey publicKey = getSignaturePublicKey();
|
||||||
byte[] privateKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
PrivateKey privateKey = getSignaturePrivateKey();
|
||||||
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
|
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +146,7 @@ public class TestUtils {
|
|||||||
AuthorId id = new AuthorId(getRandomId());
|
AuthorId id = new AuthorId(getRandomId());
|
||||||
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
|
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
|
||||||
String name = getRandomString(nameLength);
|
String name = getRandomString(nameLength);
|
||||||
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
PublicKey publicKey = getSignaturePublicKey();
|
||||||
return new Author(id, FORMAT_VERSION, name, publicKey);
|
return new Author(id, FORMAT_VERSION, name, publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,10 +179,12 @@ public class TestUtils {
|
|||||||
|
|
||||||
public static PendingContact getPendingContact(int nameLength) {
|
public static PendingContact getPendingContact(int nameLength) {
|
||||||
PendingContactId id = new PendingContactId(getRandomId());
|
PendingContactId id = new PendingContactId(getRandomId());
|
||||||
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
PublicKey publicKey = getAgreementPublicKey();
|
||||||
String alias = getRandomString(nameLength);
|
String alias = getRandomString(nameLength);
|
||||||
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
|
int stateIndex =
|
||||||
timestamp);
|
random.nextInt(PendingContactState.values().length - 1);
|
||||||
|
PendingContactState state = PendingContactState.values()[stateIndex];
|
||||||
|
return new PendingContact(id, publicKey, alias, state, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ContactId getContactId() {
|
public static ContactId getContactId() {
|
||||||
@@ -179,7 +205,7 @@ public class TestUtils {
|
|||||||
boolean verified) {
|
boolean verified) {
|
||||||
return new Contact(c, remote, local,
|
return new Contact(c, remote, local,
|
||||||
getRandomString(MAX_AUTHOR_NAME_LENGTH),
|
getRandomString(MAX_AUTHOR_NAME_LENGTH),
|
||||||
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), verified);
|
getAgreementPublicKey(), verified);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double getMedian(Collection<? extends Number> samples) {
|
public static double getMedian(Collection<? extends Number> samples) {
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ package org.briarproject.bramble.client;
|
|||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.data.BdfReader;
|
import org.briarproject.bramble.api.data.BdfReader;
|
||||||
@@ -305,14 +308,15 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] sign(String label, BdfList toSign, byte[] privateKey)
|
public byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
|
||||||
throws FormatException, GeneralSecurityException {
|
throws FormatException, GeneralSecurityException {
|
||||||
return crypto.sign(label, toByteArray(toSign), privateKey);
|
return crypto.sign(label, toByteArray(toSign), privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void verifySignature(byte[] signature, String label, BdfList signed,
|
public void verifySignature(byte[] signature, String label, BdfList signed,
|
||||||
byte[] publicKey) throws FormatException, GeneralSecurityException {
|
PublicKey publicKey)
|
||||||
|
throws FormatException, GeneralSecurityException {
|
||||||
if (!crypto.verifySignature(signature, label, toByteArray(signed),
|
if (!crypto.verifySignature(signature, label, toByteArray(signed),
|
||||||
publicKey)) {
|
publicKey)) {
|
||||||
throw new GeneralSecurityException("Invalid signature");
|
throw new GeneralSecurityException("Invalid signature");
|
||||||
@@ -327,11 +331,29 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
if (formatVersion != FORMAT_VERSION) throw new FormatException();
|
if (formatVersion != FORMAT_VERSION) throw new FormatException();
|
||||||
String name = author.getString(1);
|
String name = author.getString(1);
|
||||||
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
|
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
|
||||||
byte[] publicKey = author.getRaw(2);
|
byte[] publicKeyBytes = author.getRaw(2);
|
||||||
checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH);
|
checkLength(publicKeyBytes, 1, MAX_PUBLIC_KEY_LENGTH);
|
||||||
|
KeyParser parser = crypto.getSignatureKeyParser();
|
||||||
|
PublicKey publicKey;
|
||||||
|
try {
|
||||||
|
publicKey = parser.parsePublicKey(publicKeyBytes);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
return authorFactory.createAuthor(formatVersion, name, publicKey);
|
return authorFactory.createAuthor(formatVersion, name, publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey parseAndValidateAgreementPublicKey(byte[] publicKeyBytes)
|
||||||
|
throws FormatException {
|
||||||
|
KeyParser parser = crypto.getAgreementKeyParser();
|
||||||
|
try {
|
||||||
|
return parser.parsePublicKey(publicKeyBytes);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportProperties parseAndValidateTransportProperties(
|
public TransportProperties parseAndValidateTransportProperties(
|
||||||
BdfDictionary properties) throws FormatException {
|
BdfDictionary properties) throws FormatException {
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
SecretKey rootKey, long timestamp, boolean alice, boolean verified,
|
SecretKey rootKey, long timestamp, boolean alice, boolean verified,
|
||||||
boolean active) throws DbException {
|
boolean active) throws DbException {
|
||||||
ContactId c = db.addContact(txn, remote, local, verified);
|
ContactId c = db.addContact(txn, remote, local, verified);
|
||||||
keyManager.addContact(txn, c, rootKey, timestamp, alice, active);
|
keyManager.addContactWithRotationKeys(txn, c, rootKey, timestamp,
|
||||||
|
alice, active);
|
||||||
Contact contact = db.getContact(txn, c);
|
Contact contact = db.getContact(txn, c);
|
||||||
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
|
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
|
||||||
return c;
|
return c;
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ class PendingContactFactoryImpl implements PendingContactFactory {
|
|||||||
PublicKey publicKey = parseHandshakeLink(link);
|
PublicKey publicKey = parseHandshakeLink(link);
|
||||||
PendingContactId id = getPendingContactId(publicKey);
|
PendingContactId id = getPendingContactId(publicKey);
|
||||||
long timestamp = clock.currentTimeMillis();
|
long timestamp = clock.currentTimeMillis();
|
||||||
return new PendingContact(id, publicKey.getEncoded(), alias,
|
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
|
||||||
WAITING_FOR_CONNECTION, timestamp);
|
timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PublicKey parseHandshakeLink(String link) throws FormatException {
|
private PublicKey parseHandshakeLink(String link) throws FormatException {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
@@ -7,21 +9,24 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class Curve25519KeyParser implements KeyParser {
|
class AgreementKeyParser implements KeyParser {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicKey parsePublicKey(byte[] encodedKey)
|
public PublicKey parsePublicKey(byte[] encodedKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||||
return new Curve25519PublicKey(encodedKey);
|
return new AgreementPublicKey(encodedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||||
return new Curve25519PrivateKey(clamp(encodedKey));
|
return new AgreementPrivateKey(clamp(encodedKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] clamp(byte[] b) {
|
static byte[] clamp(byte[] b) {
|
||||||
@@ -4,12 +4,16 @@ import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
|||||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||||
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
@@ -31,6 +35,8 @@ import javax.annotation.Nullable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
@@ -80,8 +86,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
signatureKeyPairGenerator = new KeyPairGenerator();
|
signatureKeyPairGenerator = new KeyPairGenerator();
|
||||||
signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS,
|
signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS,
|
||||||
secureRandom);
|
secureRandom);
|
||||||
agreementKeyParser = new Curve25519KeyParser();
|
agreementKeyParser = new AgreementKeyParser();
|
||||||
signatureKeyParser = new EdKeyParser();
|
signatureKeyParser = new SignatureKeyParser();
|
||||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
messageEncrypter = new MessageEncrypter(secureRandom);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,9 +131,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
// Package access for testing
|
// Package access for testing
|
||||||
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
|
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
if (!(priv instanceof Curve25519PrivateKey))
|
if (!priv.getKeyType().equals(KEY_TYPE_AGREEMENT))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (!(pub instanceof Curve25519PublicKey))
|
if (!pub.getKeyType().equals(KEY_TYPE_AGREEMENT))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
long start = now();
|
long start = now();
|
||||||
byte[] secret = curve25519.calculateAgreement(pub.getEncoded(),
|
byte[] secret = curve25519.calculateAgreement(pub.getEncoded(),
|
||||||
@@ -143,8 +149,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
@Override
|
@Override
|
||||||
public KeyPair generateAgreementKeyPair() {
|
public KeyPair generateAgreementKeyPair() {
|
||||||
Curve25519KeyPair keyPair = curve25519.generateKeyPair();
|
Curve25519KeyPair keyPair = curve25519.generateKeyPair();
|
||||||
PublicKey pub = new Curve25519PublicKey(keyPair.getPublicKey());
|
PublicKey pub = new AgreementPublicKey(keyPair.getPublicKey());
|
||||||
PrivateKey priv = new Curve25519PrivateKey(keyPair.getPrivateKey());
|
PrivateKey priv = new AgreementPrivateKey(keyPair.getPrivateKey());
|
||||||
return new KeyPair(pub, priv);
|
return new KeyPair(pub, priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,9 +164,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
java.security.KeyPair keyPair =
|
java.security.KeyPair keyPair =
|
||||||
signatureKeyPairGenerator.generateKeyPair();
|
signatureKeyPairGenerator.generateKeyPair();
|
||||||
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
|
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
|
||||||
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
|
PublicKey publicKey = new SignaturePublicKey(edPublicKey.getAbyte());
|
||||||
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
|
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
|
||||||
PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed());
|
PrivateKey privateKey = new SignaturePrivateKey(edPrivateKey.getSeed());
|
||||||
return new KeyPair(publicKey, privateKey);
|
return new KeyPair(publicKey, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,21 +201,22 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
public byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
PrivateKey key = signatureKeyParser.parsePrivateKey(privateKey);
|
|
||||||
Signature sig = new EdSignature();
|
Signature sig = new EdSignature();
|
||||||
sig.initSign(key);
|
sig.initSign(privateKey);
|
||||||
updateSignature(sig, label, toSign);
|
updateSignature(sig, label, toSign);
|
||||||
return sig.sign();
|
return sig.sign();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verifySignature(byte[] signature, String label,
|
public boolean verifySignature(byte[] signature, String label,
|
||||||
byte[] signed, byte[] publicKey) throws GeneralSecurityException {
|
byte[] signed, PublicKey publicKey)
|
||||||
PublicKey key = signatureKeyParser.parsePublicKey(publicKey);
|
throws GeneralSecurityException {
|
||||||
|
if (!publicKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
Signature sig = new EdSignature();
|
Signature sig = new EdSignature();
|
||||||
sig.initVerify(key);
|
sig.initVerify(publicKey);
|
||||||
updateSignature(sig, label, signed);
|
updateSignature(sig, label, signed);
|
||||||
return sig.verify(signature);
|
return sig.verify(signature);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,7 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
|
|
||||||
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
|
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class EdSignature implements Signature {
|
class EdSignature implements Signature {
|
||||||
@@ -39,7 +40,7 @@ class EdSignature implements Signature {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initSign(PrivateKey k) throws GeneralSecurityException {
|
public void initSign(PrivateKey k) throws GeneralSecurityException {
|
||||||
if (!(k instanceof EdPrivateKey))
|
if (!k.getKeyType().equals(KEY_TYPE_SIGNATURE))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
EdDSAPrivateKey privateKey = new EdDSAPrivateKey(
|
EdDSAPrivateKey privateKey = new EdDSAPrivateKey(
|
||||||
new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC));
|
new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC));
|
||||||
@@ -48,7 +49,7 @@ class EdSignature implements Signature {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initVerify(PublicKey k) throws GeneralSecurityException {
|
public void initVerify(PublicKey k) throws GeneralSecurityException {
|
||||||
if (!(k instanceof EdPublicKey))
|
if (!k.getKeyType().equals(KEY_TYPE_SIGNATURE))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
EdDSAPublicKey publicKey = new EdDSAPublicKey(
|
EdDSAPublicKey publicKey = new EdDSAPublicKey(
|
||||||
new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC));
|
new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC));
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class MessageEncrypter {
|
public class MessageEncrypter {
|
||||||
|
|
||||||
|
private static final String KEY_TYPE = "SEC1_brainpoolp512r1";
|
||||||
private static final ECDomainParameters PARAMETERS;
|
private static final ECDomainParameters PARAMETERS;
|
||||||
private static final int MESSAGE_KEY_BITS = 512;
|
private static final int MESSAGE_KEY_BITS = 512;
|
||||||
private static final int MAC_KEY_BITS = 256;
|
private static final int MAC_KEY_BITS = 256;
|
||||||
@@ -69,7 +70,7 @@ public class MessageEncrypter {
|
|||||||
MessageEncrypter(SecureRandom random) {
|
MessageEncrypter(SecureRandom random) {
|
||||||
generator = new ECKeyPairGenerator();
|
generator = new ECKeyPairGenerator();
|
||||||
generator.init(new ECKeyGenerationParameters(PARAMETERS, random));
|
generator.init(new ECKeyGenerationParameters(PARAMETERS, random));
|
||||||
parser = new Sec1KeyParser(PARAMETERS, MESSAGE_KEY_BITS);
|
parser = new Sec1KeyParser(KEY_TYPE, PARAMETERS, MESSAGE_KEY_BITS);
|
||||||
KeyEncoder encoder = new PublicKeyEncoder();
|
KeyEncoder encoder = new PublicKeyEncoder();
|
||||||
ephemeralGenerator = new EphemeralKeyPairGenerator(generator, encoder);
|
ephemeralGenerator = new EphemeralKeyPairGenerator(generator, encoder);
|
||||||
ephemeralParser = new PublicKeyParser(PARAMETERS);
|
ephemeralParser = new PublicKeyParser(PARAMETERS);
|
||||||
@@ -80,11 +81,11 @@ public class MessageEncrypter {
|
|||||||
// Return a wrapper that uses the SEC 1 encoding
|
// Return a wrapper that uses the SEC 1 encoding
|
||||||
ECPublicKeyParameters ecPublicKey =
|
ECPublicKeyParameters ecPublicKey =
|
||||||
(ECPublicKeyParameters) keyPair.getPublic();
|
(ECPublicKeyParameters) keyPair.getPublic();
|
||||||
PublicKey publicKey = new Sec1PublicKey(ecPublicKey);
|
PublicKey publicKey = new Sec1PublicKey(KEY_TYPE, ecPublicKey);
|
||||||
ECPrivateKeyParameters ecPrivateKey =
|
ECPrivateKeyParameters ecPrivateKey =
|
||||||
(ECPrivateKeyParameters) keyPair.getPrivate();
|
(ECPrivateKeyParameters) keyPair.getPrivate();
|
||||||
PrivateKey privateKey =
|
PrivateKey privateKey =
|
||||||
new Sec1PrivateKey(ecPrivateKey, MESSAGE_KEY_BITS);
|
new Sec1PrivateKey(KEY_TYPE, ecPrivateKey, MESSAGE_KEY_BITS);
|
||||||
return new KeyPair(publicKey, privateKey);
|
return new KeyPair(publicKey, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ class Sec1KeyParser implements KeyParser {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(Sec1KeyParser.class.getName());
|
Logger.getLogger(Sec1KeyParser.class.getName());
|
||||||
|
|
||||||
|
private final String keyType;
|
||||||
private final ECDomainParameters params;
|
private final ECDomainParameters params;
|
||||||
private final BigInteger modulus;
|
private final BigInteger modulus;
|
||||||
private final int keyBits, bytesPerInt, publicKeyBytes, privateKeyBytes;
|
private final int keyBits, bytesPerInt, publicKeyBytes, privateKeyBytes;
|
||||||
|
|
||||||
Sec1KeyParser(ECDomainParameters params, int keyBits) {
|
Sec1KeyParser(String keyType, ECDomainParameters params, int keyBits) {
|
||||||
|
this.keyType = keyType;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.keyBits = keyBits;
|
this.keyBits = keyBits;
|
||||||
modulus = ((ECCurve.Fp) params.getCurve()).getQ();
|
modulus = ((ECCurve.Fp) params.getCurve()).getQ();
|
||||||
@@ -80,7 +82,7 @@ class Sec1KeyParser implements KeyParser {
|
|||||||
throw new GeneralSecurityException();
|
throw new GeneralSecurityException();
|
||||||
// Construct a public key from the point (x, y) and the params
|
// Construct a public key from the point (x, y) and the params
|
||||||
ECPublicKeyParameters k = new ECPublicKeyParameters(pub, params);
|
ECPublicKeyParameters k = new ECPublicKeyParameters(pub, params);
|
||||||
PublicKey p = new Sec1PublicKey(k);
|
PublicKey p = new Sec1PublicKey(keyType, k);
|
||||||
logDuration(LOG, "Parsing public key", start);
|
logDuration(LOG, "Parsing public key", start);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@@ -97,7 +99,7 @@ class Sec1KeyParser implements KeyParser {
|
|||||||
throw new GeneralSecurityException();
|
throw new GeneralSecurityException();
|
||||||
// Construct a private key from the private value and the params
|
// Construct a private key from the private value and the params
|
||||||
ECPrivateKeyParameters k = new ECPrivateKeyParameters(d, params);
|
ECPrivateKeyParameters k = new ECPrivateKeyParameters(d, params);
|
||||||
PrivateKey p = new Sec1PrivateKey(k, keyBits);
|
PrivateKey p = new Sec1PrivateKey(keyType, k, keyBits);
|
||||||
logDuration(LOG, "Parsing private key", start);
|
logDuration(LOG, "Parsing private key", start);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,21 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class Sec1PrivateKey implements PrivateKey {
|
class Sec1PrivateKey implements PrivateKey {
|
||||||
|
|
||||||
|
private final String keyType;
|
||||||
private final ECPrivateKeyParameters key;
|
private final ECPrivateKeyParameters key;
|
||||||
private final int bytesPerInt;
|
private final int bytesPerInt;
|
||||||
|
|
||||||
Sec1PrivateKey(ECPrivateKeyParameters key, int keyBits) {
|
Sec1PrivateKey(String keyType, ECPrivateKeyParameters key, int keyBits) {
|
||||||
|
this.keyType = keyType;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
bytesPerInt = (keyBits + 7) / 8;
|
bytesPerInt = (keyBits + 7) / 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKeyType() {
|
||||||
|
return keyType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getEncoded() {
|
public byte[] getEncoded() {
|
||||||
byte[] encodedKey = new byte[bytesPerInt];
|
byte[] encodedKey = new byte[bytesPerInt];
|
||||||
|
|||||||
@@ -15,12 +15,19 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class Sec1PublicKey implements PublicKey {
|
class Sec1PublicKey implements PublicKey {
|
||||||
|
|
||||||
|
private final String keyType;
|
||||||
private final ECPublicKeyParameters key;
|
private final ECPublicKeyParameters key;
|
||||||
|
|
||||||
Sec1PublicKey(ECPublicKeyParameters key) {
|
Sec1PublicKey(String keyType, ECPublicKeyParameters key) {
|
||||||
|
this.keyType = keyType;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKeyType() {
|
||||||
|
return keyType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getEncoded() {
|
public byte[] getEncoded() {
|
||||||
return key.getQ().getEncoded(false);
|
return key.getQ().getEncoded(false);
|
||||||
|
|||||||
@@ -3,24 +3,29 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class EdKeyParser implements KeyParser {
|
class SignatureKeyParser implements KeyParser {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicKey parsePublicKey(byte[] encodedKey)
|
public PublicKey parsePublicKey(byte[] encodedKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||||
return new EdPublicKey(encodedKey);
|
return new SignaturePublicKey(encodedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||||
return new EdPrivateKey(encodedKey);
|
return new SignaturePrivateKey(encodedKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
|
||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
@@ -42,7 +41,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportKeys deriveTransportKeys(TransportId t,
|
public TransportKeys deriveRotationKeys(TransportId t,
|
||||||
SecretKey rootKey, long timePeriod, boolean weAreAlice,
|
SecretKey rootKey, long timePeriod, boolean weAreAlice,
|
||||||
boolean active) {
|
boolean active) {
|
||||||
// Keys for the previous period are derived from the root key
|
// Keys for the previous period are derived from the root key
|
||||||
@@ -70,31 +69,6 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod) {
|
|
||||||
if (k.getTimePeriod() >= timePeriod) return k;
|
|
||||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
|
||||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
|
||||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
|
||||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
|
||||||
long startPeriod = outCurr.getTimePeriod();
|
|
||||||
boolean active = outCurr.isActive();
|
|
||||||
// Rotate the keys
|
|
||||||
for (long p = startPeriod + 1; p <= timePeriod; p++) {
|
|
||||||
inPrev = inCurr;
|
|
||||||
inCurr = inNext;
|
|
||||||
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
|
|
||||||
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
|
|
||||||
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
|
|
||||||
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
|
|
||||||
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
|
|
||||||
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p, active);
|
|
||||||
}
|
|
||||||
// Collect and return the keys
|
|
||||||
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
|
||||||
outCurr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SecretKey rotateKey(SecretKey k, long timePeriod) {
|
private SecretKey rotateKey(SecretKey k, long timePeriod) {
|
||||||
byte[] period = new byte[INT_64_BYTES];
|
byte[] period = new byte[INT_64_BYTES];
|
||||||
writeUint64(timePeriod, period, 0);
|
writeUint64(timePeriod, period, 0);
|
||||||
@@ -117,7 +91,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
|
public TransportKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
|
||||||
long timePeriod, boolean weAreAlice) {
|
long timePeriod, boolean weAreAlice) {
|
||||||
if (timePeriod < 1) throw new IllegalArgumentException();
|
if (timePeriod < 1) throw new IllegalArgumentException();
|
||||||
IncomingKeys inPrev = deriveIncomingHandshakeKeys(t, rootKey,
|
IncomingKeys inPrev = deriveIncomingHandshakeKeys(t, rootKey,
|
||||||
@@ -128,7 +102,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
weAreAlice, timePeriod + 1);
|
weAreAlice, timePeriod + 1);
|
||||||
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
|
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
|
||||||
weAreAlice, timePeriod);
|
weAreAlice, timePeriod);
|
||||||
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr, rootKey,
|
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr, rootKey,
|
||||||
weAreAlice);
|
weAreAlice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +145,13 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod) {
|
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) {
|
||||||
long elapsed = timePeriod - k.getTimePeriod();
|
long elapsed = timePeriod - k.getTimePeriod();
|
||||||
TransportId t = k.getTransportId();
|
TransportId t = k.getTransportId();
|
||||||
SecretKey rootKey = k.getRootKey();
|
SecretKey rootKey = k.getRootKey();
|
||||||
@@ -188,7 +168,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
weAreAlice, timePeriod + 1);
|
weAreAlice, timePeriod + 1);
|
||||||
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
|
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
|
||||||
weAreAlice, timePeriod);
|
weAreAlice, timePeriod);
|
||||||
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
|
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr,
|
||||||
rootKey, weAreAlice);
|
rootKey, weAreAlice);
|
||||||
} else if (elapsed == 2) {
|
} else if (elapsed == 2) {
|
||||||
// The keys are two periods old - shift by two periods, keeping
|
// The keys are two periods old - shift by two periods, keeping
|
||||||
@@ -200,7 +180,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
weAreAlice, timePeriod + 1);
|
weAreAlice, timePeriod + 1);
|
||||||
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
|
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
|
||||||
weAreAlice, timePeriod);
|
weAreAlice, timePeriod);
|
||||||
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
|
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr,
|
||||||
rootKey, weAreAlice);
|
rootKey, weAreAlice);
|
||||||
} else {
|
} else {
|
||||||
// The keys are more than two periods old - derive fresh keys
|
// The keys are more than two periods old - derive fresh keys
|
||||||
@@ -208,6 +188,30 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TransportKeys updateRotationKeys(TransportKeys k, long timePeriod) {
|
||||||
|
if (k.getTimePeriod() >= timePeriod) return k;
|
||||||
|
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||||
|
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||||
|
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||||
|
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||||
|
long startPeriod = outCurr.getTimePeriod();
|
||||||
|
boolean active = outCurr.isActive();
|
||||||
|
// Rotate the keys
|
||||||
|
for (long p = startPeriod + 1; p <= timePeriod; p++) {
|
||||||
|
inPrev = inCurr;
|
||||||
|
inCurr = inNext;
|
||||||
|
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
|
||||||
|
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
|
||||||
|
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
|
||||||
|
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
|
||||||
|
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
|
||||||
|
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p, active);
|
||||||
|
}
|
||||||
|
// Collect and return the keys
|
||||||
|
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
||||||
|
outCurr);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||||
long streamNumber) {
|
long streamNumber) {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import org.briarproject.bramble.api.contact.ContactId;
|
|||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactState;
|
import org.briarproject.bramble.api.contact.PendingContactState;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
import org.briarproject.bramble.api.db.DataTooNewException;
|
||||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||||
@@ -27,11 +29,8 @@ import org.briarproject.bramble.api.sync.Message;
|
|||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||||
import org.briarproject.bramble.api.sync.validation.MessageState;
|
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -105,20 +104,6 @@ interface Database<T> {
|
|||||||
void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
|
void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the given handshake keys for the given contact and returns a
|
|
||||||
* key set ID.
|
|
||||||
*/
|
|
||||||
HandshakeKeySetId addHandshakeKeys(T txn, ContactId c, HandshakeKeys k)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the given handshake keys for the given pending contact and
|
|
||||||
* returns a key set ID.
|
|
||||||
*/
|
|
||||||
HandshakeKeySetId addHandshakeKeys(T txn, PendingContactId p,
|
|
||||||
HandshakeKeys k) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores an identity.
|
* Stores an identity.
|
||||||
*/
|
*/
|
||||||
@@ -160,7 +145,14 @@ interface Database<T> {
|
|||||||
* Stores the given transport keys for the given contact and returns a
|
* Stores the given transport keys for the given contact and returns a
|
||||||
* key set ID.
|
* key set ID.
|
||||||
*/
|
*/
|
||||||
TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
|
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)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -273,7 +265,7 @@ interface Database<T> {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
|
Collection<ContactId> getContacts(T txn, AuthorId local) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the group with the given ID.
|
* Returns the group with the given ID.
|
||||||
@@ -315,14 +307,6 @@ interface Database<T> {
|
|||||||
Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
|
Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all handshake keys for the given transport.
|
|
||||||
* <p/>
|
|
||||||
* Read-only.
|
|
||||||
*/
|
|
||||||
Collection<HandshakeKeySet> getHandshakeKeys(T txn, TransportId t)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the identity for local pseudonym with the given ID.
|
* Returns the identity for local pseudonym with the given ID.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -545,16 +529,10 @@ interface Database<T> {
|
|||||||
Collection<TransportKeySet> getTransportKeys(T txn, TransportId t)
|
Collection<TransportKeySet> getTransportKeys(T txn, TransportId t)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Increments the outgoing stream counter for the given handshake keys.
|
|
||||||
*/
|
|
||||||
void incrementStreamCounter(T txn, TransportId t, HandshakeKeySetId k)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increments the outgoing stream counter for the given transport keys.
|
* Increments the outgoing stream counter for the given transport keys.
|
||||||
*/
|
*/
|
||||||
void incrementStreamCounter(T txn, TransportId t, TransportKeySetId k)
|
void incrementStreamCounter(T txn, TransportId t, KeySetId k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -623,12 +601,6 @@ interface Database<T> {
|
|||||||
void removeGroupVisibility(T txn, ContactId c, GroupId g)
|
void removeGroupVisibility(T txn, ContactId c, GroupId g)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given handshake keys from the database.
|
|
||||||
*/
|
|
||||||
void removeHandshakeKeys(T txn, TransportId t, HandshakeKeySetId k)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an identity (and all associated state) from the database.
|
* Removes an identity (and all associated state) from the database.
|
||||||
*/
|
*/
|
||||||
@@ -659,8 +631,7 @@ interface Database<T> {
|
|||||||
/**
|
/**
|
||||||
* Removes the given transport keys from the database.
|
* Removes the given transport keys from the database.
|
||||||
*/
|
*/
|
||||||
void removeTransportKeys(T txn, TransportId t, TransportKeySetId k)
|
void removeTransportKeys(T txn, TransportId t, KeySetId k) throws DbException;
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the transmission count and expiry time of the given message with
|
* Resets the transmission count and expiry time of the given message with
|
||||||
@@ -689,8 +660,8 @@ interface Database<T> {
|
|||||||
/**
|
/**
|
||||||
* Sets the handshake key pair for the identity with the given ID.
|
* Sets the handshake key pair for the identity with the given ID.
|
||||||
*/
|
*/
|
||||||
void setHandshakeKeyPair(T txn, AuthorId local, byte[] publicKey,
|
void setHandshakeKeyPair(T txn, AuthorId local, PublicKey publicKey,
|
||||||
byte[] privateKey) throws DbException;
|
PrivateKey privateKey) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given message as shared.
|
* Marks the given message as shared.
|
||||||
@@ -710,23 +681,16 @@ interface Database<T> {
|
|||||||
PendingContactState state) throws DbException;
|
PendingContactState state) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the reordering window for the given transport key set in the given
|
* Sets the reordering window for the given transport keys in the given
|
||||||
* time period.
|
* time period.
|
||||||
*/
|
*/
|
||||||
void setReorderingWindow(T txn, TransportKeySetId k, TransportId t,
|
void setReorderingWindow(T txn, KeySetId k, TransportId t,
|
||||||
long timePeriod, long base, byte[] bitmap) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the reordering window for the given handshake key set in the given
|
|
||||||
* time period.
|
|
||||||
*/
|
|
||||||
void setReorderingWindow(T txn, HandshakeKeySetId k, TransportId t,
|
|
||||||
long timePeriod, long base, byte[] bitmap) throws DbException;
|
long timePeriod, long base, byte[] bitmap) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given transport keys as usable for outgoing streams.
|
* Marks the given transport keys as usable for outgoing streams.
|
||||||
*/
|
*/
|
||||||
void setTransportKeysActive(T txn, TransportId t, TransportKeySetId k)
|
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -738,12 +702,7 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the given handshake keys.
|
* Stores the given transport keys, deleting any keys they have replaced.
|
||||||
*/
|
|
||||||
void updateHandshakeKeys(T txn, HandshakeKeySet ks) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the given transport keys following key rotation.
|
|
||||||
*/
|
*/
|
||||||
void updateTransportKeys(T txn, TransportKeySet ks) throws DbException;
|
void updateTransportKeys(T txn, TransportKeySet ks) throws DbException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import org.briarproject.bramble.api.contact.PendingContactId;
|
|||||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
||||||
|
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
|
||||||
|
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.CommitAction;
|
import org.briarproject.bramble.api.db.CommitAction;
|
||||||
import org.briarproject.bramble.api.db.CommitAction.Visitor;
|
import org.briarproject.bramble.api.db.CommitAction.Visitor;
|
||||||
@@ -62,11 +66,8 @@ import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
|
|||||||
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
||||||
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
||||||
import org.briarproject.bramble.api.sync.validation.MessageState;
|
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -258,30 +259,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
|
|
||||||
ContactId c, HandshakeKeys k) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsContact(txn, c))
|
|
||||||
throw new NoSuchContactException();
|
|
||||||
if (!db.containsTransport(txn, k.getTransportId()))
|
|
||||||
throw new NoSuchTransportException();
|
|
||||||
return db.addHandshakeKeys(txn, c, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
|
|
||||||
PendingContactId p, HandshakeKeys k) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsPendingContact(txn, p))
|
|
||||||
throw new NoSuchPendingContactException();
|
|
||||||
if (!db.containsTransport(txn, k.getTransportId()))
|
|
||||||
throw new NoSuchTransportException();
|
|
||||||
return db.addHandshakeKeys(txn, p, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addIdentity(Transaction transaction, Identity i)
|
public void addIdentity(Transaction transaction, Identity i)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -318,6 +295,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
if (db.containsPendingContact(txn, p.getId()))
|
if (db.containsPendingContact(txn, p.getId()))
|
||||||
throw new PendingContactExistsException();
|
throw new PendingContactExistsException();
|
||||||
db.addPendingContact(txn, p);
|
db.addPendingContact(txn, p);
|
||||||
|
transaction.attach(new PendingContactStateChangedEvent(p.getId(),
|
||||||
|
p.getState()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -330,8 +309,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportKeySetId addTransportKeys(Transaction transaction,
|
public KeySetId addTransportKeys(Transaction transaction, ContactId c,
|
||||||
ContactId c, TransportKeys k) throws DbException {
|
TransportKeys k) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsContact(txn, c))
|
if (!db.containsContact(txn, c))
|
||||||
@@ -341,6 +320,18 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return db.addTransportKeys(txn, c, k);
|
return db.addTransportKeys(txn, c, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeySetId addTransportKeys(Transaction transaction,
|
||||||
|
PendingContactId p, TransportKeys k) throws DbException {
|
||||||
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
|
T txn = unbox(transaction);
|
||||||
|
if (!db.containsPendingContact(txn, p))
|
||||||
|
throw new NoSuchPendingContactException();
|
||||||
|
if (!db.containsTransport(txn, k.getTransportId()))
|
||||||
|
throw new NoSuchTransportException();
|
||||||
|
return db.addTransportKeys(txn, p, k);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsContact(Transaction transaction, AuthorId remote,
|
public boolean containsContact(Transaction transaction, AuthorId remote,
|
||||||
AuthorId local) throws DbException {
|
AuthorId local) throws DbException {
|
||||||
@@ -503,11 +494,11 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<ContactId> getContacts(Transaction transaction,
|
public Collection<ContactId> getContacts(Transaction transaction,
|
||||||
AuthorId a) throws DbException {
|
AuthorId local) throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsIdentity(txn, a))
|
if (!db.containsIdentity(txn, local))
|
||||||
throw new NoSuchIdentityException();
|
throw new NoSuchIdentityException();
|
||||||
return db.getContacts(txn, a);
|
return db.getContacts(txn, local);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -544,15 +535,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return db.getGroupVisibility(txn, c, g);
|
return db.getGroupVisibility(txn, c, g);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<HandshakeKeySet> getHandshakeKeys(Transaction transaction,
|
|
||||||
TransportId t) throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsTransport(txn, t))
|
|
||||||
throw new NoSuchTransportException();
|
|
||||||
return db.getHandshakeKeys(txn, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Identity getIdentity(Transaction transaction, AuthorId a)
|
public Identity getIdentity(Transaction transaction, AuthorId a)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -735,17 +717,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void incrementStreamCounter(Transaction transaction, TransportId t,
|
public void incrementStreamCounter(Transaction transaction, TransportId t,
|
||||||
HandshakeKeySetId k) throws DbException {
|
KeySetId k) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsTransport(txn, t))
|
|
||||||
throw new NoSuchTransportException();
|
|
||||||
db.incrementStreamCounter(txn, t, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void incrementStreamCounter(Transaction transaction, TransportId t,
|
|
||||||
TransportKeySetId k) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsTransport(txn, t))
|
if (!db.containsTransport(txn, t))
|
||||||
@@ -894,16 +866,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
|
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeHandshakeKeys(Transaction transaction,
|
|
||||||
TransportId t, HandshakeKeySetId k) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsTransport(txn, t))
|
|
||||||
throw new NoSuchTransportException();
|
|
||||||
db.removeHandshakeKeys(txn, t, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeIdentity(Transaction transaction, AuthorId a)
|
public void removeIdentity(Transaction transaction, AuthorId a)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -934,6 +896,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
if (!db.containsPendingContact(txn, p))
|
if (!db.containsPendingContact(txn, p))
|
||||||
throw new NoSuchPendingContactException();
|
throw new NoSuchPendingContactException();
|
||||||
db.removePendingContact(txn, p);
|
db.removePendingContact(txn, p);
|
||||||
|
transaction.attach(new PendingContactRemovedEvent(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -947,8 +910,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeTransportKeys(Transaction transaction,
|
public void removeTransportKeys(Transaction transaction, TransportId t,
|
||||||
TransportId t, TransportKeySetId k) throws DbException {
|
KeySetId k) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsTransport(txn, t))
|
if (!db.containsTransport(txn, t))
|
||||||
@@ -1037,7 +1000,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHandshakeKeyPair(Transaction transaction, AuthorId local,
|
public void setHandshakeKeyPair(Transaction transaction, AuthorId local,
|
||||||
byte[] publicKey, byte[] privateKey) throws DbException {
|
PublicKey publicKey, PrivateKey privateKey) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsIdentity(txn, local))
|
if (!db.containsIdentity(txn, local))
|
||||||
@@ -1046,20 +1009,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setReorderingWindow(Transaction transaction,
|
public void setReorderingWindow(Transaction transaction, KeySetId k,
|
||||||
TransportKeySetId k, TransportId t, long timePeriod, long base,
|
TransportId t, long timePeriod, long base, byte[] bitmap)
|
||||||
byte[] bitmap) throws DbException {
|
throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsTransport(txn, t))
|
|
||||||
throw new NoSuchTransportException();
|
|
||||||
db.setReorderingWindow(txn, k, t, timePeriod, base, bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setReorderingWindow(Transaction transaction,
|
|
||||||
HandshakeKeySetId k, TransportId t, long timePeriod, long base,
|
|
||||||
byte[] bitmap) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsTransport(txn, t))
|
if (!db.containsTransport(txn, t))
|
||||||
@@ -1069,7 +1021,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTransportKeysActive(Transaction transaction, TransportId t,
|
public void setTransportKeysActive(Transaction transaction, TransportId t,
|
||||||
TransportKeySetId k) throws DbException {
|
KeySetId k) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsTransport(txn, t))
|
if (!db.containsTransport(txn, t))
|
||||||
@@ -1077,18 +1029,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
db.setTransportKeysActive(txn, t, k);
|
db.setTransportKeysActive(txn, t, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateHandshakeKeys(Transaction transaction,
|
|
||||||
Collection<HandshakeKeySet> keys) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
for (HandshakeKeySet ks : keys) {
|
|
||||||
TransportId t = ks.getKeys().getTransportId();
|
|
||||||
if (db.containsTransport(txn, t))
|
|
||||||
db.updateHandshakeKeys(txn, ks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateTransportKeys(Transaction transaction,
|
public void updateTransportKeys(Transaction transaction,
|
||||||
Collection<TransportKeySet> keys) throws DbException {
|
Collection<TransportKeySet> keys) throws DbException {
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ import java.util.logging.Logger;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all the H2-specific code for the database.
|
* Contains all the H2-specific code for the database.
|
||||||
@@ -61,8 +63,18 @@ class H2Database extends JdbcDatabase {
|
|||||||
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
boolean reopen = !config.getDatabaseDirectory().mkdirs();
|
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);
|
||||||
super.open("org.h2.Driver", reopen, key, listener);
|
super.open("org.h2.Driver", reopen, key, listener);
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Contents of account directory after opening DB:");
|
||||||
|
logFileOrDir(LOG, INFO, dir.getParentFile());
|
||||||
|
}
|
||||||
return reopen;
|
return reopen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,13 @@ import org.briarproject.bramble.api.contact.ContactId;
|
|||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.contact.PendingContactState;
|
import org.briarproject.bramble.api.contact.PendingContactState;
|
||||||
|
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
||||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
import org.briarproject.bramble.api.db.DataTooNewException;
|
||||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||||
import org.briarproject.bramble.api.db.DbClosedException;
|
import org.briarproject.bramble.api.db.DbClosedException;
|
||||||
@@ -30,13 +36,10 @@ import org.briarproject.bramble.api.sync.MessageId;
|
|||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||||
import org.briarproject.bramble.api.sync.validation.MessageState;
|
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
|
||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
@@ -62,11 +65,13 @@ import java.util.logging.Logger;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static java.sql.Types.BINARY;
|
import static java.sql.Types.BINARY;
|
||||||
|
import static java.sql.Types.BOOLEAN;
|
||||||
import static java.sql.Types.INTEGER;
|
import static java.sql.Types.INTEGER;
|
||||||
import static java.sql.Types.VARCHAR;
|
import static java.sql.Types.VARCHAR;
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
@@ -93,7 +98,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
|||||||
abstract class JdbcDatabase implements Database<Connection> {
|
abstract class JdbcDatabase implements Database<Connection> {
|
||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
static final int CODE_SCHEMA_VERSION = 43;
|
static final int CODE_SCHEMA_VERSION = 44;
|
||||||
|
|
||||||
// Time period offsets for incoming transport keys
|
// Time period offsets for incoming transport keys
|
||||||
private static final int OFFSET_PREV = -1;
|
private static final int OFFSET_PREV = -1;
|
||||||
@@ -254,16 +259,28 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " maxLatency INT NOT NULL,"
|
+ " maxLatency INT NOT NULL,"
|
||||||
+ " PRIMARY KEY (transportId))";
|
+ " PRIMARY KEY (transportId))";
|
||||||
|
|
||||||
|
private static final String CREATE_PENDING_CONTACTS =
|
||||||
|
"CREATE TABLE pendingContacts"
|
||||||
|
+ " (pendingContactId _HASH NOT NULL,"
|
||||||
|
+ " publicKey _BINARY NOT NULL,"
|
||||||
|
+ " alias _STRING NOT NULL,"
|
||||||
|
+ " state INT NOT NULL,"
|
||||||
|
+ " timestamp BIGINT NOT NULL,"
|
||||||
|
+ " PRIMARY KEY (pendingContactId))";
|
||||||
|
|
||||||
private static final String CREATE_OUTGOING_KEYS =
|
private static final String CREATE_OUTGOING_KEYS =
|
||||||
"CREATE TABLE outgoingKeys"
|
"CREATE TABLE outgoingKeys"
|
||||||
+ " (transportId _STRING NOT NULL,"
|
+ " (transportId _STRING NOT NULL,"
|
||||||
+ " keySetId _COUNTER,"
|
+ " keySetId _COUNTER,"
|
||||||
+ " timePeriod BIGINT NOT NULL,"
|
+ " timePeriod BIGINT NOT NULL,"
|
||||||
+ " contactId INT NOT NULL,"
|
+ " contactId INT," // Null if contact is pending
|
||||||
|
+ " pendingContactId _HASH," // Null if not pending
|
||||||
+ " tagKey _SECRET NOT NULL,"
|
+ " tagKey _SECRET NOT NULL,"
|
||||||
+ " headerKey _SECRET NOT NULL,"
|
+ " headerKey _SECRET NOT NULL,"
|
||||||
+ " stream BIGINT NOT NULL,"
|
+ " stream BIGINT NOT NULL,"
|
||||||
+ " active BOOLEAN NOT NULL,"
|
+ " active BOOLEAN NOT NULL,"
|
||||||
|
+ " rootKey _SECRET," // Null for rotation keys
|
||||||
|
+ " alice BOOLEAN," // Null for rotation keys
|
||||||
+ " PRIMARY KEY (transportId, keySetId),"
|
+ " PRIMARY KEY (transportId, keySetId),"
|
||||||
+ " FOREIGN KEY (transportId)"
|
+ " FOREIGN KEY (transportId)"
|
||||||
+ " REFERENCES transports (transportId)"
|
+ " REFERENCES transports (transportId)"
|
||||||
@@ -271,6 +288,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " UNIQUE (keySetId),"
|
+ " UNIQUE (keySetId),"
|
||||||
+ " FOREIGN KEY (contactId)"
|
+ " FOREIGN KEY (contactId)"
|
||||||
+ " REFERENCES contacts (contactId)"
|
+ " REFERENCES contacts (contactId)"
|
||||||
|
+ " ON DELETE CASCADE,"
|
||||||
|
+ " FOREIGN KEY (pendingContactId)"
|
||||||
|
+ " REFERENCES pendingContacts (pendingContactId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " ON DELETE CASCADE)";
|
||||||
|
|
||||||
private static final String CREATE_INCOMING_KEYS =
|
private static final String CREATE_INCOMING_KEYS =
|
||||||
@@ -291,57 +311,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " REFERENCES outgoingKeys (keySetId)"
|
+ " REFERENCES outgoingKeys (keySetId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " ON DELETE CASCADE)";
|
||||||
|
|
||||||
private static final String CREATE_PENDING_CONTACTS =
|
|
||||||
"CREATE TABLE pendingContacts"
|
|
||||||
+ " (pendingContactId _HASH NOT NULL,"
|
|
||||||
+ " publicKey _BINARY NOT NULL,"
|
|
||||||
+ " alias _STRING NOT NULL,"
|
|
||||||
+ " state INT NOT NULL,"
|
|
||||||
+ " timestamp BIGINT NOT NULL,"
|
|
||||||
+ " PRIMARY KEY (pendingContactId))";
|
|
||||||
|
|
||||||
private static final String CREATE_OUTGOING_HANDSHAKE_KEYS =
|
|
||||||
"CREATE TABLE outgoingHandshakeKeys"
|
|
||||||
+ " (transportId _STRING NOT NULL,"
|
|
||||||
+ " keySetId _COUNTER,"
|
|
||||||
+ " timePeriod BIGINT NOT NULL,"
|
|
||||||
+ " contactId INT," // Null if contact is pending
|
|
||||||
+ " pendingContactId _HASH," // Null if not pending
|
|
||||||
+ " rootKey _SECRET NOT NULL,"
|
|
||||||
+ " alice BOOLEAN NOT NULL,"
|
|
||||||
+ " tagKey _SECRET NOT NULL,"
|
|
||||||
+ " headerKey _SECRET NOT NULL,"
|
|
||||||
+ " stream BIGINT NOT NULL,"
|
|
||||||
+ " PRIMARY KEY (transportId, keySetId),"
|
|
||||||
+ " FOREIGN KEY (transportId)"
|
|
||||||
+ " REFERENCES transports (transportId)"
|
|
||||||
+ " ON DELETE CASCADE,"
|
|
||||||
+ " UNIQUE (keySetId),"
|
|
||||||
+ " FOREIGN KEY (contactId)"
|
|
||||||
+ " REFERENCES contacts (contactId)"
|
|
||||||
+ " ON DELETE CASCADE,"
|
|
||||||
+ " FOREIGN KEY (pendingContactId)"
|
|
||||||
+ " REFERENCES pendingContacts (pendingContactId)"
|
|
||||||
+ " ON DELETE CASCADE)";
|
|
||||||
|
|
||||||
private static final String CREATE_INCOMING_HANDSHAKE_KEYS =
|
|
||||||
"CREATE TABLE incomingHandshakeKeys"
|
|
||||||
+ " (transportId _STRING NOT NULL,"
|
|
||||||
+ " keySetId INT NOT NULL,"
|
|
||||||
+ " timePeriod BIGINT NOT NULL,"
|
|
||||||
+ " tagKey _SECRET NOT NULL,"
|
|
||||||
+ " headerKey _SECRET NOT NULL,"
|
|
||||||
+ " base BIGINT NOT NULL,"
|
|
||||||
+ " bitmap _BINARY NOT NULL,"
|
|
||||||
+ " periodOffset INT NOT NULL,"
|
|
||||||
+ " PRIMARY KEY (transportId, keySetId, periodOffset),"
|
|
||||||
+ " FOREIGN KEY (transportId)"
|
|
||||||
+ " REFERENCES transports (transportId)"
|
|
||||||
+ " ON DELETE CASCADE,"
|
|
||||||
+ " FOREIGN KEY (keySetId)"
|
|
||||||
+ " REFERENCES outgoingHandshakeKeys (keySetId)"
|
|
||||||
+ " ON DELETE CASCADE)";
|
|
||||||
|
|
||||||
private static final String INDEX_CONTACTS_BY_AUTHOR_ID =
|
private static final String INDEX_CONTACTS_BY_AUTHOR_ID =
|
||||||
"CREATE INDEX IF NOT EXISTS contactsByAuthorId"
|
"CREATE INDEX IF NOT EXISTS contactsByAuthorId"
|
||||||
+ " ON contacts (authorId)";
|
+ " ON contacts (authorId)";
|
||||||
@@ -367,7 +336,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " ON statuses (contactId, timestamp)";
|
+ " ON statuses (contactId, timestamp)";
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(JdbcDatabase.class.getName());
|
getLogger(JdbcDatabase.class.getName());
|
||||||
|
|
||||||
// Different database libraries use different names for certain types
|
// Different database libraries use different names for certain types
|
||||||
private final MessageFactory messageFactory;
|
private final MessageFactory messageFactory;
|
||||||
@@ -487,7 +456,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
new Migration39_40(),
|
new Migration39_40(),
|
||||||
new Migration40_41(dbTypes),
|
new Migration40_41(dbTypes),
|
||||||
new Migration41_42(dbTypes),
|
new Migration41_42(dbTypes),
|
||||||
new Migration42_43(dbTypes)
|
new Migration42_43(dbTypes),
|
||||||
|
new Migration43_44(dbTypes)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,13 +505,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_OFFERS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_OFFERS));
|
||||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_STATUSES));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_STATUSES));
|
||||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS));
|
||||||
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_PENDING_CONTACTS));
|
||||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS));
|
||||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
|
||||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_PENDING_CONTACTS));
|
|
||||||
s.executeUpdate(dbTypes.replaceTypes(
|
|
||||||
CREATE_OUTGOING_HANDSHAKE_KEYS));
|
|
||||||
s.executeUpdate(dbTypes.replaceTypes(
|
|
||||||
CREATE_INCOMING_HANDSHAKE_KEYS));
|
|
||||||
s.close();
|
s.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s, LOG, WARNING);
|
tryToClose(s, LOG, WARNING);
|
||||||
@@ -677,7 +643,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setBytes(1, remote.getId().getBytes());
|
ps.setBytes(1, remote.getId().getBytes());
|
||||||
ps.setInt(2, remote.getFormatVersion());
|
ps.setInt(2, remote.getFormatVersion());
|
||||||
ps.setString(3, remote.getName());
|
ps.setString(3, remote.getName());
|
||||||
ps.setBytes(4, remote.getPublicKey());
|
ps.setBytes(4, remote.getPublicKey().getEncoded());
|
||||||
ps.setBytes(5, local.getBytes());
|
ps.setBytes(5, local.getBytes());
|
||||||
ps.setBoolean(6, verified);
|
ps.setBoolean(6, verified);
|
||||||
int affected = ps.executeUpdate();
|
int affected = ps.executeUpdate();
|
||||||
@@ -777,103 +743,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandshakeKeySetId addHandshakeKeys(Connection txn, ContactId c,
|
|
||||||
HandshakeKeys k) throws DbException {
|
|
||||||
return addHandshakeKeys(txn, c, null, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandshakeKeySetId addHandshakeKeys(Connection txn,
|
|
||||||
PendingContactId p, HandshakeKeys k) throws DbException {
|
|
||||||
return addHandshakeKeys(txn, null, p, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HandshakeKeySetId addHandshakeKeys(Connection txn,
|
|
||||||
@Nullable ContactId c, @Nullable PendingContactId p,
|
|
||||||
HandshakeKeys k) throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
|
||||||
ResultSet rs = null;
|
|
||||||
try {
|
|
||||||
// Store the outgoing keys
|
|
||||||
String sql = "INSERT INTO outgoingHandshakeKeys (contactId,"
|
|
||||||
+ " pendingContactId, transportId, rootKey, alice,"
|
|
||||||
+ " timePeriod, tagKey, headerKey, stream)"
|
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
if (c == null) ps.setNull(1, INTEGER);
|
|
||||||
else ps.setInt(1, c.getInt());
|
|
||||||
if (p == null) ps.setNull(2, BINARY);
|
|
||||||
else ps.setBytes(2, p.getBytes());
|
|
||||||
ps.setString(3, k.getTransportId().getString());
|
|
||||||
ps.setBytes(4, k.getRootKey().getBytes());
|
|
||||||
ps.setBoolean(5, k.isAlice());
|
|
||||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
|
||||||
ps.setLong(6, outCurr.getTimePeriod());
|
|
||||||
ps.setBytes(7, outCurr.getTagKey().getBytes());
|
|
||||||
ps.setBytes(8, outCurr.getHeaderKey().getBytes());
|
|
||||||
ps.setLong(9, outCurr.getStreamCounter());
|
|
||||||
int affected = ps.executeUpdate();
|
|
||||||
if (affected != 1) throw new DbStateException();
|
|
||||||
ps.close();
|
|
||||||
// Get the new (highest) key set ID
|
|
||||||
sql = "SELECT keySetId FROM outgoingHandshakeKeys"
|
|
||||||
+ " ORDER BY keySetId DESC LIMIT 1";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
rs = ps.executeQuery();
|
|
||||||
if (!rs.next()) throw new DbStateException();
|
|
||||||
HandshakeKeySetId keySetId = new HandshakeKeySetId(rs.getInt(1));
|
|
||||||
if (rs.next()) throw new DbStateException();
|
|
||||||
rs.close();
|
|
||||||
ps.close();
|
|
||||||
// Store the incoming keys
|
|
||||||
sql = "INSERT INTO incomingHandshakeKeys (keySetId, transportId,"
|
|
||||||
+ " timePeriod, tagKey, headerKey, base, bitmap,"
|
|
||||||
+ " periodOffset)"
|
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setInt(1, keySetId.getInt());
|
|
||||||
ps.setString(2, k.getTransportId().getString());
|
|
||||||
// Previous time period
|
|
||||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
|
||||||
ps.setLong(3, inPrev.getTimePeriod());
|
|
||||||
ps.setBytes(4, inPrev.getTagKey().getBytes());
|
|
||||||
ps.setBytes(5, inPrev.getHeaderKey().getBytes());
|
|
||||||
ps.setLong(6, inPrev.getWindowBase());
|
|
||||||
ps.setBytes(7, inPrev.getWindowBitmap());
|
|
||||||
ps.setInt(8, OFFSET_PREV);
|
|
||||||
ps.addBatch();
|
|
||||||
// Current time period
|
|
||||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
|
||||||
ps.setLong(3, inCurr.getTimePeriod());
|
|
||||||
ps.setBytes(4, inCurr.getTagKey().getBytes());
|
|
||||||
ps.setBytes(5, inCurr.getHeaderKey().getBytes());
|
|
||||||
ps.setLong(6, inCurr.getWindowBase());
|
|
||||||
ps.setBytes(7, inCurr.getWindowBitmap());
|
|
||||||
ps.setInt(8, OFFSET_CURR);
|
|
||||||
ps.addBatch();
|
|
||||||
// Next time period
|
|
||||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
|
||||||
ps.setLong(3, inNext.getTimePeriod());
|
|
||||||
ps.setBytes(4, inNext.getTagKey().getBytes());
|
|
||||||
ps.setBytes(5, inNext.getHeaderKey().getBytes());
|
|
||||||
ps.setLong(6, inNext.getWindowBase());
|
|
||||||
ps.setBytes(7, inNext.getWindowBitmap());
|
|
||||||
ps.setInt(8, OFFSET_NEXT);
|
|
||||||
ps.addBatch();
|
|
||||||
int[] batchAffected = ps.executeBatch();
|
|
||||||
if (batchAffected.length != 3) throw new DbStateException();
|
|
||||||
for (int rows : batchAffected)
|
|
||||||
if (rows != 1) throw new DbStateException();
|
|
||||||
ps.close();
|
|
||||||
return keySetId;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
tryToClose(rs, LOG, WARNING);
|
|
||||||
tryToClose(ps, LOG, WARNING);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addIdentity(Connection txn, Identity i) throws DbException {
|
public void addIdentity(Connection txn, Identity i) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -887,12 +756,12 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setBytes(1, local.getId().getBytes());
|
ps.setBytes(1, local.getId().getBytes());
|
||||||
ps.setInt(2, local.getFormatVersion());
|
ps.setInt(2, local.getFormatVersion());
|
||||||
ps.setString(3, local.getName());
|
ps.setString(3, local.getName());
|
||||||
ps.setBytes(4, local.getPublicKey());
|
ps.setBytes(4, local.getPublicKey().getEncoded());
|
||||||
ps.setBytes(5, local.getPrivateKey());
|
ps.setBytes(5, local.getPrivateKey().getEncoded());
|
||||||
if (i.getHandshakePublicKey() == null) ps.setNull(6, BINARY);
|
if (i.getHandshakePublicKey() == null) ps.setNull(6, BINARY);
|
||||||
else ps.setBytes(6, i.getHandshakePublicKey());
|
else ps.setBytes(6, i.getHandshakePublicKey().getEncoded());
|
||||||
if (i.getHandshakePrivateKey() == null) ps.setNull(7, BINARY);
|
if (i.getHandshakePrivateKey() == null) ps.setNull(7, BINARY);
|
||||||
else ps.setBytes(7, i.getHandshakePrivateKey());
|
else ps.setBytes(7, i.getHandshakePrivateKey().getEncoded());
|
||||||
ps.setLong(8, i.getTimeCreated());
|
ps.setLong(8, i.getTimeCreated());
|
||||||
int affected = ps.executeUpdate();
|
int affected = ps.executeUpdate();
|
||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
@@ -1068,7 +937,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " VALUES (?, ?, ?, ?, ?)";
|
+ " VALUES (?, ?, ?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, p.getId().getBytes());
|
ps.setBytes(1, p.getId().getBytes());
|
||||||
ps.setBytes(2, p.getPublicKey());
|
ps.setBytes(2, p.getPublicKey().getEncoded());
|
||||||
ps.setString(3, p.getAlias());
|
ps.setString(3, p.getAlias());
|
||||||
ps.setInt(4, p.getState().getValue());
|
ps.setInt(4, p.getState().getValue());
|
||||||
ps.setLong(5, p.getTimestamp());
|
ps.setLong(5, p.getTimestamp());
|
||||||
@@ -1101,24 +970,47 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportKeySetId addTransportKeys(Connection txn, ContactId c,
|
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,
|
||||||
TransportKeys k) throws DbException {
|
TransportKeys k) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
// Store the outgoing keys
|
// Store the outgoing keys
|
||||||
String sql = "INSERT INTO outgoingKeys (contactId, transportId,"
|
String sql = "INSERT INTO outgoingKeys (transportId, timePeriod,"
|
||||||
+ " timePeriod, tagKey, headerKey, stream, active)"
|
+ " contactId, pendingContactId, tagKey, headerKey,"
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
+ " stream, active, rootKey, alice)"
|
||||||
|
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setString(1, k.getTransportId().getString());
|
||||||
ps.setString(2, 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());
|
||||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||||
ps.setLong(3, outCurr.getTimePeriod());
|
ps.setBytes(5, outCurr.getTagKey().getBytes());
|
||||||
ps.setBytes(4, outCurr.getTagKey().getBytes());
|
ps.setBytes(6, outCurr.getHeaderKey().getBytes());
|
||||||
ps.setBytes(5, outCurr.getHeaderKey().getBytes());
|
ps.setLong(7, outCurr.getStreamCounter());
|
||||||
ps.setLong(6, outCurr.getStreamCounter());
|
ps.setBoolean(8, outCurr.isActive());
|
||||||
ps.setBoolean(7, 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);
|
||||||
|
}
|
||||||
int affected = ps.executeUpdate();
|
int affected = ps.executeUpdate();
|
||||||
if (affected != 1) throw new DbStateException();
|
if (affected != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1128,18 +1020,18 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
if (!rs.next()) throw new DbStateException();
|
if (!rs.next()) throw new DbStateException();
|
||||||
TransportKeySetId keySetId = new TransportKeySetId(rs.getInt(1));
|
KeySetId keySetId = new KeySetId(rs.getInt(1));
|
||||||
if (rs.next()) throw new DbStateException();
|
if (rs.next()) throw new DbStateException();
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
// Store the incoming keys
|
// Store the incoming keys
|
||||||
sql = "INSERT INTO incomingKeys (keySetId, transportId,"
|
sql = "INSERT INTO incomingKeys (transportId, keySetId,"
|
||||||
+ " timePeriod, tagKey, headerKey, base, bitmap,"
|
+ " timePeriod, tagKey, headerKey, base, bitmap,"
|
||||||
+ " periodOffset)"
|
+ " periodOffset)"
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, keySetId.getInt());
|
ps.setString(1, k.getTransportId().getString());
|
||||||
ps.setString(2, k.getTransportId().getString());
|
ps.setInt(2, keySetId.getInt());
|
||||||
// Previous time period
|
// Previous time period
|
||||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||||
ps.setLong(3, inPrev.getTimePeriod());
|
ps.setLong(3, inPrev.getTimePeriod());
|
||||||
@@ -1444,14 +1336,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
int formatVersion = rs.getInt(2);
|
int formatVersion = rs.getInt(2);
|
||||||
String name = rs.getString(3);
|
String name = rs.getString(3);
|
||||||
String alias = rs.getString(4);
|
String alias = rs.getString(4);
|
||||||
byte[] publicKey = rs.getBytes(5);
|
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(5));
|
||||||
byte[] handshakePublicKey = rs.getBytes(6);
|
byte[] handshakePub = rs.getBytes(6);
|
||||||
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
|
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
|
||||||
boolean verified = rs.getBoolean(8);
|
boolean verified = rs.getBoolean(8);
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
Author author =
|
Author author =
|
||||||
new Author(authorId, formatVersion, name, publicKey);
|
new Author(authorId, formatVersion, name, publicKey);
|
||||||
|
PublicKey handshakePublicKey = handshakePub == null ?
|
||||||
|
null : new AgreementPublicKey(handshakePub);
|
||||||
return new Contact(c, author, localAuthorId, alias,
|
return new Contact(c, author, localAuthorId, alias,
|
||||||
handshakePublicKey, verified);
|
handshakePublicKey, verified);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -1479,12 +1373,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
int formatVersion = rs.getInt(3);
|
int formatVersion = rs.getInt(3);
|
||||||
String name = rs.getString(4);
|
String name = rs.getString(4);
|
||||||
String alias = rs.getString(5);
|
String alias = rs.getString(5);
|
||||||
byte[] publicKey = rs.getBytes(6);
|
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(6));
|
||||||
byte[] handshakePublicKey = rs.getBytes(7);
|
byte[] handshakePub = rs.getBytes(7);
|
||||||
AuthorId localAuthorId = new AuthorId(rs.getBytes(8));
|
AuthorId localAuthorId = new AuthorId(rs.getBytes(8));
|
||||||
boolean verified = rs.getBoolean(9);
|
boolean verified = rs.getBoolean(9);
|
||||||
Author author =
|
Author author =
|
||||||
new Author(authorId, formatVersion, name, publicKey);
|
new Author(authorId, formatVersion, name, publicKey);
|
||||||
|
PublicKey handshakePublicKey = handshakePub == null ?
|
||||||
|
null : new AgreementPublicKey(handshakePub);
|
||||||
contacts.add(new Contact(contactId, author, localAuthorId,
|
contacts.add(new Contact(contactId, author, localAuthorId,
|
||||||
alias, handshakePublicKey, verified));
|
alias, handshakePublicKey, verified));
|
||||||
}
|
}
|
||||||
@@ -1540,12 +1436,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
int formatVersion = rs.getInt(2);
|
int formatVersion = rs.getInt(2);
|
||||||
String name = rs.getString(3);
|
String name = rs.getString(3);
|
||||||
String alias = rs.getString(4);
|
String alias = rs.getString(4);
|
||||||
byte[] publicKey = rs.getBytes(5);
|
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(5));
|
||||||
byte[] handshakePublicKey = rs.getBytes(6);
|
byte[] handshakePub = rs.getBytes(6);
|
||||||
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
|
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
|
||||||
boolean verified = rs.getBoolean(8);
|
boolean verified = rs.getBoolean(8);
|
||||||
Author author =
|
Author author =
|
||||||
new Author(remote, formatVersion, name, publicKey);
|
new Author(remote, formatVersion, name, publicKey);
|
||||||
|
PublicKey handshakePublicKey = handshakePub == null ?
|
||||||
|
null : new AgreementPublicKey(handshakePub);
|
||||||
contacts.add(new Contact(contactId, author, localAuthorId,
|
contacts.add(new Contact(contactId, author, localAuthorId,
|
||||||
alias, handshakePublicKey, verified));
|
alias, handshakePublicKey, verified));
|
||||||
}
|
}
|
||||||
@@ -1661,86 +1559,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<HandshakeKeySet> getHandshakeKeys(Connection txn,
|
|
||||||
TransportId t) throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
|
||||||
ResultSet rs = null;
|
|
||||||
try {
|
|
||||||
// Retrieve the incoming keys
|
|
||||||
String sql = "SELECT timePeriod, tagKey, headerKey, base, bitmap"
|
|
||||||
+ " FROM incomingHandshakeKeys"
|
|
||||||
+ " WHERE transportId = ?"
|
|
||||||
+ " ORDER BY keySetId, periodOffset";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setString(1, t.getString());
|
|
||||||
rs = ps.executeQuery();
|
|
||||||
List<IncomingKeys> inKeys = new ArrayList<>();
|
|
||||||
while (rs.next()) {
|
|
||||||
long timePeriod = rs.getLong(1);
|
|
||||||
SecretKey tagKey = new SecretKey(rs.getBytes(2));
|
|
||||||
SecretKey headerKey = new SecretKey(rs.getBytes(3));
|
|
||||||
long windowBase = rs.getLong(4);
|
|
||||||
byte[] windowBitmap = rs.getBytes(5);
|
|
||||||
inKeys.add(new IncomingKeys(tagKey, headerKey, timePeriod,
|
|
||||||
windowBase, windowBitmap));
|
|
||||||
}
|
|
||||||
rs.close();
|
|
||||||
ps.close();
|
|
||||||
// Retrieve the outgoing keys in the same order
|
|
||||||
sql = "SELECT keySetId, contactId, pendingContactId, timePeriod,"
|
|
||||||
+ " tagKey, headerKey, rootKey, alice, stream"
|
|
||||||
+ " FROM outgoingHandshakeKeys"
|
|
||||||
+ " WHERE transportId = ?"
|
|
||||||
+ " ORDER BY keySetId";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setString(1, t.getString());
|
|
||||||
rs = ps.executeQuery();
|
|
||||||
Collection<HandshakeKeySet> keys = new ArrayList<>();
|
|
||||||
for (int i = 0; rs.next(); i++) {
|
|
||||||
// There should be three times as many incoming keys
|
|
||||||
if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
|
|
||||||
HandshakeKeySetId keySetId =
|
|
||||||
new HandshakeKeySetId(rs.getInt(1));
|
|
||||||
ContactId contactId = null;
|
|
||||||
int cId = rs.getInt(2);
|
|
||||||
if (!rs.wasNull()) contactId = new ContactId(cId);
|
|
||||||
PendingContactId pendingContactId = null;
|
|
||||||
byte[] pId = rs.getBytes(3);
|
|
||||||
if (!rs.wasNull()) pendingContactId = new PendingContactId(pId);
|
|
||||||
long timePeriod = rs.getLong(4);
|
|
||||||
SecretKey tagKey = new SecretKey(rs.getBytes(5));
|
|
||||||
SecretKey headerKey = new SecretKey(rs.getBytes(6));
|
|
||||||
SecretKey rootKey = new SecretKey(rs.getBytes(7));
|
|
||||||
boolean alice = rs.getBoolean(8);
|
|
||||||
long streamCounter = rs.getLong(9);
|
|
||||||
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
|
|
||||||
timePeriod, streamCounter, true);
|
|
||||||
IncomingKeys inPrev = inKeys.get(i * 3);
|
|
||||||
IncomingKeys inCurr = inKeys.get(i * 3 + 1);
|
|
||||||
IncomingKeys inNext = inKeys.get(i * 3 + 2);
|
|
||||||
HandshakeKeys handshakeKeys = new HandshakeKeys(t, inPrev,
|
|
||||||
inCurr, inNext, outCurr, rootKey, alice);
|
|
||||||
if (contactId == null) {
|
|
||||||
if (pendingContactId == null) throw new DbStateException();
|
|
||||||
keys.add(new HandshakeKeySet(keySetId, pendingContactId,
|
|
||||||
handshakeKeys));
|
|
||||||
} else {
|
|
||||||
if (pendingContactId != null) throw new DbStateException();
|
|
||||||
keys.add(new HandshakeKeySet(keySetId, contactId,
|
|
||||||
handshakeKeys));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rs.close();
|
|
||||||
ps.close();
|
|
||||||
return keys;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
tryToClose(rs, LOG, WARNING);
|
|
||||||
tryToClose(ps, LOG, WARNING);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Identity getIdentity(Connection txn, AuthorId a) throws DbException {
|
public Identity getIdentity(Connection txn, AuthorId a) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -1756,16 +1574,20 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (!rs.next()) throw new DbStateException();
|
if (!rs.next()) throw new DbStateException();
|
||||||
int formatVersion = rs.getInt(1);
|
int formatVersion = rs.getInt(1);
|
||||||
String name = rs.getString(2);
|
String name = rs.getString(2);
|
||||||
byte[] publicKey = rs.getBytes(3);
|
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(3));
|
||||||
byte[] privateKey = rs.getBytes(4);
|
PrivateKey privateKey = new SignaturePrivateKey(rs.getBytes(4));
|
||||||
byte[] handshakePublicKey = rs.getBytes(5);
|
byte[] handshakePub = rs.getBytes(5);
|
||||||
byte[] handshakePrivateKey = rs.getBytes(6);
|
byte[] handshakePriv = rs.getBytes(6);
|
||||||
long created = rs.getLong(7);
|
long created = rs.getLong(7);
|
||||||
if (rs.next()) throw new DbStateException();
|
if (rs.next()) throw new DbStateException();
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
LocalAuthor local = new LocalAuthor(a, formatVersion, name,
|
LocalAuthor local = new LocalAuthor(a, formatVersion, name,
|
||||||
publicKey, privateKey);
|
publicKey, privateKey);
|
||||||
|
PublicKey handshakePublicKey = handshakePub == null ?
|
||||||
|
null : new AgreementPublicKey(handshakePub);
|
||||||
|
PrivateKey handshakePrivateKey = handshakePriv == null ?
|
||||||
|
null : new AgreementPrivateKey(handshakePriv);
|
||||||
return new Identity(local, handshakePublicKey, handshakePrivateKey,
|
return new Identity(local, handshakePublicKey, handshakePrivateKey,
|
||||||
created);
|
created);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -1792,13 +1614,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
AuthorId authorId = new AuthorId(rs.getBytes(1));
|
AuthorId authorId = new AuthorId(rs.getBytes(1));
|
||||||
int formatVersion = rs.getInt(2);
|
int formatVersion = rs.getInt(2);
|
||||||
String name = rs.getString(3);
|
String name = rs.getString(3);
|
||||||
byte[] publicKey = rs.getBytes(4);
|
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(4));
|
||||||
byte[] privateKey = rs.getBytes(5);
|
PrivateKey privateKey = new SignaturePrivateKey(rs.getBytes(5));
|
||||||
byte[] handshakePublicKey = rs.getBytes(6);
|
byte[] handshakePub = rs.getBytes(6);
|
||||||
byte[] handshakePrivateKey = rs.getBytes(7);
|
byte[] handshakePriv = rs.getBytes(7);
|
||||||
long created = rs.getLong(8);
|
long created = rs.getLong(8);
|
||||||
LocalAuthor local = new LocalAuthor(authorId, formatVersion,
|
LocalAuthor local = new LocalAuthor(authorId, formatVersion,
|
||||||
name, publicKey, privateKey);
|
name, publicKey, privateKey);
|
||||||
|
PublicKey handshakePublicKey = handshakePub == null ?
|
||||||
|
null : new AgreementPublicKey(handshakePub);
|
||||||
|
PrivateKey handshakePrivateKey = handshakePriv == null ?
|
||||||
|
null : new AgreementPrivateKey(handshakePriv);
|
||||||
identities.add(new Identity(local, handshakePublicKey,
|
identities.add(new Identity(local, handshakePublicKey,
|
||||||
handshakePrivateKey, created));
|
handshakePrivateKey, created));
|
||||||
}
|
}
|
||||||
@@ -2395,7 +2221,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
List<PendingContact> pendingContacts = new ArrayList<>();
|
List<PendingContact> pendingContacts = new ArrayList<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
PendingContactId id = new PendingContactId(rs.getBytes(1));
|
PendingContactId id = new PendingContactId(rs.getBytes(1));
|
||||||
byte[] publicKey = rs.getBytes(2);
|
PublicKey publicKey = new AgreementPublicKey(rs.getBytes(2));
|
||||||
String alias = rs.getString(3);
|
String alias = rs.getString(3);
|
||||||
PendingContactState state =
|
PendingContactState state =
|
||||||
PendingContactState.fromValue(rs.getInt(4));
|
PendingContactState.fromValue(rs.getInt(4));
|
||||||
@@ -2502,8 +2328,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
// Retrieve the outgoing keys in the same order
|
// Retrieve the outgoing keys in the same order
|
||||||
sql = "SELECT keySetId, contactId, timePeriod,"
|
sql = "SELECT keySetId, timePeriod, contactId, pendingContactId,"
|
||||||
+ " tagKey, headerKey, stream, active"
|
+ " tagKey, headerKey, stream, active, rootKey, alice"
|
||||||
+ " FROM outgoingKeys"
|
+ " FROM outgoingKeys"
|
||||||
+ " WHERE transportId = ?"
|
+ " WHERE transportId = ?"
|
||||||
+ " ORDER BY keySetId";
|
+ " ORDER BY keySetId";
|
||||||
@@ -2514,23 +2340,34 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
for (int i = 0; rs.next(); i++) {
|
for (int i = 0; rs.next(); i++) {
|
||||||
// There should be three times as many incoming keys
|
// There should be three times as many incoming keys
|
||||||
if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
|
if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
|
||||||
TransportKeySetId keySetId =
|
KeySetId keySetId = new KeySetId(rs.getInt(1));
|
||||||
new TransportKeySetId(rs.getInt(1));
|
long timePeriod = rs.getLong(2);
|
||||||
ContactId contactId = new ContactId(rs.getInt(2));
|
int cId = rs.getInt(3);
|
||||||
long timePeriod = rs.getLong(3);
|
ContactId contactId = rs.wasNull() ? null : new ContactId(cId);
|
||||||
SecretKey tagKey = new SecretKey(rs.getBytes(4));
|
byte[] pId = rs.getBytes(4);
|
||||||
SecretKey headerKey = new SecretKey(rs.getBytes(5));
|
PendingContactId pendingContactId = pId == null ?
|
||||||
long streamCounter = rs.getLong(6);
|
null : new PendingContactId(pId);
|
||||||
boolean active = rs.getBoolean(7);
|
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);
|
||||||
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
|
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
|
||||||
timePeriod, streamCounter, active);
|
timePeriod, streamCounter, active);
|
||||||
IncomingKeys inPrev = inKeys.get(i * 3);
|
IncomingKeys inPrev = inKeys.get(i * 3);
|
||||||
IncomingKeys inCurr = inKeys.get(i * 3 + 1);
|
IncomingKeys inCurr = inKeys.get(i * 3 + 1);
|
||||||
IncomingKeys inNext = inKeys.get(i * 3 + 2);
|
IncomingKeys inNext = inKeys.get(i * 3 + 2);
|
||||||
TransportKeys transportKeys = new TransportKeys(t, inPrev,
|
TransportKeys transportKeys;
|
||||||
inCurr, inNext, outCurr);
|
if (rootKey == null) {
|
||||||
|
transportKeys = new TransportKeys(t, inPrev, inCurr,
|
||||||
|
inNext, outCurr);
|
||||||
|
} else {
|
||||||
|
transportKeys = new TransportKeys(t, inPrev, inCurr,
|
||||||
|
inNext, outCurr, new SecretKey(rootKey), alice);
|
||||||
|
}
|
||||||
keys.add(new TransportKeySet(keySetId, contactId,
|
keys.add(new TransportKeySet(keySetId, contactId,
|
||||||
transportKeys));
|
pendingContactId, transportKeys));
|
||||||
}
|
}
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -2544,26 +2381,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void incrementStreamCounter(Connection txn, TransportId t,
|
public void incrementStreamCounter(Connection txn, TransportId t,
|
||||||
HandshakeKeySetId k) throws DbException {
|
KeySetId k) throws DbException {
|
||||||
PreparedStatement ps = null;
|
|
||||||
try {
|
|
||||||
String sql = "UPDATE outgoingHandshakeKeys SET stream = stream + 1"
|
|
||||||
+ " WHERE transportId = ? AND keySetId = ?";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setString(1, t.getString());
|
|
||||||
ps.setInt(2, k.getInt());
|
|
||||||
int affected = ps.executeUpdate();
|
|
||||||
if (affected != 1) throw new DbStateException();
|
|
||||||
ps.close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
tryToClose(ps, LOG, WARNING);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void incrementStreamCounter(Connection txn, TransportId t,
|
|
||||||
TransportKeySetId k) throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
String sql = "UPDATE outgoingKeys SET stream = stream + 1"
|
String sql = "UPDATE outgoingKeys SET stream = stream + 1"
|
||||||
@@ -2941,27 +2759,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeHandshakeKeys(Connection txn, TransportId t,
|
|
||||||
HandshakeKeySetId k) throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
|
||||||
try {
|
|
||||||
// Delete any existing outgoing keys - this will also remove any
|
|
||||||
// incoming keys with the same key set ID
|
|
||||||
String sql = "DELETE FROM outgoingHandshakeKeys"
|
|
||||||
+ " WHERE transportId = ? AND keySetId = ?";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setString(1, t.getString());
|
|
||||||
ps.setInt(2, k.getInt());
|
|
||||||
int affected = ps.executeUpdate();
|
|
||||||
if (affected < 0) throw new DbStateException();
|
|
||||||
ps.close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
tryToClose(ps, LOG, WARNING);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeIdentity(Connection txn, AuthorId a) throws DbException {
|
public void removeIdentity(Connection txn, AuthorId a) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -3074,8 +2871,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeTransportKeys(Connection txn, TransportId t,
|
public void removeTransportKeys(Connection txn, TransportId t, KeySetId k)
|
||||||
TransportKeySetId k) throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
// Delete any existing outgoing keys - this will also remove any
|
// Delete any existing outgoing keys - this will also remove any
|
||||||
@@ -3182,15 +2979,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHandshakeKeyPair(Connection txn, AuthorId local,
|
public void setHandshakeKeyPair(Connection txn, AuthorId local,
|
||||||
byte[] publicKey, byte[] privateKey) throws DbException {
|
PublicKey publicKey, PrivateKey privateKey) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
String sql = "UPDATE localAuthors"
|
String sql = "UPDATE localAuthors"
|
||||||
+ " SET handshakePublicKey = ?, handshakePrivateKey = ?"
|
+ " SET handshakePublicKey = ?, handshakePrivateKey = ?"
|
||||||
+ " WHERE authorId = ?";
|
+ " WHERE authorId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, publicKey);
|
ps.setBytes(1, publicKey.getEncoded());
|
||||||
ps.setBytes(2, privateKey);
|
ps.setBytes(2, privateKey.getEncoded());
|
||||||
ps.setBytes(3, local.getBytes());
|
ps.setBytes(3, local.getBytes());
|
||||||
int affected = ps.executeUpdate();
|
int affected = ps.executeUpdate();
|
||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
@@ -3291,7 +3088,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setInt(1, state.getValue());
|
ps.setInt(1, state.getValue());
|
||||||
ps.setBytes(2, p.getBytes());
|
ps.setBytes(2, p.getBytes());
|
||||||
int affected = ps.executeUpdate();
|
int affected = ps.executeUpdate();
|
||||||
if (affected != 1) throw new DbStateException();
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps, LOG, WARNING);
|
||||||
@@ -3300,7 +3097,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setReorderingWindow(Connection txn, TransportKeySetId k,
|
public void setReorderingWindow(Connection txn, KeySetId k,
|
||||||
TransportId t, long timePeriod, long base, byte[] bitmap)
|
TransportId t, long timePeriod, long base, byte[] bitmap)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -3323,33 +3120,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setReorderingWindow(Connection txn, HandshakeKeySetId k,
|
|
||||||
TransportId t, long timePeriod, long base, byte[] bitmap)
|
|
||||||
throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
|
||||||
try {
|
|
||||||
String sql = "UPDATE incomingHandshakeKeys SET base = ?, bitmap = ?"
|
|
||||||
+ " WHERE transportId = ? AND keySetId = ?"
|
|
||||||
+ " AND timePeriod = ?";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setLong(1, base);
|
|
||||||
ps.setBytes(2, bitmap);
|
|
||||||
ps.setString(3, t.getString());
|
|
||||||
ps.setInt(4, k.getInt());
|
|
||||||
ps.setLong(5, timePeriod);
|
|
||||||
int affected = ps.executeUpdate();
|
|
||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
|
||||||
ps.close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
tryToClose(ps, LOG, WARNING);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTransportKeysActive(Connection txn, TransportId t,
|
public void setTransportKeysActive(Connection txn, TransportId t,
|
||||||
TransportKeySetId k) throws DbException {
|
KeySetId k) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
String sql = "UPDATE outgoingKeys SET active = true"
|
String sql = "UPDATE outgoingKeys SET active = true"
|
||||||
@@ -3469,71 +3242,4 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateHandshakeKeys(Connection txn, HandshakeKeySet ks)
|
|
||||||
throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
|
||||||
try {
|
|
||||||
// Update the outgoing keys
|
|
||||||
String sql = "UPDATE outgoingHandshakeKeys SET timePeriod = ?,"
|
|
||||||
+ " tagKey = ?, headerKey = ?, stream = ?"
|
|
||||||
+ " WHERE transportId = ? AND keySetId = ?";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
HandshakeKeys k = ks.getKeys();
|
|
||||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
|
||||||
ps.setLong(1, outCurr.getTimePeriod());
|
|
||||||
ps.setBytes(2, outCurr.getTagKey().getBytes());
|
|
||||||
ps.setBytes(3, outCurr.getHeaderKey().getBytes());
|
|
||||||
ps.setLong(4, outCurr.getStreamCounter());
|
|
||||||
ps.setString(5, k.getTransportId().getString());
|
|
||||||
ps.setInt(6, ks.getKeySetId().getInt());
|
|
||||||
int affected = ps.executeUpdate();
|
|
||||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
|
||||||
ps.close();
|
|
||||||
// Update the incoming keys
|
|
||||||
sql = "UPDATE incomingHandshakeKeys SET timePeriod = ?,"
|
|
||||||
+ " tagKey = ?, headerKey = ?, base = ?, bitmap = ?"
|
|
||||||
+ " WHERE transportId = ? AND keySetId = ?"
|
|
||||||
+ " AND periodOffset = ?";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setString(6, k.getTransportId().getString());
|
|
||||||
ps.setInt(7, ks.getKeySetId().getInt());
|
|
||||||
// Previous time period
|
|
||||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
|
||||||
ps.setLong(1, inPrev.getTimePeriod());
|
|
||||||
ps.setBytes(2, inPrev.getTagKey().getBytes());
|
|
||||||
ps.setBytes(3, inPrev.getHeaderKey().getBytes());
|
|
||||||
ps.setLong(4, inPrev.getWindowBase());
|
|
||||||
ps.setBytes(5, inPrev.getWindowBitmap());
|
|
||||||
ps.setInt(8, OFFSET_PREV);
|
|
||||||
ps.addBatch();
|
|
||||||
// Current time period
|
|
||||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
|
||||||
ps.setLong(1, inCurr.getTimePeriod());
|
|
||||||
ps.setBytes(2, inCurr.getTagKey().getBytes());
|
|
||||||
ps.setBytes(3, inCurr.getHeaderKey().getBytes());
|
|
||||||
ps.setLong(4, inCurr.getWindowBase());
|
|
||||||
ps.setBytes(5, inCurr.getWindowBitmap());
|
|
||||||
ps.setInt(8, OFFSET_CURR);
|
|
||||||
ps.addBatch();
|
|
||||||
// Next time period
|
|
||||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
|
||||||
ps.setLong(1, inNext.getTimePeriod());
|
|
||||||
ps.setBytes(2, inNext.getTagKey().getBytes());
|
|
||||||
ps.setBytes(3, inNext.getHeaderKey().getBytes());
|
|
||||||
ps.setLong(4, inNext.getWindowBase());
|
|
||||||
ps.setBytes(5, inNext.getWindowBitmap());
|
|
||||||
ps.setInt(8, OFFSET_NEXT);
|
|
||||||
ps.addBatch();
|
|
||||||
int[] batchAffected = ps.executeBatch();
|
|
||||||
if (batchAffected.length != 3) throw new DbStateException();
|
|
||||||
for (int rows : batchAffected)
|
|
||||||
if (rows < 0 || rows > 1) throw new DbStateException();
|
|
||||||
ps.close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
tryToClose(ps, LOG, WARNING);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,13 +2,13 @@ package org.briarproject.bramble.identity;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -16,6 +16,8 @@ import javax.inject.Inject;
|
|||||||
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorId.LABEL;
|
import static org.briarproject.bramble.api.identity.AuthorId.LABEL;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.writeUint32;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -29,13 +31,13 @@ class AuthorFactoryImpl implements AuthorFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Author createAuthor(String name, byte[] publicKey) {
|
public Author createAuthor(String name, PublicKey publicKey) {
|
||||||
return createAuthor(FORMAT_VERSION, name, publicKey);
|
return createAuthor(FORMAT_VERSION, name, publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Author createAuthor(int formatVersion, String name,
|
public Author createAuthor(int formatVersion, String name,
|
||||||
byte[] publicKey) {
|
PublicKey publicKey) {
|
||||||
AuthorId id = getId(formatVersion, name, publicKey);
|
AuthorId id = getId(formatVersion, name, publicKey);
|
||||||
return new Author(id, formatVersion, name, publicKey);
|
return new Author(id, formatVersion, name, publicKey);
|
||||||
}
|
}
|
||||||
@@ -43,16 +45,17 @@ class AuthorFactoryImpl implements AuthorFactory {
|
|||||||
@Override
|
@Override
|
||||||
public LocalAuthor createLocalAuthor(String name) {
|
public LocalAuthor createLocalAuthor(String name) {
|
||||||
KeyPair signatureKeyPair = crypto.generateSignatureKeyPair();
|
KeyPair signatureKeyPair = crypto.generateSignatureKeyPair();
|
||||||
byte[] publicKey = signatureKeyPair.getPublic().getEncoded();
|
PublicKey publicKey = signatureKeyPair.getPublic();
|
||||||
byte[] privateKey = signatureKeyPair.getPrivate().getEncoded();
|
PrivateKey privateKey = signatureKeyPair.getPrivate();
|
||||||
AuthorId id = getId(FORMAT_VERSION, name, publicKey);
|
AuthorId id = getId(FORMAT_VERSION, name, publicKey);
|
||||||
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
|
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthorId getId(int formatVersion, String name, byte[] publicKey) {
|
private AuthorId getId(int formatVersion, String name,
|
||||||
|
PublicKey publicKey) {
|
||||||
byte[] formatVersionBytes = new byte[INT_32_BYTES];
|
byte[] formatVersionBytes = new byte[INT_32_BYTES];
|
||||||
ByteUtils.writeUint32(formatVersion, formatVersionBytes, 0);
|
writeUint32(formatVersion, formatVersionBytes, 0);
|
||||||
return new AuthorId(crypto.hash(LABEL, formatVersionBytes,
|
return new AuthorId(crypto.hash(LABEL, formatVersionBytes,
|
||||||
StringUtils.toUtf8(name), publicKey));
|
toUtf8(name), publicKey.getEncoded()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package org.briarproject.bramble.identity;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
@@ -73,8 +75,8 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
|
|||||||
long start = now();
|
long start = now();
|
||||||
LocalAuthor localAuthor = authorFactory.createLocalAuthor(name);
|
LocalAuthor localAuthor = authorFactory.createLocalAuthor(name);
|
||||||
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
|
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] handshakePub = handshakeKeyPair.getPublic().getEncoded();
|
PublicKey handshakePub = handshakeKeyPair.getPublic();
|
||||||
byte[] handshakePriv = handshakeKeyPair.getPrivate().getEncoded();
|
PrivateKey handshakePriv = handshakeKeyPair.getPrivate();
|
||||||
logDuration(LOG, "Creating identity", start);
|
logDuration(LOG, "Creating identity", start);
|
||||||
return new Identity(localAuthor, handshakePub, handshakePriv,
|
return new Identity(localAuthor, handshakePub, handshakePriv,
|
||||||
clock.currentTimeMillis());
|
clock.currentTimeMillis());
|
||||||
@@ -98,9 +100,9 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
|
|||||||
} else if (shouldStoreKeys) {
|
} else if (shouldStoreKeys) {
|
||||||
// Handshake keys were generated when loading the identity -
|
// Handshake keys were generated when loading the identity -
|
||||||
// store them
|
// store them
|
||||||
byte[] handshakePub =
|
PublicKey handshakePub =
|
||||||
requireNonNull(cached.getHandshakePublicKey());
|
requireNonNull(cached.getHandshakePublicKey());
|
||||||
byte[] handshakePriv =
|
PrivateKey handshakePriv =
|
||||||
requireNonNull(cached.getHandshakePrivateKey());
|
requireNonNull(cached.getHandshakePrivateKey());
|
||||||
db.setHandshakeKeyPair(txn, cached.getId(), handshakePub,
|
db.setHandshakeKeyPair(txn, cached.getId(), handshakePub,
|
||||||
handshakePriv);
|
handshakePriv);
|
||||||
@@ -122,12 +124,12 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[][] getHandshakeKeys(Transaction txn) throws DbException {
|
public KeyPair getHandshakeKeys(Transaction txn) throws DbException {
|
||||||
Identity cached = getCachedIdentity(txn);
|
Identity cached = getCachedIdentity(txn);
|
||||||
return new byte[][] {
|
PublicKey handshakePub = requireNonNull(cached.getHandshakePublicKey());
|
||||||
cached.getHandshakePublicKey(),
|
PrivateKey handshakePriv =
|
||||||
cached.getHandshakePrivateKey()
|
requireNonNull(cached.getHandshakePrivateKey());
|
||||||
};
|
return new KeyPair(handshakePub, handshakePriv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,8 +161,8 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
|
|||||||
LOG.info("Identity loaded");
|
LOG.info("Identity loaded");
|
||||||
if (i.hasHandshakeKeyPair()) return i;
|
if (i.hasHandshakeKeyPair()) return i;
|
||||||
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
|
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] handshakePub = handshakeKeyPair.getPublic().getEncoded();
|
PublicKey handshakePub = handshakeKeyPair.getPublic();
|
||||||
byte[] handshakePriv = handshakeKeyPair.getPrivate().getEncoded();
|
PrivateKey handshakePriv = handshakeKeyPair.getPrivate();
|
||||||
LOG.info("Handshake key pair generated");
|
LOG.info("Handshake key pair generated");
|
||||||
shouldStoreKeys = true;
|
shouldStoreKeys = true;
|
||||||
return new Identity(i.getLocalAuthor(), handshakePub, handshakePriv,
|
return new Identity(i.getLocalAuthor(), handshakePub, handshakePriv,
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
|||||||
TransportConnectionReader r) throws IOException {
|
TransportConnectionReader r) throws IOException {
|
||||||
InputStream streamReader = streamReaderFactory.createStreamReader(
|
InputStream streamReader = streamReaderFactory.createStreamReader(
|
||||||
r.getInputStream(), ctx);
|
r.getInputStream(), ctx);
|
||||||
|
// TODO: Pending contacts, handshake mode
|
||||||
return syncSessionFactory.createIncomingSession(ctx.getContactId(),
|
return syncSessionFactory.createIncomingSession(ctx.getContactId(),
|
||||||
streamReader);
|
streamReader);
|
||||||
}
|
}
|
||||||
@@ -104,6 +105,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
|||||||
TransportConnectionWriter w) throws IOException {
|
TransportConnectionWriter w) throws IOException {
|
||||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||||
w.getOutputStream(), ctx);
|
w.getOutputStream(), ctx);
|
||||||
|
// TODO: Pending contacts, handshake mode
|
||||||
return syncSessionFactory.createSimplexOutgoingSession(
|
return syncSessionFactory.createSimplexOutgoingSession(
|
||||||
ctx.getContactId(), w.getMaxLatency(), streamWriter);
|
ctx.getContactId(), w.getMaxLatency(), streamWriter);
|
||||||
}
|
}
|
||||||
@@ -112,6 +114,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
|||||||
TransportConnectionWriter w) throws IOException {
|
TransportConnectionWriter w) throws IOException {
|
||||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||||
w.getOutputStream(), ctx);
|
w.getOutputStream(), ctx);
|
||||||
|
// TODO: Pending contacts, handshake mode
|
||||||
return syncSessionFactory.createDuplexOutgoingSession(
|
return syncSessionFactory.createDuplexOutgoingSession(
|
||||||
ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(),
|
ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(),
|
||||||
streamWriter);
|
streamWriter);
|
||||||
@@ -145,6 +148,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
|||||||
disposeReader(false, false);
|
disposeReader(false, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// TODO: Pending contacts
|
||||||
ContactId contactId = ctx.getContactId();
|
ContactId contactId = ctx.getContactId();
|
||||||
connectionRegistry.registerConnection(contactId, transportId, true);
|
connectionRegistry.registerConnection(contactId, transportId, true);
|
||||||
try {
|
try {
|
||||||
@@ -388,7 +392,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Check that the stream comes from the expected contact
|
// Check that the stream comes from the expected contact
|
||||||
if (!ctx.getContactId().equals(contactId)) {
|
if (!contactId.equals(ctx.getContactId())) {
|
||||||
LOG.warning("Wrong contact ID for returning stream");
|
LOG.warning("Wrong contact ID for returning stream");
|
||||||
disposeReader(true, true);
|
disposeReader(true, true);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -17,8 +18,8 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -28,6 +29,7 @@ import java.util.concurrent.Executor;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -88,29 +90,53 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<TransportId, TransportKeySetId> addContact(Transaction txn,
|
public Map<TransportId, KeySetId> addContactWithRotationKeys(
|
||||||
ContactId c, SecretKey rootKey, long timestamp, boolean alice,
|
Transaction txn, ContactId c, SecretKey rootKey, long timestamp,
|
||||||
boolean active) throws DbException {
|
boolean alice, boolean active) throws DbException {
|
||||||
Map<TransportId, TransportKeySetId> ids = new HashMap<>();
|
Map<TransportId, KeySetId> ids = new HashMap<>();
|
||||||
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||||
TransportId t = e.getKey();
|
TransportId t = e.getKey();
|
||||||
TransportKeyManager m = e.getValue();
|
TransportKeyManager m = e.getValue();
|
||||||
ids.put(t, m.addContact(txn, c, rootKey, timestamp, alice, active));
|
ids.put(t, m.addContactWithRotationKeys(txn, c, rootKey, timestamp,
|
||||||
|
alice, active));
|
||||||
}
|
}
|
||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activateKeys(Transaction txn, Map<TransportId,
|
public Map<TransportId, KeySetId> addContactWithHandshakeKeys(
|
||||||
TransportKeySetId> keys) throws DbException {
|
Transaction txn, ContactId c, SecretKey rootKey, boolean alice)
|
||||||
for (Entry<TransportId, TransportKeySetId> e : keys.entrySet()) {
|
throws DbException {
|
||||||
|
Map<TransportId, KeySetId> ids = new HashMap<>();
|
||||||
|
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||||
TransportId t = e.getKey();
|
TransportId t = e.getKey();
|
||||||
TransportKeyManager m = managers.get(t);
|
TransportKeyManager m = e.getValue();
|
||||||
if (m == null) {
|
ids.put(t, m.addContactWithHandshakeKeys(txn, c, rootKey, alice));
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
}
|
||||||
} else {
|
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 -> {
|
||||||
m.activateKeys(txn, e.getValue());
|
m.activateKeys(txn, e.getValue());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,28 +146,34 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
|||||||
return m != null && m.canSendOutgoingStreams(c);
|
return m != null && m.canSendOutgoingStreams(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSendOutgoingStreams(PendingContactId p, TransportId t) {
|
||||||
|
TransportKeyManager m = managers.get(t);
|
||||||
|
return m != null && m.canSendOutgoingStreams(p);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamContext getStreamContext(ContactId c, TransportId t)
|
public StreamContext getStreamContext(ContactId c, TransportId t)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
TransportKeyManager m = managers.get(t);
|
return withManager(t, m ->
|
||||||
if (m == null) {
|
db.transactionWithNullableResult(false, txn ->
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
m.getStreamContext(txn, c)));
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
return db.transactionWithNullableResult(false, txn ->
|
@Override
|
||||||
m.getStreamContext(txn, c));
|
public StreamContext getStreamContext(PendingContactId p, TransportId t)
|
||||||
|
throws DbException {
|
||||||
|
return withManager(t, m ->
|
||||||
|
db.transactionWithNullableResult(false, txn ->
|
||||||
|
m.getStreamContext(txn, p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamContext getStreamContext(TransportId t, byte[] tag)
|
public StreamContext getStreamContext(TransportId t, byte[] tag)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
TransportKeyManager m = managers.get(t);
|
return withManager(t, m ->
|
||||||
if (m == null) {
|
db.transactionWithNullableResult(false, txn ->
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
m.getStreamContext(txn, tag)));
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return db.transactionWithNullableResult(false, txn ->
|
|
||||||
m.getStreamContext(txn, tag));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,4 +188,20 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
|||||||
for (TransportKeyManager m : managers.values()) m.removeContact(c);
|
for (TransportKeyManager m : managers.values()) m.removeContact(c);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private <T> T withManager(TransportId t, ManagerTask<T> task)
|
||||||
|
throws DbException {
|
||||||
|
TransportKeyManager m = managers.get(t);
|
||||||
|
if (m == null) {
|
||||||
|
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return task.run(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface ManagerTask<T> {
|
||||||
|
@Nullable
|
||||||
|
T run(TransportKeyManager m) throws DbException;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@@ -13,6 +15,9 @@ class MutableTransportKeys {
|
|||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final MutableIncomingKeys inPrev, inCurr, inNext;
|
private final MutableIncomingKeys inPrev, inCurr, inNext;
|
||||||
private final MutableOutgoingKeys outCurr;
|
private final MutableOutgoingKeys outCurr;
|
||||||
|
@Nullable
|
||||||
|
private final SecretKey rootKey;
|
||||||
|
private final boolean alice;
|
||||||
|
|
||||||
MutableTransportKeys(TransportKeys k) {
|
MutableTransportKeys(TransportKeys k) {
|
||||||
transportId = k.getTransportId();
|
transportId = k.getTransportId();
|
||||||
@@ -20,11 +25,24 @@ class MutableTransportKeys {
|
|||||||
inCurr = new MutableIncomingKeys(k.getCurrentIncomingKeys());
|
inCurr = new MutableIncomingKeys(k.getCurrentIncomingKeys());
|
||||||
inNext = new MutableIncomingKeys(k.getNextIncomingKeys());
|
inNext = new MutableIncomingKeys(k.getNextIncomingKeys());
|
||||||
outCurr = new MutableOutgoingKeys(k.getCurrentOutgoingKeys());
|
outCurr = new MutableOutgoingKeys(k.getCurrentOutgoingKeys());
|
||||||
|
if (k.isHandshakeMode()) {
|
||||||
|
rootKey = k.getRootKey();
|
||||||
|
alice = k.isAlice();
|
||||||
|
} else {
|
||||||
|
rootKey = null;
|
||||||
|
alice = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TransportKeys snapshot() {
|
TransportKeys snapshot() {
|
||||||
return new TransportKeys(transportId, inPrev.snapshot(),
|
if (rootKey == null) {
|
||||||
inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot());
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TransportId getTransportId() {
|
TransportId getTransportId() {
|
||||||
@@ -46,4 +64,18 @@ class MutableTransportKeys {
|
|||||||
MutableOutgoingKeys getCurrentOutgoingKeys() {
|
MutableOutgoingKeys getCurrentOutgoingKeys() {
|
||||||
return outCurr;
|
return outCurr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isHandshakeMode() {
|
||||||
|
return rootKey != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SecretKey getRootKey() {
|
||||||
|
if (rootKey == null) throw new UnsupportedOperationException();
|
||||||
|
return rootKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAlice() {
|
||||||
|
if (rootKey == null) throw new UnsupportedOperationException();
|
||||||
|
return alice;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -15,20 +16,34 @@ interface TransportKeyManager {
|
|||||||
|
|
||||||
void start(Transaction txn) throws DbException;
|
void start(Transaction txn) throws DbException;
|
||||||
|
|
||||||
TransportKeySetId addContact(Transaction txn, ContactId c,
|
KeySetId addContactWithRotationKeys(Transaction txn, ContactId c,
|
||||||
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
void activateKeys(Transaction txn, TransportKeySetId k) 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 removeContact(ContactId c);
|
void removeContact(ContactId c);
|
||||||
|
|
||||||
|
void removePendingContact(PendingContactId p);
|
||||||
|
|
||||||
boolean canSendOutgoingStreams(ContactId c);
|
boolean canSendOutgoingStreams(ContactId c);
|
||||||
|
|
||||||
|
boolean canSendOutgoingStreams(PendingContactId p);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
StreamContext getStreamContext(Transaction txn, ContactId c)
|
StreamContext getStreamContext(Transaction txn, ContactId c)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
StreamContext getStreamContext(Transaction txn, PendingContactId p)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
StreamContext getStreamContext(Transaction txn, byte[] tag)
|
StreamContext getStreamContext(Transaction txn, byte[] tag)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.transport;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -11,9 +12,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.Scheduler;
|
import org.briarproject.bramble.api.system.Scheduler;
|
||||||
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.transport.ReorderingWindow.Change;
|
import org.briarproject.bramble.transport.ReorderingWindow.Change;
|
||||||
|
|
||||||
@@ -28,10 +29,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
@@ -43,7 +48,7 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
|||||||
class TransportKeyManagerImpl implements TransportKeyManager {
|
class TransportKeyManagerImpl implements TransportKeyManager {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(TransportKeyManagerImpl.class.getName());
|
getLogger(TransportKeyManagerImpl.class.getName());
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final TransportCrypto transportCrypto;
|
private final TransportCrypto transportCrypto;
|
||||||
@@ -55,10 +60,16 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
// The following are locking: lock
|
@GuardedBy("lock")
|
||||||
private final Map<TransportKeySetId, MutableKeySet> keys = new HashMap<>();
|
private final Map<KeySetId, MutableTransportKeySet> keys = new HashMap<>();
|
||||||
|
@GuardedBy("lock")
|
||||||
private final Map<Bytes, TagContext> inContexts = new HashMap<>();
|
private final Map<Bytes, TagContext> inContexts = new HashMap<>();
|
||||||
private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>();
|
@GuardedBy("lock")
|
||||||
|
private final Map<ContactId, MutableTransportKeySet>
|
||||||
|
contactOutContexts = new HashMap<>();
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final Map<PendingContactId, MutableTransportKeySet>
|
||||||
|
pendingContactOutContexts = new HashMap<>();
|
||||||
|
|
||||||
TransportKeyManagerImpl(DatabaseComponent db,
|
TransportKeyManagerImpl(DatabaseComponent db,
|
||||||
TransportCrypto transportCrypto, Executor dbExecutor,
|
TransportCrypto transportCrypto, Executor dbExecutor,
|
||||||
@@ -82,62 +93,71 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
// Load the transport keys from the DB
|
// Load the transport keys from the DB
|
||||||
Collection<TransportKeySet> loaded =
|
Collection<TransportKeySet> loaded =
|
||||||
db.getTransportKeys(txn, transportId);
|
db.getTransportKeys(txn, transportId);
|
||||||
// Rotate the keys to the current time period
|
// Update the keys to the current time period
|
||||||
RotationResult rotationResult = rotateKeys(loaded, now);
|
UpdateResult updateResult = updateKeys(loaded, now);
|
||||||
// Initialise mutable state for all contacts
|
// Initialise mutable state for all contacts
|
||||||
addKeys(rotationResult.current);
|
addKeys(updateResult.current);
|
||||||
// Write any rotated keys back to the DB
|
// Write any updated keys back to the DB
|
||||||
if (!rotationResult.rotated.isEmpty())
|
if (!updateResult.updated.isEmpty())
|
||||||
db.updateTransportKeys(txn, rotationResult.rotated);
|
db.updateTransportKeys(txn, updateResult.updated);
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
// Schedule the next key rotation
|
// Schedule the next key update
|
||||||
scheduleKeyRotation(now);
|
scheduleKeyUpdate(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RotationResult rotateKeys(Collection<TransportKeySet> keys,
|
private UpdateResult updateKeys(Collection<TransportKeySet> keys,
|
||||||
long now) {
|
long now) {
|
||||||
RotationResult rotationResult = new RotationResult();
|
UpdateResult updateResult = new UpdateResult();
|
||||||
long timePeriod = now / timePeriodLength;
|
long timePeriod = now / timePeriodLength;
|
||||||
for (TransportKeySet ks : keys) {
|
for (TransportKeySet ks : keys) {
|
||||||
TransportKeys k = ks.getKeys();
|
TransportKeys k = ks.getKeys();
|
||||||
TransportKeys k1 = transportCrypto.rotateTransportKeys(k,
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k,
|
||||||
timePeriod);
|
timePeriod);
|
||||||
TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
|
TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
|
||||||
ks.getContactId(), k1);
|
ks.getContactId(), null, k1);
|
||||||
if (k1.getTimePeriod() > k.getTimePeriod())
|
if (k1.getTimePeriod() > k.getTimePeriod())
|
||||||
rotationResult.rotated.add(ks1);
|
updateResult.updated.add(ks1);
|
||||||
rotationResult.current.add(ks1);
|
updateResult.current.add(ks1);
|
||||||
}
|
}
|
||||||
return rotationResult;
|
return updateResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: lock
|
@GuardedBy("lock")
|
||||||
private void addKeys(Collection<TransportKeySet> keys) {
|
private void addKeys(Collection<TransportKeySet> keys) {
|
||||||
for (TransportKeySet ks : keys) {
|
for (TransportKeySet ks : keys) {
|
||||||
addKeys(ks.getKeySetId(), ks.getContactId(),
|
addKeys(ks.getKeySetId(), ks.getContactId(),
|
||||||
|
ks.getPendingContactId(),
|
||||||
new MutableTransportKeys(ks.getKeys()));
|
new MutableTransportKeys(ks.getKeys()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: lock
|
@GuardedBy("lock")
|
||||||
private void addKeys(TransportKeySetId keySetId, ContactId contactId,
|
private void addKeys(KeySetId keySetId, @Nullable ContactId contactId,
|
||||||
MutableTransportKeys m) {
|
@Nullable PendingContactId pendingContactId,
|
||||||
MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
|
MutableTransportKeys keys) {
|
||||||
keys.put(keySetId, ks);
|
requireExactlyOneNull(contactId, pendingContactId);
|
||||||
encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
|
MutableTransportKeySet ks = new MutableTransportKeySet(keySetId,
|
||||||
encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
|
contactId, pendingContactId, keys);
|
||||||
encodeTags(keySetId, contactId, m.getNextIncomingKeys());
|
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);
|
||||||
considerReplacingOutgoingKeys(ks);
|
considerReplacingOutgoingKeys(ks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: lock
|
@GuardedBy("lock")
|
||||||
private void encodeTags(TransportKeySetId keySetId, ContactId contactId,
|
private void encodeTags(KeySetId keySetId, @Nullable ContactId contactId,
|
||||||
MutableIncomingKeys inKeys) {
|
@Nullable PendingContactId pendingContactId,
|
||||||
|
MutableIncomingKeys inKeys, boolean handshakeMode) {
|
||||||
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
||||||
TagContext tagCtx =
|
TagContext tagCtx = new TagContext(keySetId, contactId,
|
||||||
new TagContext(keySetId, contactId, inKeys, streamNumber);
|
pendingContactId, inKeys, streamNumber, handshakeMode);
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||||
streamNumber);
|
streamNumber);
|
||||||
@@ -145,27 +165,41 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: lock
|
@GuardedBy("lock")
|
||||||
private void considerReplacingOutgoingKeys(MutableKeySet ks) {
|
private void considerReplacingOutgoingKeys(MutableTransportKeySet ks) {
|
||||||
// Use the active outgoing keys with the highest key set ID
|
// Use the active outgoing keys with the highest key set ID, preferring
|
||||||
if (ks.getTransportKeys().getCurrentOutgoingKeys().isActive()) {
|
// rotation keys to handshake keys
|
||||||
MutableKeySet old = outContexts.get(ks.getContactId());
|
if (ks.getKeys().getCurrentOutgoingKeys().isActive()) {
|
||||||
if (old == null ||
|
MutableTransportKeySet old = getOutgoingKeySet(ks.getContactId(),
|
||||||
|
ks.getPendingContactId());
|
||||||
|
if (old == null || (old.getKeys().isHandshakeMode() &&
|
||||||
|
!ks.getKeys().isHandshakeMode()) ||
|
||||||
old.getKeySetId().getInt() < ks.getKeySetId().getInt()) {
|
old.getKeySetId().getInt() < ks.getKeySetId().getInt()) {
|
||||||
outContexts.put(ks.getContactId(), ks);
|
if (ks.getContactId() == null)
|
||||||
|
pendingContactOutContexts.put(ks.getPendingContactId(), ks);
|
||||||
|
else contactOutContexts.put(ks.getContactId(), ks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleKeyRotation(long now) {
|
@GuardedBy("lock")
|
||||||
long delay = timePeriodLength - now % timePeriodLength;
|
@Nullable
|
||||||
scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS);
|
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 rotateKeys() {
|
private void scheduleKeyUpdate(long now) {
|
||||||
|
long delay = timePeriodLength - now % timePeriodLength;
|
||||||
|
scheduler.schedule((Runnable) this::updateKeys, delay, MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateKeys() {
|
||||||
dbExecutor.execute(() -> {
|
dbExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
db.transaction(false, this::rotateKeys);
|
db.transaction(false, this::updateKeys);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
@@ -173,7 +207,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportKeySetId addContact(Transaction txn, ContactId c,
|
public KeySetId addContactWithRotationKeys(Transaction txn, ContactId c,
|
||||||
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
@@ -181,15 +215,15 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
// Work out what time period the timestamp belongs to
|
// Work out what time period the timestamp belongs to
|
||||||
long timePeriod = timestamp / timePeriodLength;
|
long timePeriod = timestamp / timePeriodLength;
|
||||||
// Derive the transport keys
|
// Derive the transport keys
|
||||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, timePeriod, alice, active);
|
rootKey, timePeriod, alice, active);
|
||||||
// Rotate the keys to the current time period if necessary
|
// Update the keys to the current time period if necessary
|
||||||
timePeriod = clock.currentTimeMillis() / timePeriodLength;
|
timePeriod = clock.currentTimeMillis() / timePeriodLength;
|
||||||
k = transportCrypto.rotateTransportKeys(k, timePeriod);
|
k = transportCrypto.updateTransportKeys(k, timePeriod);
|
||||||
// Write the keys back to the DB
|
// Write the keys back to the DB
|
||||||
TransportKeySetId keySetId = db.addTransportKeys(txn, c, k);
|
KeySetId keySetId = db.addTransportKeys(txn, c, k);
|
||||||
// Initialise mutable state for the contact
|
// Initialise mutable state for the contact
|
||||||
addKeys(keySetId, c, new MutableTransportKeys(k));
|
addKeys(keySetId, c, null, new MutableTransportKeys(k));
|
||||||
return keySetId;
|
return keySetId;
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
@@ -197,13 +231,52 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activateKeys(Transaction txn, TransportKeySetId k)
|
public KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c,
|
||||||
throws DbException {
|
SecretKey rootKey, boolean alice) throws DbException {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
MutableKeySet ks = keys.get(k);
|
// 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);
|
||||||
if (ks == null) throw new IllegalArgumentException();
|
if (ks == null) throw new IllegalArgumentException();
|
||||||
MutableTransportKeys m = ks.getTransportKeys();
|
MutableTransportKeys m = ks.getKeys();
|
||||||
m.getCurrentOutgoingKeys().activate();
|
m.getCurrentOutgoingKeys().activate();
|
||||||
considerReplacingOutgoingKeys(ks);
|
considerReplacingOutgoingKeys(ks);
|
||||||
db.setTransportKeysActive(txn, m.getTransportId(), k);
|
db.setTransportKeysActive(txn, m.getTransportId(), k);
|
||||||
@@ -218,13 +291,29 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
try {
|
try {
|
||||||
// Remove mutable state for the contact
|
// Remove mutable state for the contact
|
||||||
Iterator<TagContext> it = inContexts.values().iterator();
|
Iterator<TagContext> it = inContexts.values().iterator();
|
||||||
while (it.hasNext()) if (it.next().contactId.equals(c)) it.remove();
|
while (it.hasNext())
|
||||||
outContexts.remove(c);
|
if (c.equals(it.next().contactId)) it.remove();
|
||||||
Iterator<MutableKeySet> it1 = keys.values().iterator();
|
contactOutContexts.remove(c);
|
||||||
while (it1.hasNext()) {
|
Iterator<MutableTransportKeySet> it1 = keys.values().iterator();
|
||||||
ContactId c1 = it1.next().getContactId();
|
while (it1.hasNext())
|
||||||
if (c1 != null && c1.equals(c)) it1.remove();
|
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();
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
@@ -232,12 +321,21 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canSendOutgoingStreams(ContactId c) {
|
public boolean canSendOutgoingStreams(ContactId c) {
|
||||||
|
return canSendOutgoingStreams(c, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSendOutgoingStreams(PendingContactId p) {
|
||||||
|
return canSendOutgoingStreams(null, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canSendOutgoingStreams(@Nullable ContactId c,
|
||||||
|
@Nullable PendingContactId p) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
MutableKeySet ks = outContexts.get(c);
|
MutableTransportKeySet ks = getOutgoingKeySet(c, p);
|
||||||
if (ks == null) return false;
|
if (ks == null) return false;
|
||||||
MutableOutgoingKeys outKeys =
|
MutableOutgoingKeys outKeys = ks.getKeys().getCurrentOutgoingKeys();
|
||||||
ks.getTransportKeys().getCurrentOutgoingKeys();
|
|
||||||
if (!outKeys.isActive()) throw new AssertionError();
|
if (!outKeys.isActive()) throw new AssertionError();
|
||||||
return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED;
|
return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -248,19 +346,32 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
@Override
|
@Override
|
||||||
public StreamContext getStreamContext(Transaction txn, ContactId c)
|
public StreamContext getStreamContext(Transaction txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
return getStreamContext(txn, c, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamContext getStreamContext(Transaction txn, PendingContactId p)
|
||||||
|
throws DbException {
|
||||||
|
return getStreamContext(txn, null, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private StreamContext getStreamContext(Transaction txn,
|
||||||
|
@Nullable ContactId c, @Nullable PendingContactId p)
|
||||||
|
throws DbException {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
// Look up the outgoing keys for the contact
|
// Look up the outgoing keys for the contact
|
||||||
MutableKeySet ks = outContexts.get(c);
|
MutableTransportKeySet ks = getOutgoingKeySet(c, p);
|
||||||
if (ks == null) return null;
|
if (ks == null) return null;
|
||||||
MutableOutgoingKeys outKeys =
|
MutableTransportKeys keys = ks.getKeys();
|
||||||
ks.getTransportKeys().getCurrentOutgoingKeys();
|
MutableOutgoingKeys outKeys = keys.getCurrentOutgoingKeys();
|
||||||
if (!outKeys.isActive()) throw new AssertionError();
|
if (!outKeys.isActive()) throw new AssertionError();
|
||||||
if (outKeys.getStreamCounter() > MAX_32_BIT_UNSIGNED) return null;
|
if (outKeys.getStreamCounter() > MAX_32_BIT_UNSIGNED) return null;
|
||||||
// Create a stream context
|
// Create a stream context
|
||||||
StreamContext ctx = new StreamContext(c, transportId,
|
StreamContext ctx = new StreamContext(c, p, transportId,
|
||||||
outKeys.getTagKey(), outKeys.getHeaderKey(),
|
outKeys.getTagKey(), outKeys.getHeaderKey(),
|
||||||
outKeys.getStreamCounter());
|
outKeys.getStreamCounter(), keys.isHandshakeMode());
|
||||||
// Increment the stream counter and write it back to the DB
|
// Increment the stream counter and write it back to the DB
|
||||||
outKeys.incrementStreamCounter();
|
outKeys.incrementStreamCounter();
|
||||||
db.incrementStreamCounter(txn, transportId, ks.getKeySetId());
|
db.incrementStreamCounter(txn, transportId, ks.getKeySetId());
|
||||||
@@ -280,9 +391,10 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
if (tagCtx == null) return null;
|
if (tagCtx == null) return null;
|
||||||
MutableIncomingKeys inKeys = tagCtx.inKeys;
|
MutableIncomingKeys inKeys = tagCtx.inKeys;
|
||||||
// Create a stream context
|
// Create a stream context
|
||||||
StreamContext ctx = new StreamContext(tagCtx.contactId, transportId,
|
StreamContext ctx = new StreamContext(tagCtx.contactId,
|
||||||
|
tagCtx.pendingContactId, transportId,
|
||||||
inKeys.getTagKey(), inKeys.getHeaderKey(),
|
inKeys.getTagKey(), inKeys.getHeaderKey(),
|
||||||
tagCtx.streamNumber);
|
tagCtx.streamNumber, tagCtx.handshakeMode);
|
||||||
// Update the reordering window
|
// Update the reordering window
|
||||||
ReorderingWindow window = inKeys.getWindow();
|
ReorderingWindow window = inKeys.getWindow();
|
||||||
Change change = window.setSeen(tagCtx.streamNumber);
|
Change change = window.setSeen(tagCtx.streamNumber);
|
||||||
@@ -292,7 +404,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
|
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
|
||||||
PROTOCOL_VERSION, streamNumber);
|
PROTOCOL_VERSION, streamNumber);
|
||||||
TagContext tagCtx1 = new TagContext(tagCtx.keySetId,
|
TagContext tagCtx1 = new TagContext(tagCtx.keySetId,
|
||||||
tagCtx.contactId, inKeys, streamNumber);
|
tagCtx.contactId, tagCtx.pendingContactId, inKeys,
|
||||||
|
streamNumber, tagCtx.handshakeMode);
|
||||||
inContexts.put(new Bytes(addTag), tagCtx1);
|
inContexts.put(new Bytes(addTag), tagCtx1);
|
||||||
}
|
}
|
||||||
// Remove tags for any stream numbers removed from the window
|
// Remove tags for any stream numbers removed from the window
|
||||||
@@ -308,9 +421,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
inKeys.getTimePeriod(), window.getBase(),
|
inKeys.getTimePeriod(), window.getBase(),
|
||||||
window.getBitmap());
|
window.getBitmap());
|
||||||
// If the outgoing keys are inactive, activate them
|
// If the outgoing keys are inactive, activate them
|
||||||
MutableKeySet ks = keys.get(tagCtx.keySetId);
|
MutableTransportKeySet ks = keys.get(tagCtx.keySetId);
|
||||||
MutableOutgoingKeys outKeys =
|
MutableOutgoingKeys outKeys =
|
||||||
ks.getTransportKeys().getCurrentOutgoingKeys();
|
ks.getKeys().getCurrentOutgoingKeys();
|
||||||
if (!outKeys.isActive()) {
|
if (!outKeys.isActive()) {
|
||||||
LOG.info("Activating outgoing keys");
|
LOG.info("Activating outgoing keys");
|
||||||
outKeys.activate();
|
outKeys.activate();
|
||||||
@@ -323,51 +436,62 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rotateKeys(Transaction txn) throws DbException {
|
private void updateKeys(Transaction txn) throws DbException {
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
// Rotate the keys to the current time period
|
// Update the keys to the current time period
|
||||||
Collection<TransportKeySet> snapshot = new ArrayList<>(keys.size());
|
Collection<TransportKeySet> snapshot = new ArrayList<>(keys.size());
|
||||||
for (MutableKeySet ks : keys.values()) {
|
for (MutableTransportKeySet ks : keys.values()) {
|
||||||
snapshot.add(new TransportKeySet(ks.getKeySetId(),
|
snapshot.add(new TransportKeySet(ks.getKeySetId(),
|
||||||
ks.getContactId(), ks.getTransportKeys().snapshot()));
|
ks.getContactId(), ks.getPendingContactId(),
|
||||||
|
ks.getKeys().snapshot()));
|
||||||
}
|
}
|
||||||
RotationResult rotationResult = rotateKeys(snapshot, now);
|
UpdateResult updateResult = updateKeys(snapshot, now);
|
||||||
// Rebuild the mutable state for all contacts
|
// Rebuild the mutable state for all contacts
|
||||||
inContexts.clear();
|
inContexts.clear();
|
||||||
outContexts.clear();
|
contactOutContexts.clear();
|
||||||
|
pendingContactOutContexts.clear();
|
||||||
keys.clear();
|
keys.clear();
|
||||||
addKeys(rotationResult.current);
|
addKeys(updateResult.current);
|
||||||
// Write any rotated keys back to the DB
|
// Write any updated keys back to the DB
|
||||||
if (!rotationResult.rotated.isEmpty())
|
if (!updateResult.updated.isEmpty())
|
||||||
db.updateTransportKeys(txn, rotationResult.rotated);
|
db.updateTransportKeys(txn, updateResult.updated);
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
// Schedule the next key rotation
|
// Schedule the next key update
|
||||||
scheduleKeyRotation(now);
|
scheduleKeyUpdate(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TagContext {
|
private static class TagContext {
|
||||||
|
|
||||||
private final TransportKeySetId keySetId;
|
private final KeySetId keySetId;
|
||||||
|
@Nullable
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
|
@Nullable
|
||||||
|
private final PendingContactId pendingContactId;
|
||||||
private final MutableIncomingKeys inKeys;
|
private final MutableIncomingKeys inKeys;
|
||||||
private final long streamNumber;
|
private final long streamNumber;
|
||||||
|
private final boolean handshakeMode;
|
||||||
|
|
||||||
private TagContext(TransportKeySetId keySetId, ContactId contactId,
|
private TagContext(KeySetId keySetId, @Nullable ContactId contactId,
|
||||||
MutableIncomingKeys inKeys, long streamNumber) {
|
@Nullable PendingContactId pendingContactId,
|
||||||
|
MutableIncomingKeys inKeys, long streamNumber,
|
||||||
|
boolean handshakeMode) {
|
||||||
|
requireExactlyOneNull(contactId, pendingContactId);
|
||||||
this.keySetId = keySetId;
|
this.keySetId = keySetId;
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
|
this.pendingContactId = pendingContactId;
|
||||||
this.inKeys = inKeys;
|
this.inKeys = inKeys;
|
||||||
this.streamNumber = streamNumber;
|
this.streamNumber = streamNumber;
|
||||||
|
this.handshakeMode = handshakeMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class RotationResult {
|
private static class UpdateResult {
|
||||||
|
|
||||||
private final Collection<TransportKeySet> current = new ArrayList<>();
|
private final Collection<TransportKeySet> current = new ArrayList<>();
|
||||||
private final Collection<TransportKeySet> rotated = new ArrayList<>();
|
private final Collection<TransportKeySet> updated = new ArrayList<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ package org.briarproject.bramble.client;
|
|||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
import org.briarproject.bramble.api.data.BdfEntry;
|
import org.briarproject.bramble.api.data.BdfEntry;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
@@ -43,6 +46,8 @@ import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSignaturePrivateKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSignaturePublicKey;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -66,6 +71,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
context.mock(CryptoComponent.class);
|
context.mock(CryptoComponent.class);
|
||||||
private final AuthorFactory authorFactory =
|
private final AuthorFactory authorFactory =
|
||||||
context.mock(AuthorFactory.class);
|
context.mock(AuthorFactory.class);
|
||||||
|
private final KeyParser keyParser = context.mock(KeyParser.class);
|
||||||
|
|
||||||
private final GroupId groupId = new GroupId(getRandomId());
|
private final GroupId groupId = new GroupId(getRandomId());
|
||||||
private final BdfDictionary dictionary = new BdfDictionary();
|
private final BdfDictionary dictionary = new BdfDictionary();
|
||||||
@@ -262,24 +268,25 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSign() throws Exception {
|
public void testSign() throws Exception {
|
||||||
byte[] privateKey = getRandomBytes(42);
|
PrivateKey privateKey = getSignaturePrivateKey();
|
||||||
byte[] signed = getRandomBytes(42);
|
byte[] signature = getRandomBytes(42);
|
||||||
|
|
||||||
byte[] bytes = expectToByteArray(list);
|
byte[] bytes = expectToByteArray(list);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(cryptoComponent).sign(label, bytes, privateKey);
|
oneOf(cryptoComponent).sign(label, bytes, privateKey);
|
||||||
will(returnValue(signed));
|
will(returnValue(signature));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertArrayEquals(signed, clientHelper.sign(label, list, privateKey));
|
assertArrayEquals(signature,
|
||||||
|
clientHelper.sign(label, list, privateKey));
|
||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVerifySignature() throws Exception {
|
public void testVerifySignature() throws Exception {
|
||||||
byte[] signature = getRandomBytes(MAX_SIGNATURE_LENGTH);
|
byte[] signature = getRandomBytes(MAX_SIGNATURE_LENGTH);
|
||||||
byte[] publicKey = getRandomBytes(42);
|
|
||||||
byte[] signed = expectToByteArray(list);
|
byte[] signed = expectToByteArray(list);
|
||||||
|
PublicKey publicKey = getSignaturePublicKey();
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(cryptoComponent).verifySignature(signature, label, signed,
|
oneOf(cryptoComponent).verifySignature(signature, label, signed,
|
||||||
@@ -294,8 +301,8 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testVerifyWrongSignature() throws Exception {
|
public void testVerifyWrongSignature() throws Exception {
|
||||||
byte[] signature = getRandomBytes(MAX_SIGNATURE_LENGTH);
|
byte[] signature = getRandomBytes(MAX_SIGNATURE_LENGTH);
|
||||||
byte[] publicKey = getRandomBytes(42);
|
|
||||||
byte[] signed = expectToByteArray(list);
|
byte[] signed = expectToByteArray(list);
|
||||||
|
PublicKey publicKey = getSignaturePublicKey();
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(cryptoComponent).verifySignature(signature, label, signed,
|
oneOf(cryptoComponent).verifySignature(signature, label, signed,
|
||||||
@@ -315,6 +322,10 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testParsesAndEncodesAuthor() throws Exception {
|
public void testParsesAndEncodesAuthor() throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(cryptoComponent).getSignatureKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
oneOf(keyParser).parsePublicKey(author.getPublicKey().getEncoded());
|
||||||
|
will(returnValue(author.getPublicKey()));
|
||||||
oneOf(authorFactory).createAuthor(author.getFormatVersion(),
|
oneOf(authorFactory).createAuthor(author.getFormatVersion(),
|
||||||
author.getName(), author.getPublicKey());
|
author.getName(), author.getPublicKey());
|
||||||
will(returnValue(author));
|
will(returnValue(author));
|
||||||
@@ -329,10 +340,14 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
BdfList authorList = BdfList.of(
|
BdfList authorList = BdfList.of(
|
||||||
author.getFormatVersion(),
|
author.getFormatVersion(),
|
||||||
author.getName(),
|
author.getName(),
|
||||||
author.getPublicKey()
|
author.getPublicKey().getEncoded()
|
||||||
);
|
);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(cryptoComponent).getSignatureKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
oneOf(keyParser).parsePublicKey(author.getPublicKey().getEncoded());
|
||||||
|
will(returnValue(author.getPublicKey()));
|
||||||
oneOf(authorFactory).createAuthor(author.getFormatVersion(),
|
oneOf(authorFactory).createAuthor(author.getFormatVersion(),
|
||||||
author.getName(), author.getPublicKey());
|
author.getName(), author.getPublicKey());
|
||||||
will(returnValue(author));
|
will(returnValue(author));
|
||||||
@@ -355,7 +370,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
BdfList invalidAuthor = BdfList.of(
|
BdfList invalidAuthor = BdfList.of(
|
||||||
author.getFormatVersion(),
|
author.getFormatVersion(),
|
||||||
author.getName(),
|
author.getName(),
|
||||||
author.getPublicKey(),
|
author.getPublicKey().getEncoded(),
|
||||||
"foo"
|
"foo"
|
||||||
);
|
);
|
||||||
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
||||||
@@ -366,7 +381,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
BdfList invalidAuthor = BdfList.of(
|
BdfList invalidAuthor = BdfList.of(
|
||||||
null,
|
null,
|
||||||
author.getName(),
|
author.getName(),
|
||||||
author.getPublicKey()
|
author.getPublicKey().getEncoded()
|
||||||
);
|
);
|
||||||
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
||||||
}
|
}
|
||||||
@@ -377,7 +392,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
BdfList invalidAuthor = BdfList.of(
|
BdfList invalidAuthor = BdfList.of(
|
||||||
"foo",
|
"foo",
|
||||||
author.getName(),
|
author.getName(),
|
||||||
author.getPublicKey()
|
author.getPublicKey().getEncoded()
|
||||||
);
|
);
|
||||||
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
||||||
}
|
}
|
||||||
@@ -387,7 +402,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
BdfList invalidAuthor = BdfList.of(
|
BdfList invalidAuthor = BdfList.of(
|
||||||
author.getFormatVersion() + 1,
|
author.getFormatVersion() + 1,
|
||||||
author.getName(),
|
author.getName(),
|
||||||
author.getPublicKey()
|
author.getPublicKey().getEncoded()
|
||||||
);
|
);
|
||||||
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
||||||
}
|
}
|
||||||
@@ -397,7 +412,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
BdfList invalidAuthor = BdfList.of(
|
BdfList invalidAuthor = BdfList.of(
|
||||||
author.getFormatVersion(),
|
author.getFormatVersion(),
|
||||||
"",
|
"",
|
||||||
author.getPublicKey()
|
author.getPublicKey().getEncoded()
|
||||||
);
|
);
|
||||||
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
||||||
}
|
}
|
||||||
@@ -407,7 +422,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
BdfList invalidAuthor = BdfList.of(
|
BdfList invalidAuthor = BdfList.of(
|
||||||
author.getFormatVersion(),
|
author.getFormatVersion(),
|
||||||
getRandomString(MAX_AUTHOR_NAME_LENGTH + 1),
|
getRandomString(MAX_AUTHOR_NAME_LENGTH + 1),
|
||||||
author.getPublicKey()
|
author.getPublicKey().getEncoded()
|
||||||
);
|
);
|
||||||
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
||||||
}
|
}
|
||||||
@@ -417,7 +432,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
BdfList invalidAuthor = BdfList.of(
|
BdfList invalidAuthor = BdfList.of(
|
||||||
author.getFormatVersion(),
|
author.getFormatVersion(),
|
||||||
null,
|
null,
|
||||||
author.getPublicKey()
|
author.getPublicKey().getEncoded()
|
||||||
);
|
);
|
||||||
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
||||||
}
|
}
|
||||||
@@ -472,6 +487,24 @@ public class ClientHelperImplTest extends BrambleTestCase {
|
|||||||
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsAuthorWithInvalidPublicKey() throws Exception {
|
||||||
|
BdfList invalidAuthor = BdfList.of(
|
||||||
|
author.getFormatVersion(),
|
||||||
|
author.getName(),
|
||||||
|
author.getPublicKey().getEncoded()
|
||||||
|
);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(cryptoComponent).getSignatureKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
oneOf(keyParser).parsePublicKey(author.getPublicKey().getEncoded());
|
||||||
|
will(throwException(new GeneralSecurityException()));
|
||||||
|
}});
|
||||||
|
|
||||||
|
clientHelper.parseAndValidateAuthor(invalidAuthor);
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] expectToByteArray(BdfList list) throws Exception {
|
private byte[] expectToByteArray(BdfList list) throws Exception {
|
||||||
BdfWriter bdfWriter = context.mock(BdfWriter.class);
|
BdfWriter bdfWriter = context.mock(BdfWriter.class);
|
||||||
|
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(db).transactionWithResult(with(false), withDbCallable(txn));
|
oneOf(db).transactionWithResult(with(false), withDbCallable(txn));
|
||||||
oneOf(db).addContact(txn, remote, local, verified);
|
oneOf(db).addContact(txn, remote, local, verified);
|
||||||
will(returnValue(contactId));
|
will(returnValue(contactId));
|
||||||
oneOf(keyManager).addContact(txn, contactId, rootKey, timestamp,
|
oneOf(keyManager).addContactWithRotationKeys(txn, contactId,
|
||||||
alice, active);
|
rootKey, timestamp, alice, active);
|
||||||
oneOf(db).getContact(txn, contactId);
|
oneOf(db).getContact(txn, contactId);
|
||||||
will(returnValue(contact));
|
will(returnValue(contact));
|
||||||
}});
|
}});
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LAB
|
|||||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
@@ -34,12 +34,11 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
|
|||||||
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||||
private final Clock clock = context.mock(Clock.class);
|
private final Clock clock = context.mock(Clock.class);
|
||||||
private final KeyParser keyParser = context.mock(KeyParser.class);
|
private final KeyParser keyParser = context.mock(KeyParser.class);
|
||||||
private final PublicKey publicKey = context.mock(PublicKey.class);
|
|
||||||
|
|
||||||
private final PendingContactFactory pendingContactFactory =
|
private final PendingContactFactory pendingContactFactory =
|
||||||
new PendingContactFactoryImpl(crypto, clock);
|
new PendingContactFactoryImpl(crypto, clock);
|
||||||
private final String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
|
private final String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
|
||||||
private final byte[] publicKeyBytes = getRandomBytes(RAW_LINK_BYTES - 1);
|
private final PublicKey publicKey = getAgreementPublicKey();
|
||||||
private final byte[] idBytes = getRandomId();
|
private final byte[] idBytes = getRandomId();
|
||||||
private final long timestamp = System.currentTimeMillis();
|
private final long timestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
@@ -64,7 +63,7 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
|
|||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).getAgreementKeyParser();
|
oneOf(crypto).getAgreementKeyParser();
|
||||||
will(returnValue(keyParser));
|
will(returnValue(keyParser));
|
||||||
oneOf(keyParser).parsePublicKey(with(equal(publicKeyBytes)));
|
oneOf(keyParser).parsePublicKey(publicKey.getEncoded());
|
||||||
will(throwException(new GeneralSecurityException()));
|
will(throwException(new GeneralSecurityException()));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
@@ -95,11 +94,9 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
|
|||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).getAgreementKeyParser();
|
oneOf(crypto).getAgreementKeyParser();
|
||||||
will(returnValue(keyParser));
|
will(returnValue(keyParser));
|
||||||
oneOf(keyParser).parsePublicKey(with(equal(publicKeyBytes)));
|
oneOf(keyParser).parsePublicKey(publicKey.getEncoded());
|
||||||
will(returnValue(publicKey));
|
will(returnValue(publicKey));
|
||||||
allowing(publicKey).getEncoded();
|
oneOf(crypto).hash(ID_LABEL, publicKey.getEncoded());
|
||||||
will(returnValue(publicKeyBytes));
|
|
||||||
oneOf(crypto).hash(ID_LABEL, publicKeyBytes);
|
|
||||||
will(returnValue(idBytes));
|
will(returnValue(idBytes));
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(timestamp));
|
will(returnValue(timestamp));
|
||||||
@@ -108,7 +105,8 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
|
|||||||
PendingContact p =
|
PendingContact p =
|
||||||
pendingContactFactory.createPendingContact(link, alias);
|
pendingContactFactory.createPendingContact(link, alias);
|
||||||
assertArrayEquals(idBytes, p.getId().getBytes());
|
assertArrayEquals(idBytes, p.getId().getBytes());
|
||||||
assertArrayEquals(publicKeyBytes, p.getPublicKey());
|
assertArrayEquals(publicKey.getEncoded(),
|
||||||
|
p.getPublicKey().getEncoded());
|
||||||
assertEquals(alias, p.getAlias());
|
assertEquals(alias, p.getAlias());
|
||||||
assertEquals(WAITING_FOR_CONNECTION, p.getState());
|
assertEquals(WAITING_FOR_CONNECTION, p.getState());
|
||||||
assertEquals(timestamp, p.getTimestamp());
|
assertEquals(timestamp, p.getTimestamp());
|
||||||
@@ -121,6 +119,7 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
|
|||||||
private String encodeLink(int formatVersion) {
|
private String encodeLink(int formatVersion) {
|
||||||
byte[] rawLink = new byte[RAW_LINK_BYTES];
|
byte[] rawLink = new byte[RAW_LINK_BYTES];
|
||||||
rawLink[0] = (byte) formatVersion;
|
rawLink[0] = (byte) formatVersion;
|
||||||
|
byte[] publicKeyBytes = publicKey.getEncoded();
|
||||||
arraycopy(publicKeyBytes, 0, rawLink, 1, publicKeyBytes.length);
|
arraycopy(publicKeyBytes, 0, rawLink, 1, publicKeyBytes.length);
|
||||||
String base32 = Base32.encode(rawLink).toLowerCase();
|
String base32 = Base32.encode(rawLink).toLowerCase();
|
||||||
assertEquals(BASE32_LINK_BYTES, base32.length());
|
assertEquals(BASE32_LINK_BYTES, base32.length());
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
@@ -137,14 +141,14 @@ public class EdSignatureTest extends SignatureTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
protected byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
return crypto.sign(label, toSign, privateKey);
|
return crypto.sign(label, toSign, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean verify(byte[] signature, String label, byte[] signed,
|
protected boolean verify(byte[] signature, String label, byte[] signed,
|
||||||
byte[] publicKey) throws GeneralSecurityException {
|
PublicKey publicKey) throws GeneralSecurityException {
|
||||||
return crypto.verifySignature(signature, label, signed, publicKey);
|
return crypto.verifySignature(signature, label, signed, publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,11 +161,11 @@ public class EdSignatureTest extends SignatureTest {
|
|||||||
byte[] signatureBytes = fromHexString(vector[3]);
|
byte[] signatureBytes = fromHexString(vector[3]);
|
||||||
|
|
||||||
EdSignature signature = new EdSignature();
|
EdSignature signature = new EdSignature();
|
||||||
signature.initSign(new EdPrivateKey(privateKeyBytes));
|
signature.initSign(new SignaturePrivateKey(privateKeyBytes));
|
||||||
signature.update(messageBytes);
|
signature.update(messageBytes);
|
||||||
assertArrayEquals(signatureBytes, signature.sign());
|
assertArrayEquals(signatureBytes, signature.sign());
|
||||||
|
|
||||||
signature.initVerify(new EdPublicKey(publicKeyBytes));
|
signature.initVerify(new SignaturePublicKey(publicKeyBytes));
|
||||||
signature.update(messageBytes);
|
signature.update(messageBytes);
|
||||||
assertTrue(signature.verify(signatureBytes));
|
assertTrue(signature.verify(signatureBytes));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,167 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
@@ -10,12 +11,14 @@ import org.junit.Test;
|
|||||||
import org.whispersystems.curve25519.Curve25519;
|
import org.whispersystems.curve25519.Curve25519;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
public class KeyAgreementTest extends BrambleTestCase {
|
public class KeyAgreementTest extends BrambleTestCase {
|
||||||
|
|
||||||
@@ -35,6 +38,7 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
|
|
||||||
private final CryptoComponent crypto =
|
private final CryptoComponent crypto =
|
||||||
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
|
private final String label = getRandomString(123);
|
||||||
private final byte[][] inputs;
|
private final byte[][] inputs;
|
||||||
|
|
||||||
public KeyAgreementTest() {
|
public KeyAgreementTest() {
|
||||||
@@ -48,9 +52,9 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
public void testDerivesSharedSecret() throws Exception {
|
public void testDerivesSharedSecret() throws Exception {
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
SecretKey aShared = crypto.deriveSharedSecret(label,
|
||||||
bPair.getPublic(), aPair, inputs);
|
bPair.getPublic(), aPair, inputs);
|
||||||
SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
SecretKey bShared = crypto.deriveSharedSecret(label,
|
||||||
aPair.getPublic(), bPair, inputs);
|
aPair.getPublic(), bPair, inputs);
|
||||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
||||||
}
|
}
|
||||||
@@ -58,18 +62,15 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
@Test(expected = GeneralSecurityException.class)
|
@Test(expected = GeneralSecurityException.class)
|
||||||
public void testRejectsInvalidPublicKey() throws Exception {
|
public void testRejectsInvalidPublicKey() throws Exception {
|
||||||
KeyPair keyPair = crypto.generateAgreementKeyPair();
|
KeyPair keyPair = crypto.generateAgreementKeyPair();
|
||||||
PublicKey invalid = new Curve25519PublicKey(new byte[32]);
|
PublicKey invalid = new AgreementPublicKey(new byte[32]);
|
||||||
crypto.deriveSharedSecret(SHARED_SECRET_LABEL, invalid, keyPair,
|
crypto.deriveSharedSecret(label, invalid, keyPair, inputs);
|
||||||
inputs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRfc7748TestVector() throws Exception {
|
public void testRfc7748TestVector() {
|
||||||
// Private keys need to be clamped because curve25519-java does the
|
byte[] aPriv = parsePrivateKey(ALICE_PRIVATE);
|
||||||
// clamping at key generation time, not multiplication time
|
|
||||||
byte[] aPriv = Curve25519KeyParser.clamp(fromHexString(ALICE_PRIVATE));
|
|
||||||
byte[] aPub = fromHexString(ALICE_PUBLIC);
|
byte[] aPub = fromHexString(ALICE_PUBLIC);
|
||||||
byte[] bPriv = Curve25519KeyParser.clamp(fromHexString(BOB_PRIVATE));
|
byte[] bPriv = parsePrivateKey(BOB_PRIVATE);
|
||||||
byte[] bPub = fromHexString(BOB_PUBLIC);
|
byte[] bPub = fromHexString(BOB_PUBLIC);
|
||||||
byte[] sharedSecret = fromHexString(SHARED_SECRET);
|
byte[] sharedSecret = fromHexString(SHARED_SECRET);
|
||||||
Curve25519 curve25519 = Curve25519.getInstance("java");
|
Curve25519 curve25519 = Curve25519.getInstance("java");
|
||||||
@@ -78,4 +79,82 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
assertArrayEquals(sharedSecret,
|
assertArrayEquals(sharedSecret,
|
||||||
curve25519.calculateAgreement(bPub, aPriv));
|
curve25519.calculateAgreement(bPub, aPriv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDerivesSameSharedSecretFromEquivalentPublicKey() {
|
||||||
|
byte[] aPub = fromHexString(ALICE_PUBLIC);
|
||||||
|
byte[] bPriv = parsePrivateKey(BOB_PRIVATE);
|
||||||
|
byte[] sharedSecret = fromHexString(SHARED_SECRET);
|
||||||
|
Curve25519 curve25519 = Curve25519.getInstance("java");
|
||||||
|
|
||||||
|
// Flip the unused most significant bit of the little-endian public key
|
||||||
|
byte[] aPubEquiv = aPub.clone();
|
||||||
|
aPubEquiv[31] ^= (byte) 128;
|
||||||
|
|
||||||
|
// The public keys should be different but give the same shared secret
|
||||||
|
assertFalse(Arrays.equals(aPub, aPubEquiv));
|
||||||
|
assertArrayEquals(sharedSecret,
|
||||||
|
curve25519.calculateAgreement(aPub, bPriv));
|
||||||
|
assertArrayEquals(sharedSecret,
|
||||||
|
curve25519.calculateAgreement(aPubEquiv, bPriv));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDerivesSameSharedSecretFromEquivalentPublicKeyWithoutPublicKeysHashedIn()
|
||||||
|
throws Exception {
|
||||||
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
|
|
||||||
|
// Flip the unused most significant bit of the little-endian public key
|
||||||
|
byte[] aPub = aPair.getPublic().getEncoded();
|
||||||
|
byte[] aPubEquiv = aPub.clone();
|
||||||
|
aPubEquiv[31] ^= (byte) 128;
|
||||||
|
KeyPair aPairEquiv = new KeyPair(new AgreementPublicKey(aPubEquiv),
|
||||||
|
aPair.getPrivate());
|
||||||
|
|
||||||
|
// The public keys should be different but give the same shared secret
|
||||||
|
assertFalse(Arrays.equals(aPub, aPubEquiv));
|
||||||
|
SecretKey shared = crypto.deriveSharedSecret(label,
|
||||||
|
aPair.getPublic(), bPair);
|
||||||
|
SecretKey sharedEquiv = crypto.deriveSharedSecret(label,
|
||||||
|
aPairEquiv.getPublic(), bPair);
|
||||||
|
assertArrayEquals(shared.getBytes(), sharedEquiv.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDerivesDifferentSharedSecretFromEquivalentPublicKeyWithPublicKeysHashedIn()
|
||||||
|
throws Exception {
|
||||||
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
|
|
||||||
|
// Flip the unused most significant bit of the little-endian public key
|
||||||
|
byte[] aPub = aPair.getPublic().getEncoded();
|
||||||
|
byte[] aPubEquiv = aPub.clone();
|
||||||
|
aPubEquiv[31] ^= (byte) 128;
|
||||||
|
KeyPair aPairEquiv = new KeyPair(new AgreementPublicKey(aPubEquiv),
|
||||||
|
aPair.getPrivate());
|
||||||
|
|
||||||
|
// The public keys should be different and give different shared secrets
|
||||||
|
assertFalse(Arrays.equals(aPub, aPubEquiv));
|
||||||
|
SecretKey shared = deriveSharedSecretWithPublicKeysHashedIn(label,
|
||||||
|
aPair.getPublic(), bPair);
|
||||||
|
SecretKey sharedEquiv = deriveSharedSecretWithPublicKeysHashedIn(label,
|
||||||
|
aPairEquiv.getPublic(), bPair);
|
||||||
|
assertFalse(Arrays.equals(shared.getBytes(), sharedEquiv.getBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecretKey deriveSharedSecretWithPublicKeysHashedIn(String label,
|
||||||
|
PublicKey publicKey, KeyPair keyPair) throws Exception {
|
||||||
|
byte[][] inputs = new byte[][] {
|
||||||
|
publicKey.getEncoded(),
|
||||||
|
keyPair.getPublic().getEncoded()
|
||||||
|
};
|
||||||
|
return crypto.deriveSharedSecret(label, publicKey, keyPair, inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] parsePrivateKey(String hex) {
|
||||||
|
// Private keys need to be clamped because curve25519-java does the
|
||||||
|
// clamping at key generation time, not multiplication time
|
||||||
|
return AgreementKeyParser.clamp(fromHexString(hex));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,7 +23,7 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
|||||||
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAgreementPublicKeyLength() throws Exception {
|
public void testAgreementPublicKeyLength() {
|
||||||
// Generate 10 agreement key pairs
|
// Generate 10 agreement key pairs
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
KeyPair keyPair = crypto.generateAgreementKeyPair();
|
KeyPair keyPair = crypto.generateAgreementKeyPair();
|
||||||
@@ -70,7 +70,7 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAgreementKeyParserByFuzzing() throws Exception {
|
public void testAgreementKeyParserByFuzzing() {
|
||||||
KeyParser parser = crypto.getAgreementKeyParser();
|
KeyParser parser = crypto.getAgreementKeyParser();
|
||||||
// Generate a key pair to get the proper public key length
|
// Generate a key pair to get the proper public key length
|
||||||
KeyPair p = crypto.generateAgreementKeyPair();
|
KeyPair p = crypto.generateAgreementKeyPair();
|
||||||
@@ -92,7 +92,7 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignaturePublicKeyLength() throws Exception {
|
public void testSignaturePublicKeyLength() {
|
||||||
// Generate 10 signature key pairs
|
// Generate 10 signature key pairs
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||||
@@ -107,10 +107,10 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
|||||||
// Generate 10 signature key pairs
|
// Generate 10 signature key pairs
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||||
byte[] key = keyPair.getPrivate().getEncoded();
|
PrivateKey privateKey = keyPair.getPrivate();
|
||||||
// Sign some random data and check the length of the signature
|
// Sign some random data and check the length of the signature
|
||||||
byte[] toBeSigned = getRandomBytes(1234);
|
byte[] toBeSigned = getRandomBytes(1234);
|
||||||
byte[] signature = crypto.sign("label", toBeSigned, key);
|
byte[] signature = crypto.sign("label", toBeSigned, privateKey);
|
||||||
assertTrue(signature.length <= MAX_SIGNATURE_BYTES);
|
assertTrue(signature.length <= MAX_SIGNATURE_BYTES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,16 +123,15 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
|||||||
PublicKey publicKey = keyPair.getPublic();
|
PublicKey publicKey = keyPair.getPublic();
|
||||||
PrivateKey privateKey = keyPair.getPrivate();
|
PrivateKey privateKey = keyPair.getPrivate();
|
||||||
byte[] message = getRandomBytes(123);
|
byte[] message = getRandomBytes(123);
|
||||||
byte[] signature = crypto.sign("test", message,
|
byte[] signature = crypto.sign("test", message, privateKey);
|
||||||
privateKey.getEncoded());
|
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
assertTrue(crypto.verifySignature(signature, "test", message,
|
assertTrue(crypto.verifySignature(signature, "test", message,
|
||||||
publicKey.getEncoded()));
|
publicKey));
|
||||||
// Encode and parse the public key - no exceptions should be thrown
|
// Encode and parse the public key - no exceptions should be thrown
|
||||||
publicKey = parser.parsePublicKey(publicKey.getEncoded());
|
publicKey = parser.parsePublicKey(publicKey.getEncoded());
|
||||||
// Verify the signature again
|
// Verify the signature again
|
||||||
assertTrue(crypto.verifySignature(signature, "test", message,
|
assertTrue(crypto.verifySignature(signature, "test", message,
|
||||||
publicKey.getEncoded()));
|
publicKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -143,23 +142,21 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
|||||||
PublicKey publicKey = keyPair.getPublic();
|
PublicKey publicKey = keyPair.getPublic();
|
||||||
PrivateKey privateKey = keyPair.getPrivate();
|
PrivateKey privateKey = keyPair.getPrivate();
|
||||||
byte[] message = getRandomBytes(123);
|
byte[] message = getRandomBytes(123);
|
||||||
byte[] signature = crypto.sign("test", message,
|
byte[] signature = crypto.sign("test", message, privateKey);
|
||||||
privateKey.getEncoded());
|
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
assertTrue(crypto.verifySignature(signature, "test", message,
|
assertTrue(crypto.verifySignature(signature, "test", message,
|
||||||
publicKey.getEncoded()));
|
publicKey));
|
||||||
// Encode and parse the private key - no exceptions should be thrown
|
// Encode and parse the private key - no exceptions should be thrown
|
||||||
privateKey = parser.parsePrivateKey(privateKey.getEncoded());
|
privateKey = parser.parsePrivateKey(privateKey.getEncoded());
|
||||||
// Sign the data again - the signatures should be the same
|
// Sign the data again - the signatures should be the same
|
||||||
byte[] signature1 = crypto.sign("test", message,
|
byte[] signature1 = crypto.sign("test", message, privateKey);
|
||||||
privateKey.getEncoded());
|
|
||||||
assertTrue(crypto.verifySignature(signature1, "test", message,
|
assertTrue(crypto.verifySignature(signature1, "test", message,
|
||||||
publicKey.getEncoded()));
|
publicKey));
|
||||||
assertArrayEquals(signature, signature1);
|
assertArrayEquals(signature, signature1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignatureKeyParserByFuzzing() throws Exception {
|
public void testSignatureKeyParserByFuzzing() {
|
||||||
KeyParser parser = crypto.getSignatureKeyParser();
|
KeyParser parser = crypto.getSignatureKeyParser();
|
||||||
// Generate a key pair to get the proper public key length
|
// Generate a key pair to get the proper public key length
|
||||||
KeyPair p = crypto.generateSignatureKeyPair();
|
KeyPair p = crypto.generateSignatureKeyPair();
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package org.briarproject.bramble.crypto;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
@@ -19,23 +21,24 @@ public abstract class SignatureTest extends BrambleTestCase {
|
|||||||
|
|
||||||
protected final CryptoComponent crypto;
|
protected final CryptoComponent crypto;
|
||||||
|
|
||||||
private final byte[] publicKey, privateKey;
|
private final PublicKey publicKey;
|
||||||
|
private final PrivateKey privateKey;
|
||||||
private final String label = StringUtils.getRandomString(42);
|
private final String label = StringUtils.getRandomString(42);
|
||||||
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
||||||
|
|
||||||
protected abstract KeyPair generateKeyPair();
|
protected abstract KeyPair generateKeyPair();
|
||||||
|
|
||||||
protected abstract byte[] sign(String label, byte[] toSign,
|
protected abstract byte[] sign(String label, byte[] toSign,
|
||||||
byte[] privateKey) throws GeneralSecurityException;
|
PrivateKey privateKey) throws GeneralSecurityException;
|
||||||
|
|
||||||
protected abstract boolean verify(byte[] signature, String label,
|
protected abstract boolean verify(byte[] signature, String label,
|
||||||
byte[] signed, byte[] publicKey) throws GeneralSecurityException;
|
byte[] signed, PublicKey publicKey) throws GeneralSecurityException;
|
||||||
|
|
||||||
SignatureTest() {
|
SignatureTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
KeyPair k = generateKeyPair();
|
KeyPair k = generateKeyPair();
|
||||||
publicKey = k.getPublic().getEncoded();
|
publicKey = k.getPublic();
|
||||||
privateKey = k.getPrivate().getEncoded();
|
privateKey = k.getPrivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -51,7 +54,7 @@ public abstract class SignatureTest extends BrambleTestCase {
|
|||||||
public void testDifferentKeysProduceDifferentSignatures() throws Exception {
|
public void testDifferentKeysProduceDifferentSignatures() throws Exception {
|
||||||
// Generate second private key
|
// Generate second private key
|
||||||
KeyPair k2 = generateKeyPair();
|
KeyPair k2 = generateKeyPair();
|
||||||
byte[] privateKey2 = k2.getPrivate().getEncoded();
|
PrivateKey privateKey2 = k2.getPrivate();
|
||||||
// Calculate the signature with each key
|
// Calculate the signature with each key
|
||||||
byte[] sig1 = sign(label, inputBytes, privateKey);
|
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||||
byte[] sig2 = sign(label, inputBytes, privateKey2);
|
byte[] sig2 = sign(label, inputBytes, privateKey2);
|
||||||
@@ -92,7 +95,7 @@ public abstract class SignatureTest extends BrambleTestCase {
|
|||||||
public void testDifferentKeyFailsVerification() throws Exception {
|
public void testDifferentKeyFailsVerification() throws Exception {
|
||||||
// Generate second private key
|
// Generate second private key
|
||||||
KeyPair k2 = generateKeyPair();
|
KeyPair k2 = generateKeyPair();
|
||||||
byte[] privateKey2 = k2.getPrivate().getEncoded();
|
PrivateKey privateKey2 = k2.getPrivate();
|
||||||
// calculate the signature with different key, should fail to verify
|
// calculate the signature with different key, should fail to verify
|
||||||
byte[] sig = sign(label, inputBytes, privateKey2);
|
byte[] sig = sign(label, inputBytes, privateKey2);
|
||||||
assertFalse(verify(sig, label, inputBytes, publicKey));
|
assertFalse(verify(sig, label, inputBytes, publicKey));
|
||||||
|
|||||||
@@ -1,23 +1,30 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertAllDifferent;
|
|
||||||
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertMatches;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class TransportKeyDerivationTest extends BrambleTestCase {
|
public class TransportKeyDerivationTest extends BrambleTestCase {
|
||||||
|
|
||||||
@@ -29,70 +36,70 @@ public class TransportKeyDerivationTest extends BrambleTestCase {
|
|||||||
private final SecretKey rootKey = getSecretKey();
|
private final SecretKey rootKey = getSecretKey();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreDistinct() {
|
public void testRotationKeysAreDistinct() {
|
||||||
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, false, true);
|
rootKey, 123, false, true);
|
||||||
assertAllDifferent(kA);
|
assertAllDifferent(kA);
|
||||||
assertAllDifferent(kB);
|
assertAllDifferent(kB);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreNotRotatedToPreviousPeriod() {
|
public void testRotationKeysAreNotRotatedToPreviousPeriod() {
|
||||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 122);
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 122);
|
||||||
assertSame(k, k1);
|
assertSame(k, k1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreNotRotatedToCurrentPeriod() {
|
public void testRotationKeysAreNotRotatedToCurrentPeriod() {
|
||||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 123);
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 123);
|
||||||
assertSame(k, k1);
|
assertSame(k, k1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedByOnePeriod() {
|
public void testRotationKeysAreRotatedByOnePeriod() {
|
||||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 124);
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 124);
|
||||||
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
|
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
|
||||||
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
|
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedByTwoPeriods() {
|
public void testRotationKeysAreRotatedByTwoPeriods() {
|
||||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 125);
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 125);
|
||||||
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
|
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedByThreePeriods() {
|
public void testRotationKeysAreRotatedByThreePeriods() {
|
||||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 126);
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 126);
|
||||||
assertAllDifferent(k, k1);
|
assertAllDifferent(k, k1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCurrentKeysMatchContact() {
|
public void testCurrentRotationKeysMatchContact() {
|
||||||
// Start in time period 123
|
// Start in time period 123
|
||||||
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, false, true);
|
rootKey, 123, false, true);
|
||||||
// Alice's incoming keys should equal Bob's outgoing keys
|
// Alice's incoming keys should equal Bob's outgoing keys
|
||||||
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
|
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
|
||||||
// Bob's incoming keys should equal Alice's outgoing keys
|
// Bob's incoming keys should equal Alice's outgoing keys
|
||||||
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
|
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
|
||||||
// Rotate into the future
|
// Rotate into the future
|
||||||
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
kA = transportCrypto.updateTransportKeys(kA, 456);
|
||||||
kB = transportCrypto.rotateTransportKeys(kB, 456);
|
kB = transportCrypto.updateTransportKeys(kB, 456);
|
||||||
// Alice's incoming keys should equal Bob's outgoing keys
|
// Alice's incoming keys should equal Bob's outgoing keys
|
||||||
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
|
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
|
||||||
// Bob's incoming keys should equal Alice's outgoing keys
|
// Bob's incoming keys should equal Alice's outgoing keys
|
||||||
@@ -100,23 +107,23 @@ public class TransportKeyDerivationTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPreviousKeysMatchContact() {
|
public void testPreviousRotationKeysMatchContact() {
|
||||||
// Start in time period 123
|
// Start in time period 123
|
||||||
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, false, true);
|
rootKey, 123, false, true);
|
||||||
// Compare Alice's previous keys in period 456 with Bob's current keys
|
// Compare Alice's previous keys in period 456 with Bob's current keys
|
||||||
// in period 455
|
// in period 455
|
||||||
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
kA = transportCrypto.updateTransportKeys(kA, 456);
|
||||||
kB = transportCrypto.rotateTransportKeys(kB, 455);
|
kB = transportCrypto.updateTransportKeys(kB, 455);
|
||||||
// Alice's previous incoming keys should equal Bob's current
|
// Alice's previous incoming keys should equal Bob's current
|
||||||
// outgoing keys
|
// outgoing keys
|
||||||
assertMatches(kA.getPreviousIncomingKeys(),
|
assertMatches(kA.getPreviousIncomingKeys(),
|
||||||
kB.getCurrentOutgoingKeys());
|
kB.getCurrentOutgoingKeys());
|
||||||
// Compare Alice's current keys in period 456 with Bob's previous keys
|
// Compare Alice's current keys in period 456 with Bob's previous keys
|
||||||
// in period 457
|
// in period 457
|
||||||
kB = transportCrypto.rotateTransportKeys(kB, 457);
|
kB = transportCrypto.updateTransportKeys(kB, 457);
|
||||||
// Bob's previous incoming keys should equal Alice's current
|
// Bob's previous incoming keys should equal Alice's current
|
||||||
// outgoing keys
|
// outgoing keys
|
||||||
assertMatches(kB.getPreviousIncomingKeys(),
|
assertMatches(kB.getPreviousIncomingKeys(),
|
||||||
@@ -124,44 +131,208 @@ public class TransportKeyDerivationTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNextKeysMatchContact() {
|
public void testNextRotationKeysMatchContact() {
|
||||||
// Start in time period 123
|
// Start in time period 123
|
||||||
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys kA = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys kB = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, false, true);
|
rootKey, 123, false, true);
|
||||||
// Compare Alice's current keys in period 456 with Bob's next keys in
|
// Compare Alice's current keys in period 456 with Bob's next keys in
|
||||||
// period 455
|
// period 455
|
||||||
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
kA = transportCrypto.updateTransportKeys(kA, 456);
|
||||||
kB = transportCrypto.rotateTransportKeys(kB, 455);
|
kB = transportCrypto.updateTransportKeys(kB, 455);
|
||||||
// Bob's next incoming keys should equal Alice's current outgoing keys
|
// Bob's next incoming keys should equal Alice's current outgoing keys
|
||||||
assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys());
|
assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys());
|
||||||
// Compare Alice's next keys in period 456 with Bob's current keys
|
// Compare Alice's next keys in period 456 with Bob's current keys
|
||||||
// in period 457
|
// in period 457
|
||||||
kB = transportCrypto.rotateTransportKeys(kB, 457);
|
kB = transportCrypto.updateTransportKeys(kB, 457);
|
||||||
// Alice's next incoming keys should equal Bob's current outgoing keys
|
// Alice's next incoming keys should equal Bob's current outgoing keys
|
||||||
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
|
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRootKeyAffectsOutput() {
|
public void testRootKeyAffectsRotationKeyDerivation() {
|
||||||
SecretKey rootKey1 = getSecretKey();
|
SecretKey rootKey1 = getSecretKey();
|
||||||
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
|
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
|
||||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys k1 = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey1, 123, true, true);
|
rootKey1, 123, true, true);
|
||||||
assertAllDifferent(k, k1);
|
assertAllDifferent(k, k1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransportIdAffectsOutput() {
|
public void testTransportIdAffectsRotationKeyDerivation() {
|
||||||
TransportId transportId1 = getTransportId();
|
TransportId transportId1 = getTransportId();
|
||||||
assertNotEquals(transportId.getString(), transportId1.getString());
|
assertNotEquals(transportId.getString(), transportId1.getString());
|
||||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
|
TransportKeys k1 = transportCrypto.deriveRotationKeys(transportId1,
|
||||||
rootKey, 123, true, true);
|
rootKey, 123, true, true);
|
||||||
assertAllDifferent(k, k1);
|
assertAllDifferent(k, k1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeKeysAreDistinct() {
|
||||||
|
TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, false);
|
||||||
|
assertAllDifferent(kA);
|
||||||
|
assertAllDifferent(kB);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeKeysAreNotUpdatedToPreviousPeriod() {
|
||||||
|
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 122);
|
||||||
|
assertSame(k, k1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeKeysAreNotUpdatedToCurrentPeriod() {
|
||||||
|
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 123);
|
||||||
|
assertSame(k, k1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeKeysAreUpdatedByOnePeriod() {
|
||||||
|
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 124);
|
||||||
|
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
|
||||||
|
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeKeysAreUpdatedByTwoPeriods() {
|
||||||
|
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 125);
|
||||||
|
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeKeysAreUpdatedByThreePeriods() {
|
||||||
|
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys k1 = transportCrypto.updateTransportKeys(k, 126);
|
||||||
|
assertAllDifferent(k, k1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCurrentHandshakeKeysMatchContact() {
|
||||||
|
// Start in time period 123
|
||||||
|
TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, false);
|
||||||
|
// Alice's incoming keys should equal Bob's outgoing keys
|
||||||
|
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
|
||||||
|
// Bob's incoming keys should equal Alice's outgoing keys
|
||||||
|
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
|
||||||
|
// Update into the future
|
||||||
|
kA = transportCrypto.updateTransportKeys(kA, 456);
|
||||||
|
kB = transportCrypto.updateTransportKeys(kB, 456);
|
||||||
|
// Alice's incoming keys should equal Bob's outgoing keys
|
||||||
|
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
|
||||||
|
// Bob's incoming keys should equal Alice's outgoing keys
|
||||||
|
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPreviousHandshakeKeysMatchContact() {
|
||||||
|
// Start in time period 123
|
||||||
|
TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, false);
|
||||||
|
// Compare Alice's previous keys in period 456 with Bob's current keys
|
||||||
|
// in period 455
|
||||||
|
kA = transportCrypto.updateTransportKeys(kA, 456);
|
||||||
|
kB = transportCrypto.updateTransportKeys(kB, 455);
|
||||||
|
// Alice's previous incoming keys should equal Bob's current
|
||||||
|
// outgoing keys
|
||||||
|
assertMatches(kA.getPreviousIncomingKeys(),
|
||||||
|
kB.getCurrentOutgoingKeys());
|
||||||
|
// Compare Alice's current keys in period 456 with Bob's previous keys
|
||||||
|
// in period 457
|
||||||
|
kB = transportCrypto.updateTransportKeys(kB, 457);
|
||||||
|
// Bob's previous incoming keys should equal Alice's current
|
||||||
|
// outgoing keys
|
||||||
|
assertMatches(kB.getPreviousIncomingKeys(),
|
||||||
|
kA.getCurrentOutgoingKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNextHandshakeKeysMatchContact() {
|
||||||
|
// Start in time period 123
|
||||||
|
TransportKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, false);
|
||||||
|
// Compare Alice's current keys in period 456 with Bob's next keys in
|
||||||
|
// period 455
|
||||||
|
kA = transportCrypto.updateTransportKeys(kA, 456);
|
||||||
|
kB = transportCrypto.updateTransportKeys(kB, 455);
|
||||||
|
// Bob's next incoming keys should equal Alice's current outgoing keys
|
||||||
|
assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys());
|
||||||
|
// Compare Alice's next keys in period 456 with Bob's current keys
|
||||||
|
// in period 457
|
||||||
|
kB = transportCrypto.updateTransportKeys(kB, 457);
|
||||||
|
// Alice's next incoming keys should equal Bob's current outgoing keys
|
||||||
|
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRootKeyAffectsHandshakeKeyDerivation() {
|
||||||
|
SecretKey rootKey1 = getSecretKey();
|
||||||
|
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
|
||||||
|
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys k1 = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey1, 123, true);
|
||||||
|
assertAllDifferent(k, k1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransportIdAffectsHandshakeKeyDerivation() {
|
||||||
|
TransportId transportId1 = getTransportId();
|
||||||
|
assertNotEquals(transportId.getString(), transportId1.getString());
|
||||||
|
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
|
||||||
|
rootKey, 123, true);
|
||||||
|
TransportKeys k1 = transportCrypto.deriveHandshakeKeys(transportId1,
|
||||||
|
rootKey, 123, true);
|
||||||
|
assertAllDifferent(k, k1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAllDifferent(TransportKeys... transportKeys) {
|
||||||
|
List<SecretKey> secretKeys = new ArrayList<>();
|
||||||
|
for (TransportKeys k : transportKeys) {
|
||||||
|
secretKeys.add(k.getPreviousIncomingKeys().getTagKey());
|
||||||
|
secretKeys.add(k.getPreviousIncomingKeys().getHeaderKey());
|
||||||
|
secretKeys.add(k.getCurrentIncomingKeys().getTagKey());
|
||||||
|
secretKeys.add(k.getCurrentIncomingKeys().getHeaderKey());
|
||||||
|
secretKeys.add(k.getNextIncomingKeys().getTagKey());
|
||||||
|
secretKeys.add(k.getNextIncomingKeys().getHeaderKey());
|
||||||
|
secretKeys.add(k.getCurrentOutgoingKeys().getTagKey());
|
||||||
|
secretKeys.add(k.getCurrentOutgoingKeys().getHeaderKey());
|
||||||
|
}
|
||||||
|
assertAllDifferent(secretKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAllDifferent(List<SecretKey> keys) {
|
||||||
|
Set<Bytes> set = new HashSet<>();
|
||||||
|
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertMatches(IncomingKeys in, OutgoingKeys out) {
|
||||||
|
assertArrayEquals(in.getTagKey().getBytes(),
|
||||||
|
out.getTagKey().getBytes());
|
||||||
|
assertArrayEquals(in.getHeaderKey().getBytes(),
|
||||||
|
out.getHeaderKey().getBytes());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import org.briarproject.bramble.api.contact.ContactId;
|
|||||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.ContactExistsException;
|
import org.briarproject.bramble.api.db.ContactExistsException;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -46,11 +48,10 @@ import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
|
|||||||
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
|
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
|
||||||
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
||||||
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
|
||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.CaptureArgumentAction;
|
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||||
@@ -66,7 +67,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
||||||
@@ -75,13 +75,14 @@ import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERE
|
|||||||
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
|
import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getClientId;
|
import static org.briarproject.bramble.test.TestUtils.getClientId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getContact;
|
import static org.briarproject.bramble.test.TestUtils.getContact;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getIdentity;
|
import static org.briarproject.bramble.test.TestUtils.getIdentity;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
@@ -117,7 +118,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
private final int maxLatency;
|
private final int maxLatency;
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final Contact contact;
|
private final Contact contact;
|
||||||
private final TransportKeySetId keySetId;
|
private final KeySetId keySetId;
|
||||||
private final PendingContactId pendingContactId;
|
private final PendingContactId pendingContactId;
|
||||||
|
|
||||||
public DatabaseComponentImplTest() {
|
public DatabaseComponentImplTest() {
|
||||||
@@ -139,7 +140,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
contact = getContact(author, localAuthor.getId(), true);
|
contact = getContact(author, localAuthor.getId(), true);
|
||||||
contactId = contact.getId();
|
contactId = contact.getId();
|
||||||
alias = contact.getAlias();
|
alias = contact.getAlias();
|
||||||
keySetId = new TransportKeySetId(345);
|
keySetId = new KeySetId(345);
|
||||||
pendingContactId = new PendingContactId(getRandomId());
|
pendingContactId = new PendingContactId(getRandomId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,24 +285,15 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the contact is in the DB (which it's not)
|
// Check whether the contact is in the DB (which it's not)
|
||||||
exactly(17).of(database).startTransaction();
|
exactly(16).of(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
exactly(17).of(database).containsContact(txn, contactId);
|
exactly(16).of(database).containsContact(txn, contactId);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(17).of(database).abortTransaction(txn);
|
exactly(16).of(database).abortTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
eventExecutor, shutdownManager);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
try {
|
|
||||||
db.transaction(false, transaction ->
|
|
||||||
db.addHandshakeKeys(transaction, contactId,
|
|
||||||
createHandshakeKeys()));
|
|
||||||
fail();
|
|
||||||
} catch (NoSuchContactException expected) {
|
|
||||||
// Expected
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.addTransportKeys(transaction, contactId,
|
db.addTransportKeys(transaction, contactId,
|
||||||
@@ -476,8 +468,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
|
PublicKey publicKey = getAgreementPublicKey();
|
||||||
byte[] privateKey = getRandomBytes(123);
|
PrivateKey privateKey = getAgreementPrivateKey();
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.setHandshakeKeyPair(transaction, localAuthor.getId(),
|
db.setHandshakeKeyPair(transaction, localAuthor.getId(),
|
||||||
publicKey, privateKey));
|
publicKey, privateKey));
|
||||||
@@ -497,8 +489,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
exactly(8).of(database).containsGroup(txn, groupId);
|
exactly(8).of(database).containsGroup(txn, groupId);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(8).of(database).abortTransaction(txn);
|
exactly(8).of(database).abortTransaction(txn);
|
||||||
// This is needed for getMessageStatus() and setGroupVisibility()
|
// Allow other checks to pass
|
||||||
exactly(2).of(database).containsContact(txn, contactId);
|
allowing(database).containsContact(txn, contactId);
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
@@ -581,8 +573,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
exactly(11).of(database).containsMessage(txn, messageId);
|
exactly(11).of(database).containsMessage(txn, messageId);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(11).of(database).abortTransaction(txn);
|
exactly(11).of(database).abortTransaction(txn);
|
||||||
// This is needed for getMessageStatus() to proceed
|
// Allow other checks to pass
|
||||||
exactly(1).of(database).containsContact(txn, contactId);
|
allowing(database).containsContact(txn, contactId);
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
@@ -682,15 +674,38 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the transport is in the DB (which it's not)
|
// Check whether the transport is in the DB (which it's not)
|
||||||
exactly(5).of(database).startTransaction();
|
exactly(8).of(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
exactly(5).of(database).containsTransport(txn, transportId);
|
exactly(8).of(database).containsTransport(txn, transportId);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(5).of(database).abortTransaction(txn);
|
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));
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
eventExecutor, shutdownManager);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.transaction(false, transaction ->
|
||||||
|
db.addTransportKeys(transaction, contactId,
|
||||||
|
createHandshakeKeys()));
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchTransportException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.transaction(false, transaction ->
|
||||||
|
db.addTransportKeys(transaction, pendingContactId,
|
||||||
|
createHandshakeKeys()));
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchTransportException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.getTransportKeys(transaction, transportId));
|
db.getTransportKeys(transaction, transportId));
|
||||||
@@ -710,7 +725,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.removeTransport(transaction, transportId));
|
db.removeTransportKeys(transaction, transportId, keySetId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchTransportException expected) {
|
} catch (NoSuchTransportException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
@@ -718,7 +733,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.removeTransportKeys(transaction, transportId, keySetId));
|
db.removeTransport(transaction, transportId));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchTransportException expected) {
|
} catch (NoSuchTransportException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
@@ -732,6 +747,15 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
} catch (NoSuchTransportException expected) {
|
} catch (NoSuchTransportException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.transaction(false, transaction ->
|
||||||
|
db.setTransportKeysActive(transaction, transportId,
|
||||||
|
keySetId));
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchTransportException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -751,7 +775,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.addHandshakeKeys(transaction, pendingContactId,
|
db.addTransportKeys(transaction, pendingContactId,
|
||||||
createHandshakeKeys()));
|
createHandshakeKeys()));
|
||||||
fail();
|
fail();
|
||||||
} catch (NoSuchPendingContactException expected) {
|
} catch (NoSuchPendingContactException expected) {
|
||||||
@@ -1167,7 +1191,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
public void testTransportKeys() throws Exception {
|
public void testTransportKeys() throws Exception {
|
||||||
TransportKeys transportKeys = createTransportKeys();
|
TransportKeys transportKeys = createTransportKeys();
|
||||||
TransportKeySet ks =
|
TransportKeySet ks =
|
||||||
new TransportKeySet(keySetId, contactId, transportKeys);
|
new TransportKeySet(keySetId, contactId, null, transportKeys);
|
||||||
Collection<TransportKeySet> keys = singletonList(ks);
|
Collection<TransportKeySet> keys = singletonList(ks);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
@@ -1295,7 +1319,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private HandshakeKeys createHandshakeKeys() {
|
private TransportKeys createHandshakeKeys() {
|
||||||
SecretKey inPrevTagKey = getSecretKey();
|
SecretKey inPrevTagKey = getSecretKey();
|
||||||
SecretKey inPrevHeaderKey = getSecretKey();
|
SecretKey inPrevHeaderKey = getSecretKey();
|
||||||
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
|
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
|
||||||
@@ -1312,7 +1336,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
SecretKey outCurrHeaderKey = getSecretKey();
|
SecretKey outCurrHeaderKey = getSecretKey();
|
||||||
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
|
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
|
||||||
2, 456, true);
|
2, 456, true);
|
||||||
return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
|
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr,
|
||||||
getSecretKey(), true);
|
getSecretKey(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package org.briarproject.bramble.db;
|
|||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -22,13 +24,10 @@ import org.briarproject.bramble.api.sync.MessageId;
|
|||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||||
import org.briarproject.bramble.api.sync.validation.MessageState;
|
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
|
||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.system.SystemClock;
|
import org.briarproject.bramble.system.SystemClock;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
@@ -58,7 +57,6 @@ import static java.util.Collections.singletonList;
|
|||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
|
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
|
||||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
@@ -72,13 +70,14 @@ import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPAC
|
|||||||
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
|
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
|
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
|
||||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getClientId;
|
import static org.briarproject.bramble.test.TestUtils.getClientId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getIdentity;
|
import static org.briarproject.bramble.test.TestUtils.getIdentity;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getPendingContact;
|
import static org.briarproject.bramble.test.TestUtils.getPendingContact;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||||
@@ -112,8 +111,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
private final MessageId messageId;
|
private final MessageId messageId;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final TransportKeySetId keySetId, keySetId1;
|
private final KeySetId keySetId, keySetId1;
|
||||||
private final HandshakeKeySetId handshakeKeySetId, handshakeKeySetId1;
|
|
||||||
private final PendingContact pendingContact;
|
private final PendingContact pendingContact;
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
|
|
||||||
@@ -129,10 +127,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
messageId = message.getId();
|
messageId = message.getId();
|
||||||
transportId = getTransportId();
|
transportId = getTransportId();
|
||||||
contactId = new ContactId(1);
|
contactId = new ContactId(1);
|
||||||
keySetId = new TransportKeySetId(1);
|
keySetId = new KeySetId(1);
|
||||||
keySetId1 = new TransportKeySetId(2);
|
keySetId1 = new KeySetId(2);
|
||||||
handshakeKeySetId = new HandshakeKeySetId(1);
|
|
||||||
handshakeKeySetId1 = new HandshakeKeySetId(2);
|
|
||||||
pendingContact = getPendingContact();
|
pendingContact = getPendingContact();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -701,14 +697,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate the transport keys
|
// Update the transport keys
|
||||||
TransportKeys rotated = createTransportKeys(timePeriod + 1, active);
|
TransportKeys updated = createTransportKeys(timePeriod + 1, active);
|
||||||
TransportKeys rotated1 =
|
TransportKeys updated1 =
|
||||||
createTransportKeys(timePeriod1 + 1, active);
|
createTransportKeys(timePeriod1 + 1, active);
|
||||||
db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId,
|
db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId,
|
||||||
rotated));
|
null, updated));
|
||||||
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId,
|
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId,
|
||||||
rotated1));
|
null, updated1));
|
||||||
|
|
||||||
// Retrieve the transport keys again
|
// Retrieve the transport keys again
|
||||||
allKeys = db.getTransportKeys(txn, transportId);
|
allKeys = db.getTransportKeys(txn, transportId);
|
||||||
@@ -716,10 +712,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
for (TransportKeySet ks : allKeys) {
|
for (TransportKeySet ks : allKeys) {
|
||||||
assertEquals(contactId, ks.getContactId());
|
assertEquals(contactId, ks.getContactId());
|
||||||
if (ks.getKeySetId().equals(keySetId)) {
|
if (ks.getKeySetId().equals(keySetId)) {
|
||||||
assertKeysEquals(rotated, ks.getKeys());
|
assertKeysEquals(updated, ks.getKeys());
|
||||||
} else {
|
} else {
|
||||||
assertEquals(keySetId1, ks.getKeySetId());
|
assertEquals(keySetId1, ks.getKeySetId());
|
||||||
assertKeysEquals(rotated1, ks.getKeys());
|
assertKeysEquals(updated1, ks.getKeys());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,6 +739,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
actual.getNextIncomingKeys());
|
actual.getNextIncomingKeys());
|
||||||
assertKeysEquals(expected.getCurrentOutgoingKeys(),
|
assertKeysEquals(expected.getCurrentOutgoingKeys(),
|
||||||
actual.getCurrentOutgoingKeys());
|
actual.getCurrentOutgoingKeys());
|
||||||
|
if (expected.isHandshakeMode()) {
|
||||||
|
assertTrue(actual.isHandshakeMode());
|
||||||
|
assertArrayEquals(expected.getRootKey().getBytes(),
|
||||||
|
actual.getRootKey().getBytes());
|
||||||
|
assertEquals(expected.isAlice(), actual.isAlice());
|
||||||
|
} else {
|
||||||
|
assertFalse(actual.isHandshakeMode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertKeysEquals(IncomingKeys expected, IncomingKeys actual) {
|
private void assertKeysEquals(IncomingKeys expected, IncomingKeys actual) {
|
||||||
@@ -771,154 +775,135 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
boolean alice = random.nextBoolean();
|
boolean alice = random.nextBoolean();
|
||||||
SecretKey rootKey = getSecretKey();
|
SecretKey rootKey = getSecretKey();
|
||||||
SecretKey rootKey1 = getSecretKey();
|
SecretKey rootKey1 = getSecretKey();
|
||||||
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
||||||
HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
|
TransportKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
|
||||||
|
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Initially there should be no handshake keys in the database
|
// Initially there should be no handshake keys in the database
|
||||||
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
|
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
|
||||||
|
|
||||||
// Add the contact, the transport and the handshake keys
|
// Add the contact, the transport and the handshake keys
|
||||||
db.addIdentity(txn, identity);
|
db.addIdentity(txn, identity);
|
||||||
assertEquals(contactId,
|
assertEquals(contactId,
|
||||||
db.addContact(txn, author, localAuthor.getId(), true));
|
db.addContact(txn, author, localAuthor.getId(), true));
|
||||||
db.addTransport(txn, transportId, 123);
|
db.addTransport(txn, transportId, 123);
|
||||||
assertEquals(handshakeKeySetId,
|
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||||
db.addHandshakeKeys(txn, contactId, keys));
|
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
|
||||||
assertEquals(handshakeKeySetId1,
|
|
||||||
db.addHandshakeKeys(txn, contactId, keys1));
|
|
||||||
|
|
||||||
// Retrieve the handshake keys
|
// Retrieve the handshake keys
|
||||||
Collection<HandshakeKeySet> allKeys =
|
Collection<TransportKeySet> allKeys =
|
||||||
db.getHandshakeKeys(txn, transportId);
|
db.getTransportKeys(txn, transportId);
|
||||||
assertEquals(2, allKeys.size());
|
assertEquals(2, allKeys.size());
|
||||||
for (HandshakeKeySet ks : allKeys) {
|
for (TransportKeySet ks : allKeys) {
|
||||||
assertEquals(contactId, ks.getContactId());
|
assertEquals(contactId, ks.getContactId());
|
||||||
assertNull(ks.getPendingContactId());
|
assertNull(ks.getPendingContactId());
|
||||||
if (ks.getKeySetId().equals(handshakeKeySetId)) {
|
if (ks.getKeySetId().equals(keySetId)) {
|
||||||
assertKeysEquals(keys, ks.getKeys());
|
assertKeysEquals(keys, ks.getKeys());
|
||||||
} else {
|
} else {
|
||||||
assertEquals(handshakeKeySetId1, ks.getKeySetId());
|
assertEquals(keySetId1, ks.getKeySetId());
|
||||||
assertKeysEquals(keys1, ks.getKeys());
|
assertKeysEquals(keys1, ks.getKeys());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the handshake keys
|
// Update the handshake keys
|
||||||
HandshakeKeys updated =
|
TransportKeys updated =
|
||||||
createHandshakeKeys(timePeriod + 1, rootKey, alice);
|
createHandshakeKeys(timePeriod + 1, rootKey, alice);
|
||||||
HandshakeKeys updated1 =
|
TransportKeys updated1 =
|
||||||
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
|
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
|
||||||
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
|
db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId,
|
||||||
contactId, updated));
|
null, updated));
|
||||||
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
|
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId,
|
||||||
contactId, updated1));
|
null, updated1));
|
||||||
|
|
||||||
// Retrieve the handshake keys again
|
// Retrieve the handshake keys again
|
||||||
allKeys = db.getHandshakeKeys(txn, transportId);
|
allKeys = db.getTransportKeys(txn, transportId);
|
||||||
assertEquals(2, allKeys.size());
|
assertEquals(2, allKeys.size());
|
||||||
for (HandshakeKeySet ks : allKeys) {
|
for (TransportKeySet ks : allKeys) {
|
||||||
assertEquals(contactId, ks.getContactId());
|
assertEquals(contactId, ks.getContactId());
|
||||||
assertNull(ks.getPendingContactId());
|
assertNull(ks.getPendingContactId());
|
||||||
if (ks.getKeySetId().equals(handshakeKeySetId)) {
|
if (ks.getKeySetId().equals(keySetId)) {
|
||||||
assertKeysEquals(updated, ks.getKeys());
|
assertKeysEquals(updated, ks.getKeys());
|
||||||
} else {
|
} else {
|
||||||
assertEquals(handshakeKeySetId1, ks.getKeySetId());
|
assertEquals(keySetId1, ks.getKeySetId());
|
||||||
assertKeysEquals(updated1, ks.getKeys());
|
assertKeysEquals(updated1, ks.getKeys());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removing the contact should remove the handshake keys
|
// Removing the contact should remove the handshake keys
|
||||||
db.removeContact(txn, contactId);
|
db.removeContact(txn, contactId);
|
||||||
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
|
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertKeysEquals(HandshakeKeys expected,
|
|
||||||
HandshakeKeys actual) {
|
|
||||||
assertEquals(expected.getTransportId(), actual.getTransportId());
|
|
||||||
assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
|
|
||||||
assertArrayEquals(expected.getRootKey().getBytes(),
|
|
||||||
actual.getRootKey().getBytes());
|
|
||||||
assertEquals(expected.isAlice(), actual.isAlice());
|
|
||||||
assertKeysEquals(expected.getPreviousIncomingKeys(),
|
|
||||||
actual.getPreviousIncomingKeys());
|
|
||||||
assertKeysEquals(expected.getCurrentIncomingKeys(),
|
|
||||||
actual.getCurrentIncomingKeys());
|
|
||||||
assertKeysEquals(expected.getNextIncomingKeys(),
|
|
||||||
actual.getNextIncomingKeys());
|
|
||||||
assertKeysEquals(expected.getCurrentOutgoingKeys(),
|
|
||||||
actual.getCurrentOutgoingKeys());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHandshakeKeysForPendingContact() throws Exception {
|
public void testHandshakeKeysForPendingContact() throws Exception {
|
||||||
long timePeriod = 123, timePeriod1 = 234;
|
long timePeriod = 123, timePeriod1 = 234;
|
||||||
boolean alice = random.nextBoolean();
|
boolean alice = random.nextBoolean();
|
||||||
SecretKey rootKey = getSecretKey();
|
SecretKey rootKey = getSecretKey();
|
||||||
SecretKey rootKey1 = getSecretKey();
|
SecretKey rootKey1 = getSecretKey();
|
||||||
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
||||||
HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
|
TransportKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
|
||||||
|
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Initially there should be no handshake keys in the database
|
// Initially there should be no handshake keys in the database
|
||||||
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
|
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
|
||||||
|
|
||||||
// Add the pending contact, the transport and the handshake keys
|
// Add the pending contact, the transport and the handshake keys
|
||||||
db.addPendingContact(txn, pendingContact);
|
db.addPendingContact(txn, pendingContact);
|
||||||
db.addTransport(txn, transportId, 123);
|
db.addTransport(txn, transportId, 123);
|
||||||
assertEquals(handshakeKeySetId, db.addHandshakeKeys(txn,
|
assertEquals(keySetId,
|
||||||
pendingContact.getId(), keys));
|
db.addTransportKeys(txn, pendingContact.getId(), keys));
|
||||||
assertEquals(handshakeKeySetId1, db.addHandshakeKeys(txn,
|
assertEquals(keySetId1,
|
||||||
pendingContact.getId(), keys1));
|
db.addTransportKeys(txn, pendingContact.getId(), keys1));
|
||||||
|
|
||||||
// Retrieve the handshake keys
|
// Retrieve the handshake keys
|
||||||
Collection<HandshakeKeySet> allKeys =
|
Collection<TransportKeySet> allKeys =
|
||||||
db.getHandshakeKeys(txn, transportId);
|
db.getTransportKeys(txn, transportId);
|
||||||
assertEquals(2, allKeys.size());
|
assertEquals(2, allKeys.size());
|
||||||
for (HandshakeKeySet ks : allKeys) {
|
for (TransportKeySet ks : allKeys) {
|
||||||
assertNull(ks.getContactId());
|
assertNull(ks.getContactId());
|
||||||
assertEquals(pendingContact.getId(), ks.getPendingContactId());
|
assertEquals(pendingContact.getId(), ks.getPendingContactId());
|
||||||
if (ks.getKeySetId().equals(handshakeKeySetId)) {
|
if (ks.getKeySetId().equals(keySetId)) {
|
||||||
assertKeysEquals(keys, ks.getKeys());
|
assertKeysEquals(keys, ks.getKeys());
|
||||||
} else {
|
} else {
|
||||||
assertEquals(handshakeKeySetId1, ks.getKeySetId());
|
assertEquals(keySetId1, ks.getKeySetId());
|
||||||
assertKeysEquals(keys1, ks.getKeys());
|
assertKeysEquals(keys1, ks.getKeys());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the handshake keys
|
// Update the handshake keys
|
||||||
HandshakeKeys updated =
|
TransportKeys updated =
|
||||||
createHandshakeKeys(timePeriod + 1, rootKey, alice);
|
createHandshakeKeys(timePeriod + 1, rootKey, alice);
|
||||||
HandshakeKeys updated1 =
|
TransportKeys updated1 =
|
||||||
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
|
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
|
||||||
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
|
db.updateTransportKeys(txn, new TransportKeySet(keySetId, null,
|
||||||
pendingContact.getId(), updated));
|
pendingContact.getId(), updated));
|
||||||
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
|
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, null,
|
||||||
pendingContact.getId(), updated1));
|
pendingContact.getId(), updated1));
|
||||||
|
|
||||||
// Retrieve the handshake keys again
|
// Retrieve the handshake keys again
|
||||||
allKeys = db.getHandshakeKeys(txn, transportId);
|
allKeys = db.getTransportKeys(txn, transportId);
|
||||||
assertEquals(2, allKeys.size());
|
assertEquals(2, allKeys.size());
|
||||||
for (HandshakeKeySet ks : allKeys) {
|
for (TransportKeySet ks : allKeys) {
|
||||||
assertNull(ks.getContactId());
|
assertNull(ks.getContactId());
|
||||||
assertEquals(pendingContact.getId(), ks.getPendingContactId());
|
assertEquals(pendingContact.getId(), ks.getPendingContactId());
|
||||||
if (ks.getKeySetId().equals(handshakeKeySetId)) {
|
if (ks.getKeySetId().equals(keySetId)) {
|
||||||
assertKeysEquals(updated, ks.getKeys());
|
assertKeysEquals(updated, ks.getKeys());
|
||||||
} else {
|
} else {
|
||||||
assertEquals(handshakeKeySetId1, ks.getKeySetId());
|
assertEquals(keySetId1, ks.getKeySetId());
|
||||||
assertKeysEquals(updated1, ks.getKeys());
|
assertKeysEquals(updated1, ks.getKeys());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removing the pending contact should remove the handshake keys
|
// Removing the pending contact should remove the handshake keys
|
||||||
db.removePendingContact(txn, pendingContact.getId());
|
db.removePendingContact(txn, pendingContact.getId());
|
||||||
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
|
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
db.close();
|
db.close();
|
||||||
@@ -971,7 +956,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
long timePeriod = 123;
|
long timePeriod = 123;
|
||||||
SecretKey rootKey = getSecretKey();
|
SecretKey rootKey = getSecretKey();
|
||||||
boolean alice = random.nextBoolean();
|
boolean alice = random.nextBoolean();
|
||||||
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
||||||
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
|
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
|
||||||
|
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
@@ -982,20 +967,20 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
assertEquals(contactId,
|
assertEquals(contactId,
|
||||||
db.addContact(txn, author, localAuthor.getId(), true));
|
db.addContact(txn, author, localAuthor.getId(), true));
|
||||||
db.addTransport(txn, transportId, 123);
|
db.addTransport(txn, transportId, 123);
|
||||||
assertEquals(handshakeKeySetId,
|
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||||
db.addHandshakeKeys(txn, contactId, keys));
|
|
||||||
|
|
||||||
// Increment the stream counter twice and retrieve the handshake keys
|
// Increment the stream counter twice and retrieve the handshake keys
|
||||||
db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
|
db.incrementStreamCounter(txn, transportId, keySetId);
|
||||||
db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
|
db.incrementStreamCounter(txn, transportId, keySetId);
|
||||||
Collection<HandshakeKeySet> newKeys =
|
Collection<TransportKeySet> newKeys =
|
||||||
db.getHandshakeKeys(txn, transportId);
|
db.getTransportKeys(txn, transportId);
|
||||||
assertEquals(1, newKeys.size());
|
assertEquals(1, newKeys.size());
|
||||||
HandshakeKeySet ks = newKeys.iterator().next();
|
TransportKeySet ks = newKeys.iterator().next();
|
||||||
assertEquals(handshakeKeySetId, ks.getKeySetId());
|
assertEquals(keySetId, ks.getKeySetId());
|
||||||
assertEquals(contactId, ks.getContactId());
|
assertEquals(contactId, ks.getContactId());
|
||||||
HandshakeKeys k = ks.getKeys();
|
TransportKeys k = ks.getKeys();
|
||||||
assertEquals(transportId, k.getTransportId());
|
assertEquals(transportId, k.getTransportId());
|
||||||
|
assertNotNull(k.getRootKey());
|
||||||
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
|
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
|
||||||
assertEquals(alice, k.isAlice());
|
assertEquals(alice, k.isAlice());
|
||||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||||
@@ -1064,7 +1049,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
long timePeriod = 123;
|
long timePeriod = 123;
|
||||||
SecretKey rootKey = getSecretKey();
|
SecretKey rootKey = getSecretKey();
|
||||||
boolean alice = random.nextBoolean();
|
boolean alice = random.nextBoolean();
|
||||||
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
TransportKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
||||||
long base = keys.getCurrentIncomingKeys().getWindowBase();
|
long base = keys.getCurrentIncomingKeys().getWindowBase();
|
||||||
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
|
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
|
||||||
|
|
||||||
@@ -1076,21 +1061,21 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
assertEquals(contactId,
|
assertEquals(contactId,
|
||||||
db.addContact(txn, author, localAuthor.getId(), true));
|
db.addContact(txn, author, localAuthor.getId(), true));
|
||||||
db.addTransport(txn, transportId, 123);
|
db.addTransport(txn, transportId, 123);
|
||||||
assertEquals(handshakeKeySetId,
|
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||||
db.addHandshakeKeys(txn, contactId, keys));
|
|
||||||
|
|
||||||
// Update the reordering window and retrieve the handshake keys
|
// Update the reordering window and retrieve the handshake keys
|
||||||
random.nextBytes(bitmap);
|
random.nextBytes(bitmap);
|
||||||
db.setReorderingWindow(txn, handshakeKeySetId, transportId, timePeriod,
|
db.setReorderingWindow(txn, keySetId, transportId, timePeriod,
|
||||||
base + 1, bitmap);
|
base + 1, bitmap);
|
||||||
Collection<HandshakeKeySet> newKeys =
|
Collection<TransportKeySet> newKeys =
|
||||||
db.getHandshakeKeys(txn, transportId);
|
db.getTransportKeys(txn, transportId);
|
||||||
assertEquals(1, newKeys.size());
|
assertEquals(1, newKeys.size());
|
||||||
HandshakeKeySet ks = newKeys.iterator().next();
|
TransportKeySet ks = newKeys.iterator().next();
|
||||||
assertEquals(handshakeKeySetId, ks.getKeySetId());
|
assertEquals(keySetId, ks.getKeySetId());
|
||||||
assertEquals(contactId, ks.getContactId());
|
assertEquals(contactId, ks.getContactId());
|
||||||
HandshakeKeys k = ks.getKeys();
|
TransportKeys k = ks.getKeys();
|
||||||
assertEquals(transportId, k.getTransportId());
|
assertEquals(transportId, k.getTransportId());
|
||||||
|
assertNotNull(k.getRootKey());
|
||||||
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
|
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
|
||||||
assertEquals(alice, k.isAlice());
|
assertEquals(alice, k.isAlice());
|
||||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||||
@@ -2250,8 +2235,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Identity withoutKeys =
|
Identity withoutKeys =
|
||||||
new Identity(localAuthor, null, null, identity.getTimeCreated());
|
new Identity(localAuthor, null, null, identity.getTimeCreated());
|
||||||
assertFalse(withoutKeys.hasHandshakeKeyPair());
|
assertFalse(withoutKeys.hasHandshakeKeyPair());
|
||||||
byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
|
PublicKey publicKey = getAgreementPublicKey();
|
||||||
byte[] privateKey = getRandomBytes(123);
|
PrivateKey privateKey = getAgreementPrivateKey();
|
||||||
|
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
@@ -2262,8 +2247,12 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.setHandshakeKeyPair(txn, localAuthor.getId(), publicKey, privateKey);
|
db.setHandshakeKeyPair(txn, localAuthor.getId(), publicKey, privateKey);
|
||||||
retrieved = db.getIdentity(txn, localAuthor.getId());
|
retrieved = db.getIdentity(txn, localAuthor.getId());
|
||||||
assertTrue(retrieved.hasHandshakeKeyPair());
|
assertTrue(retrieved.hasHandshakeKeyPair());
|
||||||
assertArrayEquals(publicKey, retrieved.getHandshakePublicKey());
|
PublicKey handshakePub = retrieved.getHandshakePublicKey();
|
||||||
assertArrayEquals(privateKey, retrieved.getHandshakePrivateKey());
|
assertNotNull(handshakePub);
|
||||||
|
assertArrayEquals(publicKey.getEncoded(), handshakePub.getEncoded());
|
||||||
|
PrivateKey handshakePriv = retrieved.getHandshakePrivateKey();
|
||||||
|
assertNotNull(handshakePriv);
|
||||||
|
assertArrayEquals(privateKey.getEncoded(), handshakePriv.getEncoded());
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
db.close();
|
db.close();
|
||||||
@@ -2302,7 +2291,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
|
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HandshakeKeys createHandshakeKeys(long timePeriod,
|
private TransportKeys createHandshakeKeys(long timePeriod,
|
||||||
SecretKey rootKey, boolean alice) {
|
SecretKey rootKey, boolean alice) {
|
||||||
SecretKey inPrevTagKey = getSecretKey();
|
SecretKey inPrevTagKey = getSecretKey();
|
||||||
SecretKey inPrevHeaderKey = getSecretKey();
|
SecretKey inPrevHeaderKey = getSecretKey();
|
||||||
@@ -2320,7 +2309,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
SecretKey outCurrHeaderKey = getSecretKey();
|
SecretKey outCurrHeaderKey = getSecretKey();
|
||||||
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
|
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
|
||||||
timePeriod, 456, true);
|
timePeriod, 456, true);
|
||||||
return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
|
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr,
|
||||||
rootKey, alice);
|
rootKey, alice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getIdentity;
|
import static org.briarproject.bramble.test.TestUtils.getIdentity;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
@@ -28,21 +30,16 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
|
|||||||
private final AuthorFactory authorFactory =
|
private final AuthorFactory authorFactory =
|
||||||
context.mock(AuthorFactory.class);
|
context.mock(AuthorFactory.class);
|
||||||
private final Clock clock = context.mock(Clock.class);
|
private final Clock clock = context.mock(Clock.class);
|
||||||
private final PublicKey handshakePublicKey = context.mock(PublicKey.class);
|
|
||||||
private final PrivateKey handshakePrivateKey =
|
|
||||||
context.mock(PrivateKey.class);
|
|
||||||
|
|
||||||
private final Transaction txn = new Transaction(null, false);
|
private final Transaction txn = new Transaction(null, false);
|
||||||
private final Identity identityWithKeys = getIdentity();
|
private final Identity identityWithKeys = getIdentity();
|
||||||
private final LocalAuthor localAuthor = identityWithKeys.getLocalAuthor();
|
private final LocalAuthor localAuthor = identityWithKeys.getLocalAuthor();
|
||||||
private final Identity identityWithoutKeys = new Identity(localAuthor,
|
private final Identity identityWithoutKeys = new Identity(localAuthor,
|
||||||
null, null, identityWithKeys.getTimeCreated());
|
null, null, identityWithKeys.getTimeCreated());
|
||||||
|
private final PublicKey handshakePublicKey = getAgreementPublicKey();
|
||||||
|
private final PrivateKey handshakePrivateKey = getAgreementPrivateKey();
|
||||||
private final KeyPair handshakeKeyPair =
|
private final KeyPair handshakeKeyPair =
|
||||||
new KeyPair(handshakePublicKey, handshakePrivateKey);
|
new KeyPair(handshakePublicKey, handshakePrivateKey);
|
||||||
private final byte[] handshakePublicKeyBytes =
|
|
||||||
identityWithKeys.getHandshakePublicKey();
|
|
||||||
private final byte[] handshakePrivateKeyBytes =
|
|
||||||
identityWithKeys.getHandshakePrivateKey();
|
|
||||||
|
|
||||||
private IdentityManagerImpl identityManager;
|
private IdentityManagerImpl identityManager;
|
||||||
|
|
||||||
@@ -69,12 +66,8 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(singletonList(identityWithoutKeys)));
|
will(returnValue(singletonList(identityWithoutKeys)));
|
||||||
oneOf(crypto).generateAgreementKeyPair();
|
oneOf(crypto).generateAgreementKeyPair();
|
||||||
will(returnValue(handshakeKeyPair));
|
will(returnValue(handshakeKeyPair));
|
||||||
oneOf(handshakePublicKey).getEncoded();
|
|
||||||
will(returnValue(handshakePublicKeyBytes));
|
|
||||||
oneOf(handshakePrivateKey).getEncoded();
|
|
||||||
will(returnValue(handshakePrivateKeyBytes));
|
|
||||||
oneOf(db).setHandshakeKeyPair(txn, localAuthor.getId(),
|
oneOf(db).setHandshakeKeyPair(txn, localAuthor.getId(),
|
||||||
handshakePublicKeyBytes, handshakePrivateKeyBytes);
|
handshakePublicKey, handshakePrivateKey);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
identityManager.onDatabaseOpened(txn);
|
identityManager.onDatabaseOpened(txn);
|
||||||
@@ -104,10 +97,6 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(singletonList(identityWithoutKeys)));
|
will(returnValue(singletonList(identityWithoutKeys)));
|
||||||
oneOf(crypto).generateAgreementKeyPair();
|
oneOf(crypto).generateAgreementKeyPair();
|
||||||
will(returnValue(handshakeKeyPair));
|
will(returnValue(handshakeKeyPair));
|
||||||
oneOf(handshakePublicKey).getEncoded();
|
|
||||||
will(returnValue(handshakePublicKeyBytes));
|
|
||||||
oneOf(handshakePrivateKey).getEncoded();
|
|
||||||
will(returnValue(handshakePrivateKeyBytes));
|
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertEquals(localAuthor, identityManager.getLocalAuthor());
|
assertEquals(localAuthor, identityManager.getLocalAuthor());
|
||||||
|
|||||||
@@ -16,10 +16,13 @@ import org.jmock.lib.legacy.ClassImposteriser;
|
|||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_LABEL;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_LABEL;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
@@ -34,21 +37,17 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
setImposteriser(ClassImposteriser.INSTANCE);
|
setImposteriser(ClassImposteriser.INSTANCE);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
private final PublicKey alicePubKey =
|
private final PublicKey alicePubKey = getAgreementPublicKey();
|
||||||
context.mock(PublicKey.class, "alice");
|
|
||||||
private final byte[] alicePubKeyBytes = getRandomBytes(32);
|
|
||||||
private final byte[] aliceCommit = getRandomBytes(COMMIT_LENGTH);
|
private final byte[] aliceCommit = getRandomBytes(COMMIT_LENGTH);
|
||||||
private final byte[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8);
|
private final byte[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8);
|
||||||
private final byte[] aliceConfirm = getRandomBytes(SecretKey.LENGTH);
|
private final byte[] aliceConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||||
|
|
||||||
private final PublicKey bobPubKey = context.mock(PublicKey.class, "bob");
|
private final PublicKey bobPubKey = getAgreementPublicKey();
|
||||||
private final byte[] bobPubKeyBytes = getRandomBytes(32);
|
|
||||||
private final byte[] bobCommit = getRandomBytes(COMMIT_LENGTH);
|
private final byte[] bobCommit = getRandomBytes(COMMIT_LENGTH);
|
||||||
private final byte[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19);
|
private final byte[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19);
|
||||||
private final byte[] bobConfirm = getRandomBytes(SecretKey.LENGTH);
|
private final byte[] bobConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||||
|
|
||||||
private final PublicKey badPubKey = context.mock(PublicKey.class, "bad");
|
private final PublicKey badPubKey = getAgreementPublicKey();
|
||||||
private final byte[] badPubKeyBytes = getRandomBytes(32);
|
|
||||||
private final byte[] badCommit = getRandomBytes(COMMIT_LENGTH);
|
private final byte[] badCommit = getRandomBytes(COMMIT_LENGTH);
|
||||||
private final byte[] badConfirm = getRandomBytes(SecretKey.LENGTH);
|
private final byte[] badConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||||
|
|
||||||
@@ -64,15 +63,13 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
PayloadEncoder payloadEncoder;
|
PayloadEncoder payloadEncoder;
|
||||||
@Mock
|
@Mock
|
||||||
KeyAgreementTransport transport;
|
KeyAgreementTransport transport;
|
||||||
@Mock
|
|
||||||
PublicKey ourPubKey;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAliceProtocol() throws Exception {
|
public void testAliceProtocol() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(bobCommit, null);
|
Payload theirPayload = new Payload(bobCommit, emptyList());
|
||||||
Payload ourPayload = new Payload(aliceCommit, null);
|
Payload ourPayload = new Payload(aliceCommit, emptyList());
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(alicePubKey, getAgreementPrivateKey());
|
||||||
SecretKey sharedSecret = getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
SecretKey masterKey = getSecretKey();
|
SecretKey masterKey = getSecretKey();
|
||||||
|
|
||||||
@@ -87,24 +84,18 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
will(returnValue(alicePayload));
|
will(returnValue(alicePayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(bobPayload));
|
will(returnValue(bobPayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
|
||||||
will(returnValue(alicePubKeyBytes));
|
|
||||||
allowing(crypto).getAgreementKeyParser();
|
allowing(crypto).getAgreementKeyParser();
|
||||||
will(returnValue(keyParser));
|
will(returnValue(keyParser));
|
||||||
allowing(alicePubKey).getEncoded();
|
|
||||||
will(returnValue(alicePubKeyBytes));
|
|
||||||
allowing(bobPubKey).getEncoded();
|
|
||||||
will(returnValue(bobPubKeyBytes));
|
|
||||||
|
|
||||||
// Alice sends her public key
|
// Alice sends her public key
|
||||||
oneOf(transport).sendKey(alicePubKeyBytes);
|
oneOf(transport).sendKey(alicePubKey.getEncoded());
|
||||||
|
|
||||||
// Alice receives Bob's public key
|
// Alice receives Bob's public key
|
||||||
oneOf(callbacks).connectionWaiting();
|
oneOf(callbacks).connectionWaiting();
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(bobPubKeyBytes));
|
will(returnValue(bobPubKey.getEncoded()));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
|
oneOf(keyParser).parsePublicKey(bobPubKey.getEncoded());
|
||||||
will(returnValue(bobPubKey));
|
will(returnValue(bobPubKey));
|
||||||
|
|
||||||
// Alice verifies Bob's public key
|
// Alice verifies Bob's public key
|
||||||
@@ -114,7 +105,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
// Alice computes shared secret
|
// Alice computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
||||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||||
alicePubKeyBytes, bobPubKeyBytes);
|
alicePubKey.getEncoded(), bobPubKey.getEncoded());
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Alice sends her confirmation record
|
// Alice sends her confirmation record
|
||||||
@@ -146,9 +137,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testBobProtocol() throws Exception {
|
public void testBobProtocol() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(aliceCommit, null);
|
Payload theirPayload = new Payload(aliceCommit, emptyList());
|
||||||
Payload ourPayload = new Payload(bobCommit, null);
|
Payload ourPayload = new Payload(bobCommit, emptyList());
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(bobPubKey, getAgreementPrivateKey());
|
||||||
SecretKey sharedSecret = getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
SecretKey masterKey = getSecretKey();
|
SecretKey masterKey = getSecretKey();
|
||||||
|
|
||||||
@@ -163,20 +154,14 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
will(returnValue(bobPayload));
|
will(returnValue(bobPayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(alicePayload));
|
will(returnValue(alicePayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
|
||||||
will(returnValue(bobPubKeyBytes));
|
|
||||||
allowing(crypto).getAgreementKeyParser();
|
allowing(crypto).getAgreementKeyParser();
|
||||||
will(returnValue(keyParser));
|
will(returnValue(keyParser));
|
||||||
allowing(alicePubKey).getEncoded();
|
|
||||||
will(returnValue(alicePubKeyBytes));
|
|
||||||
allowing(bobPubKey).getEncoded();
|
|
||||||
will(returnValue(bobPubKeyBytes));
|
|
||||||
|
|
||||||
// Bob receives Alice's public key
|
// Bob receives Alice's public key
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(alicePubKeyBytes));
|
will(returnValue(alicePubKey.getEncoded()));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
|
oneOf(keyParser).parsePublicKey(alicePubKey.getEncoded());
|
||||||
will(returnValue(alicePubKey));
|
will(returnValue(alicePubKey));
|
||||||
|
|
||||||
// Bob verifies Alice's public key
|
// Bob verifies Alice's public key
|
||||||
@@ -184,12 +169,12 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
will(returnValue(aliceCommit));
|
will(returnValue(aliceCommit));
|
||||||
|
|
||||||
// Bob sends his public key
|
// Bob sends his public key
|
||||||
oneOf(transport).sendKey(bobPubKeyBytes);
|
oneOf(transport).sendKey(bobPubKey.getEncoded());
|
||||||
|
|
||||||
// Bob computes shared secret
|
// Bob computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
||||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||||
alicePubKeyBytes, bobPubKeyBytes);
|
alicePubKey.getEncoded(), bobPubKey.getEncoded());
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Bob receives Alices's confirmation record
|
// Bob receives Alices's confirmation record
|
||||||
@@ -221,9 +206,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testAliceProtocolAbortOnBadKey() throws Exception {
|
public void testAliceProtocolAbortOnBadKey() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(bobCommit, null);
|
Payload theirPayload = new Payload(bobCommit, emptyList());
|
||||||
Payload ourPayload = new Payload(aliceCommit, null);
|
Payload ourPayload = new Payload(aliceCommit, emptyList());
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(alicePubKey, getAgreementPrivateKey());
|
||||||
|
|
||||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
@@ -232,20 +217,18 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(ourPubKey).getEncoded();
|
|
||||||
will(returnValue(alicePubKeyBytes));
|
|
||||||
allowing(crypto).getAgreementKeyParser();
|
allowing(crypto).getAgreementKeyParser();
|
||||||
will(returnValue(keyParser));
|
will(returnValue(keyParser));
|
||||||
|
|
||||||
// Alice sends her public key
|
// Alice sends her public key
|
||||||
oneOf(transport).sendKey(alicePubKeyBytes);
|
oneOf(transport).sendKey(alicePubKey.getEncoded());
|
||||||
|
|
||||||
// Alice receives a bad public key
|
// Alice receives a bad public key
|
||||||
oneOf(callbacks).connectionWaiting();
|
oneOf(callbacks).connectionWaiting();
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(badPubKeyBytes));
|
will(returnValue(badPubKey.getEncoded()));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
|
oneOf(keyParser).parsePublicKey(badPubKey.getEncoded());
|
||||||
will(returnValue(badPubKey));
|
will(returnValue(badPubKey));
|
||||||
|
|
||||||
// Alice verifies Bob's public key
|
// Alice verifies Bob's public key
|
||||||
@@ -258,7 +241,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
// Alice never computes shared secret
|
// Alice never computes shared secret
|
||||||
never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey,
|
never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey,
|
||||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||||
alicePubKeyBytes, bobPubKeyBytes);
|
alicePubKey.getEncoded(), bobPubKey.getEncoded());
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
@@ -268,9 +251,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testBobProtocolAbortOnBadKey() throws Exception {
|
public void testBobProtocolAbortOnBadKey() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(aliceCommit, null);
|
Payload theirPayload = new Payload(aliceCommit, emptyList());
|
||||||
Payload ourPayload = new Payload(bobCommit, null);
|
Payload ourPayload = new Payload(bobCommit, emptyList());
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(bobPubKey, getAgreementPrivateKey());
|
||||||
|
|
||||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
@@ -279,16 +262,14 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(ourPubKey).getEncoded();
|
|
||||||
will(returnValue(bobPubKeyBytes));
|
|
||||||
allowing(crypto).getAgreementKeyParser();
|
allowing(crypto).getAgreementKeyParser();
|
||||||
will(returnValue(keyParser));
|
will(returnValue(keyParser));
|
||||||
|
|
||||||
// Bob receives a bad public key
|
// Bob receives a bad public key
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(badPubKeyBytes));
|
will(returnValue(badPubKey.getEncoded()));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
|
oneOf(keyParser).parsePublicKey(badPubKey.getEncoded());
|
||||||
will(returnValue(badPubKey));
|
will(returnValue(badPubKey));
|
||||||
|
|
||||||
// Bob verifies Alice's public key
|
// Bob verifies Alice's public key
|
||||||
@@ -299,7 +280,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
oneOf(transport).sendAbort(false);
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
// Bob never sends his public key
|
// Bob never sends his public key
|
||||||
never(transport).sendKey(bobPubKeyBytes);
|
never(transport).sendKey(bobPubKey.getEncoded());
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
@@ -309,9 +290,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
|
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(bobCommit, null);
|
Payload theirPayload = new Payload(bobCommit, emptyList());
|
||||||
Payload ourPayload = new Payload(aliceCommit, null);
|
Payload ourPayload = new Payload(aliceCommit, emptyList());
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(alicePubKey, getAgreementPrivateKey());
|
||||||
SecretKey sharedSecret = getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
@@ -325,22 +306,18 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
will(returnValue(alicePayload));
|
will(returnValue(alicePayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(bobPayload));
|
will(returnValue(bobPayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
|
||||||
will(returnValue(alicePubKeyBytes));
|
|
||||||
allowing(crypto).getAgreementKeyParser();
|
allowing(crypto).getAgreementKeyParser();
|
||||||
will(returnValue(keyParser));
|
will(returnValue(keyParser));
|
||||||
allowing(bobPubKey).getEncoded();
|
|
||||||
will(returnValue(bobPubKeyBytes));
|
|
||||||
|
|
||||||
// Alice sends her public key
|
// Alice sends her public key
|
||||||
oneOf(transport).sendKey(alicePubKeyBytes);
|
oneOf(transport).sendKey(alicePubKey.getEncoded());
|
||||||
|
|
||||||
// Alice receives Bob's public key
|
// Alice receives Bob's public key
|
||||||
oneOf(callbacks).connectionWaiting();
|
oneOf(callbacks).connectionWaiting();
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(bobPubKeyBytes));
|
will(returnValue(bobPubKey.getEncoded()));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
|
oneOf(keyParser).parsePublicKey(bobPubKey.getEncoded());
|
||||||
will(returnValue(bobPubKey));
|
will(returnValue(bobPubKey));
|
||||||
|
|
||||||
// Alice verifies Bob's public key
|
// Alice verifies Bob's public key
|
||||||
@@ -350,7 +327,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
// Alice computes shared secret
|
// Alice computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
||||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||||
alicePubKeyBytes, bobPubKeyBytes);
|
alicePubKey.getEncoded(), bobPubKey.getEncoded());
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Alice sends her confirmation record
|
// Alice sends her confirmation record
|
||||||
@@ -384,9 +361,9 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testBobProtocolAbortOnBadConfirm() throws Exception {
|
public void testBobProtocolAbortOnBadConfirm() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(aliceCommit, null);
|
Payload theirPayload = new Payload(aliceCommit, emptyList());
|
||||||
Payload ourPayload = new Payload(bobCommit, null);
|
Payload ourPayload = new Payload(bobCommit, emptyList());
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(bobPubKey, getAgreementPrivateKey());
|
||||||
SecretKey sharedSecret = getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
@@ -400,18 +377,14 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
will(returnValue(bobPayload));
|
will(returnValue(bobPayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(alicePayload));
|
will(returnValue(alicePayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
|
||||||
will(returnValue(bobPubKeyBytes));
|
|
||||||
allowing(crypto).getAgreementKeyParser();
|
allowing(crypto).getAgreementKeyParser();
|
||||||
will(returnValue(keyParser));
|
will(returnValue(keyParser));
|
||||||
allowing(alicePubKey).getEncoded();
|
|
||||||
will(returnValue(alicePubKeyBytes));
|
|
||||||
|
|
||||||
// Bob receives Alice's public key
|
// Bob receives Alice's public key
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(alicePubKeyBytes));
|
will(returnValue(alicePubKey.getEncoded()));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
|
oneOf(keyParser).parsePublicKey(alicePubKey.getEncoded());
|
||||||
will(returnValue(alicePubKey));
|
will(returnValue(alicePubKey));
|
||||||
|
|
||||||
// Bob verifies Alice's public key
|
// Bob verifies Alice's public key
|
||||||
@@ -419,12 +392,12 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
will(returnValue(aliceCommit));
|
will(returnValue(aliceCommit));
|
||||||
|
|
||||||
// Bob sends his public key
|
// Bob sends his public key
|
||||||
oneOf(transport).sendKey(bobPubKeyBytes);
|
oneOf(transport).sendKey(bobPubKey.getEncoded());
|
||||||
|
|
||||||
// Bob computes shared secret
|
// Bob computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
||||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||||
alicePubKeyBytes, bobPubKeyBytes);
|
alicePubKey.getEncoded(), bobPubKey.getEncoded());
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Bob receives a bad confirmation record
|
// Bob receives a bad confirmation record
|
||||||
|
|||||||
@@ -101,8 +101,8 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
|||||||
|
|
||||||
private byte[] write() throws Exception {
|
private byte[] write() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
|
StreamContext ctx = new StreamContext(contactId, null, transportId,
|
||||||
headerKey, streamNumber);
|
tagKey, headerKey, streamNumber, false);
|
||||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out,
|
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out,
|
||||||
ctx);
|
ctx);
|
||||||
SyncRecordWriter recordWriter = recordWriterFactory.createRecordWriter(
|
SyncRecordWriter recordWriter = recordWriterFactory.createRecordWriter(
|
||||||
@@ -131,8 +131,8 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
|||||||
assertArrayEquals(expectedTag, tag);
|
assertArrayEquals(expectedTag, tag);
|
||||||
|
|
||||||
// Create the readers
|
// Create the readers
|
||||||
StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
|
StreamContext ctx = new StreamContext(contactId, null, transportId,
|
||||||
headerKey, streamNumber);
|
tagKey, headerKey, streamNumber, false);
|
||||||
InputStream streamReader = streamReaderFactory.createStreamReader(in,
|
InputStream streamReader = streamReaderFactory.createStreamReader(in,
|
||||||
ctx);
|
ctx);
|
||||||
SyncRecordReader recordReader = recordReaderFactory.createRecordReader(
|
SyncRecordReader recordReader = recordReaderFactory.createRecordReader(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -8,8 +9,8 @@ import org.briarproject.bramble.api.db.Transaction;
|
|||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||||
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
@@ -26,6 +27,7 @@ import static java.util.Collections.singletonMap;
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -43,11 +45,17 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
private final DeterministicExecutor executor = new DeterministicExecutor();
|
private final DeterministicExecutor executor = new DeterministicExecutor();
|
||||||
private final Transaction txn = new Transaction(null, false);
|
private final Transaction txn = new Transaction(null, false);
|
||||||
private final ContactId contactId = getContactId();
|
private final ContactId contactId = getContactId();
|
||||||
private final TransportKeySetId keySetId = new TransportKeySetId(345);
|
private final PendingContactId pendingContactId =
|
||||||
|
new PendingContactId(getRandomId());
|
||||||
|
private final KeySetId keySetId = new KeySetId(345);
|
||||||
private final TransportId transportId = getTransportId();
|
private final TransportId transportId = getTransportId();
|
||||||
private final TransportId unknownTransportId = getTransportId();
|
private final TransportId unknownTransportId = getTransportId();
|
||||||
private final StreamContext streamContext = new StreamContext(contactId,
|
private final StreamContext contactStreamContext =
|
||||||
transportId, getSecretKey(), getSecretKey(), 1);
|
new StreamContext(contactId, null, transportId, getSecretKey(),
|
||||||
|
getSecretKey(), 1, false);
|
||||||
|
private final StreamContext pendingContactStreamContext =
|
||||||
|
new StreamContext(null, pendingContactId, transportId,
|
||||||
|
getSecretKey(), getSecretKey(), 1, true);
|
||||||
private final byte[] tag = getRandomBytes(TAG_LENGTH);
|
private final byte[] tag = getRandomBytes(TAG_LENGTH);
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
|
|
||||||
@@ -83,41 +91,94 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddContact() throws Exception {
|
public void testAddContactWithRotationModeKeys() throws Exception {
|
||||||
SecretKey secretKey = getSecretKey();
|
SecretKey secretKey = getSecretKey();
|
||||||
long timestamp = System.currentTimeMillis();
|
long timestamp = System.currentTimeMillis();
|
||||||
boolean alice = random.nextBoolean();
|
boolean alice = random.nextBoolean();
|
||||||
boolean active = random.nextBoolean();
|
boolean active = random.nextBoolean();
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(transportKeyManager).addContact(txn, contactId, secretKey,
|
oneOf(transportKeyManager).addContactWithRotationKeys(txn,
|
||||||
timestamp, alice, active);
|
contactId, secretKey, timestamp, alice, active);
|
||||||
will(returnValue(keySetId));
|
will(returnValue(keySetId));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
Map<TransportId, TransportKeySetId> ids = keyManager.addContact(txn,
|
Map<TransportId, KeySetId> ids = keyManager.addContactWithRotationKeys(
|
||||||
contactId, secretKey, timestamp, alice, active);
|
txn, contactId, secretKey, timestamp, alice, active);
|
||||||
assertEquals(singletonMap(transportId, keySetId), ids);
|
assertEquals(singletonMap(transportId, keySetId), ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetStreamContextForUnknownTransport() throws Exception {
|
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 {
|
||||||
assertNull(keyManager.getStreamContext(contactId, unknownTransportId));
|
assertNull(keyManager.getStreamContext(contactId, unknownTransportId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetStreamContextForPendingContactWithUnknownTransport()
|
||||||
|
throws Exception {
|
||||||
|
assertNull(keyManager.getStreamContext(pendingContactId,
|
||||||
|
unknownTransportId));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetStreamContextForContact() throws Exception {
|
public void testGetStreamContextForContact() throws Exception {
|
||||||
context.checking(new DbExpectations() {{
|
context.checking(new DbExpectations() {{
|
||||||
oneOf(db).transactionWithNullableResult(with(false),
|
oneOf(db).transactionWithNullableResult(with(false),
|
||||||
withNullableDbCallable(txn));
|
withNullableDbCallable(txn));
|
||||||
oneOf(transportKeyManager).getStreamContext(txn, contactId);
|
oneOf(transportKeyManager).getStreamContext(txn, contactId);
|
||||||
will(returnValue(streamContext));
|
will(returnValue(contactStreamContext));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertEquals(streamContext,
|
assertEquals(contactStreamContext,
|
||||||
keyManager.getStreamContext(contactId, transportId));
|
keyManager.getStreamContext(contactId, transportId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetStreamContextForPendingContact() throws Exception {
|
||||||
|
context.checking(new DbExpectations() {{
|
||||||
|
oneOf(db).transactionWithNullableResult(with(false),
|
||||||
|
withNullableDbCallable(txn));
|
||||||
|
oneOf(transportKeyManager).getStreamContext(txn, pendingContactId);
|
||||||
|
will(returnValue(pendingContactStreamContext));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertEquals(pendingContactStreamContext,
|
||||||
|
keyManager.getStreamContext(pendingContactId, transportId));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetStreamContextForTagAndUnknownTransport()
|
public void testGetStreamContextForTagAndUnknownTransport()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -130,10 +191,10 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(db).transactionWithNullableResult(with(false),
|
oneOf(db).transactionWithNullableResult(with(false),
|
||||||
withNullableDbCallable(txn));
|
withNullableDbCallable(txn));
|
||||||
oneOf(transportKeyManager).getStreamContext(txn, tag);
|
oneOf(transportKeyManager).getStreamContext(txn, tag);
|
||||||
will(returnValue(streamContext));
|
will(returnValue(contactStreamContext));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertEquals(streamContext,
|
assertEquals(contactStreamContext,
|
||||||
keyManager.getStreamContext(transportId, tag));
|
keyManager.getStreamContext(transportId, tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -8,10 +9,10 @@ import org.briarproject.bramble.api.db.Transaction;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
|
import org.briarproject.bramble.api.transport.KeySetId;
|
||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
@@ -37,6 +38,7 @@ import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
@@ -61,22 +63,26 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
private final long timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
|
private final long timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
|
||||||
private final ContactId contactId = getContactId();
|
private final ContactId contactId = getContactId();
|
||||||
private final ContactId contactId1 = getContactId();
|
private final ContactId contactId1 = getContactId();
|
||||||
private final TransportKeySetId keySetId = new TransportKeySetId(345);
|
private final PendingContactId pendingContactId =
|
||||||
private final TransportKeySetId keySetId1 = new TransportKeySetId(456);
|
new PendingContactId(getRandomId());
|
||||||
|
private final KeySetId keySetId = new KeySetId(345);
|
||||||
|
private final KeySetId keySetId1 = new KeySetId(456);
|
||||||
private final SecretKey tagKey = getSecretKey();
|
private final SecretKey tagKey = getSecretKey();
|
||||||
private final SecretKey headerKey = getSecretKey();
|
private final SecretKey headerKey = getSecretKey();
|
||||||
private final SecretKey rootKey = getSecretKey();
|
private final SecretKey rootKey = getSecretKey();
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedAtStartup() throws Exception {
|
public void testKeysAreUpdatedAtStartup() throws Exception {
|
||||||
TransportKeys shouldRotate = createTransportKeys(900, 0, true);
|
boolean active = random.nextBoolean();
|
||||||
TransportKeys shouldNotRotate = createTransportKeys(1000, 0, true);
|
TransportKeys shouldUpdate = createTransportKeys(900, 0, active);
|
||||||
|
TransportKeys shouldNotUpdate = createTransportKeys(1000, 0, active);
|
||||||
Collection<TransportKeySet> loaded = asList(
|
Collection<TransportKeySet> loaded = asList(
|
||||||
new TransportKeySet(keySetId, contactId, shouldRotate),
|
new TransportKeySet(keySetId, contactId, null, shouldUpdate),
|
||||||
new TransportKeySet(keySetId1, contactId1, shouldNotRotate)
|
new TransportKeySet(keySetId1, contactId1, null,
|
||||||
|
shouldNotUpdate)
|
||||||
);
|
);
|
||||||
TransportKeys rotated = createTransportKeys(1000, 0, true);
|
TransportKeys updated = createTransportKeys(1000, 0, active);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
@@ -86,11 +92,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
// Load the transport keys
|
// Load the transport keys
|
||||||
oneOf(db).getTransportKeys(txn, transportId);
|
oneOf(db).getTransportKeys(txn, transportId);
|
||||||
will(returnValue(loaded));
|
will(returnValue(loaded));
|
||||||
// Rotate the transport keys
|
// Update the transport keys
|
||||||
oneOf(transportCrypto).rotateTransportKeys(shouldRotate, 1000);
|
oneOf(transportCrypto).updateTransportKeys(shouldUpdate, 1000);
|
||||||
will(returnValue(rotated));
|
will(returnValue(updated));
|
||||||
oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
|
oneOf(transportCrypto).updateTransportKeys(shouldNotUpdate, 1000);
|
||||||
will(returnValue(shouldNotRotate));
|
will(returnValue(shouldNotUpdate));
|
||||||
// Encode the tags (3 sets per contact)
|
// Encode the tags (3 sets per contact)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(6).of(transportCrypto).encodeTag(
|
exactly(6).of(transportCrypto).encodeTag(
|
||||||
@@ -98,10 +104,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
with(PROTOCOL_VERSION), with(i));
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys that were rotated
|
// Save the keys that were updated
|
||||||
oneOf(db).updateTransportKeys(txn, singletonList(
|
oneOf(db).updateTransportKeys(txn, singletonList(
|
||||||
new TransportKeySet(keySetId, contactId, rotated)));
|
new TransportKeySet(keySetId, contactId, null, updated)));
|
||||||
// Schedule key rotation at the start of the next time period
|
// Schedule a key update at the start of the next time period
|
||||||
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
||||||
with(timePeriodLength - 1), with(MILLISECONDS));
|
with(timePeriodLength - 1), with(MILLISECONDS));
|
||||||
}});
|
}});
|
||||||
@@ -110,26 +116,29 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
maxLatency);
|
maxLatency);
|
||||||
transportKeyManager.start(txn);
|
transportKeyManager.start(txn);
|
||||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
assertEquals(active,
|
||||||
|
transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedWhenAddingContact() throws Exception {
|
public void testRotationKeysAreDerivedAndUpdatedWhenAddingContact()
|
||||||
|
throws Exception {
|
||||||
boolean alice = random.nextBoolean();
|
boolean alice = random.nextBoolean();
|
||||||
TransportKeys transportKeys = createTransportKeys(999, 0, true);
|
boolean active = random.nextBoolean();
|
||||||
TransportKeys rotated = createTransportKeys(1000, 0, true);
|
TransportKeys transportKeys = createTransportKeys(999, 0, active);
|
||||||
|
TransportKeys updated = createTransportKeys(1000, 0, active);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
|
oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey,
|
||||||
999, alice, true);
|
999, alice, active);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (1 ms after start of time period 1000)
|
// Get the current time (1 ms after start of time period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(timePeriodLength * 1000 + 1));
|
will(returnValue(timePeriodLength * 1000 + 1));
|
||||||
// Rotate the transport keys
|
// Update the transport keys
|
||||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(rotated));
|
will(returnValue(updated));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(transportCrypto).encodeTag(
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
@@ -138,7 +147,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, rotated);
|
oneOf(db).addTransportKeys(txn, contactId, updated);
|
||||||
will(returnValue(keySetId));
|
will(returnValue(keySetId));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
@@ -147,11 +156,83 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
maxLatency);
|
maxLatency);
|
||||||
// The timestamp is 1 ms before the start of time period 1000
|
// The timestamp is 1 ms before the start of time period 1000
|
||||||
long timestamp = timePeriodLength * 1000 - 1;
|
long timestamp = timePeriodLength * 1000 - 1;
|
||||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||||
rootKey, timestamp, alice, true));
|
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));
|
||||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeKeysAreDerivedWhenAddingPendingContact()
|
||||||
|
throws Exception {
|
||||||
|
boolean alice = random.nextBoolean();
|
||||||
|
TransportKeys transportKeys = createHandshakeKeys(1000, 0, alice);
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// Get the current time (1 ms after start of time period 1000)
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(timePeriodLength * 1000 + 1));
|
||||||
|
// Derive the transport keys
|
||||||
|
oneOf(transportCrypto).deriveHandshakeKeys(transportId, rootKey,
|
||||||
|
1000, alice);
|
||||||
|
will(returnValue(transportKeys));
|
||||||
|
// Encode the tags (3 sets)
|
||||||
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
|
will(new EncodeTagAction());
|
||||||
|
}
|
||||||
|
// Save the keys
|
||||||
|
oneOf(db).addTransportKeys(txn, pendingContactId, transportKeys);
|
||||||
|
will(returnValue(keySetId));
|
||||||
|
}});
|
||||||
|
|
||||||
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
|
maxLatency);
|
||||||
|
assertEquals(keySetId, transportKeyManager.addPendingContact(txn,
|
||||||
|
pendingContactId, rootKey, alice));
|
||||||
|
assertTrue(transportKeyManager.canSendOutgoingStreams(
|
||||||
|
pendingContactId));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -164,6 +245,19 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOutgoingStreamContextIsNullIfPendingContactIsNotFound()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
|
maxLatency);
|
||||||
|
assertNull(transportKeyManager.getStreamContext(txn, pendingContactId));
|
||||||
|
assertFalse(transportKeyManager.canSendOutgoingStreams(
|
||||||
|
pendingContactId));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
|
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -173,15 +267,15 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
MAX_32_BIT_UNSIGNED + 1, true);
|
MAX_32_BIT_UNSIGNED + 1, true);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
expectAddContactNoRotation(alice, true, transportKeys, txn);
|
expectAddContactKeysNotUpdated(alice, true, transportKeys, txn);
|
||||||
|
|
||||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of time period 1000
|
// The timestamp is at the start of time period 1000
|
||||||
long timestamp = timePeriodLength * 1000;
|
long timestamp = timePeriodLength * 1000;
|
||||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||||
rootKey, timestamp, alice, true));
|
txn, contactId, rootKey, timestamp, alice, true));
|
||||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||||
}
|
}
|
||||||
@@ -194,7 +288,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
MAX_32_BIT_UNSIGNED, true);
|
MAX_32_BIT_UNSIGNED, true);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
expectAddContactNoRotation(alice, true, transportKeys, txn);
|
expectAddContactKeysNotUpdated(alice, true, transportKeys, txn);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Increment the stream counter
|
// Increment the stream counter
|
||||||
@@ -206,8 +300,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of time period 1000
|
// The timestamp is at the start of time period 1000
|
||||||
long timestamp = timePeriodLength * 1000;
|
long timestamp = timePeriodLength * 1000;
|
||||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||||
rootKey, timestamp, alice, true));
|
txn, contactId, rootKey, timestamp, alice, true));
|
||||||
// The first request should return a stream context
|
// The first request should return a stream context
|
||||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
StreamContext ctx = transportKeyManager.getStreamContext(txn,
|
StreamContext ctx = transportKeyManager.getStreamContext(txn,
|
||||||
@@ -231,15 +325,15 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
TransportKeys transportKeys = createTransportKeys(1000, 0, active);
|
TransportKeys transportKeys = createTransportKeys(1000, 0, active);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
expectAddContactNoRotation(alice, active, transportKeys, txn);
|
expectAddContactKeysNotUpdated(alice, active, transportKeys, txn);
|
||||||
|
|
||||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of time period 1000
|
// The timestamp is at the start of time period 1000
|
||||||
long timestamp = timePeriodLength * 1000;
|
long timestamp = timePeriodLength * 1000;
|
||||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||||
rootKey, timestamp, alice, active));
|
txn, contactId, rootKey, timestamp, alice, active));
|
||||||
assertEquals(active,
|
assertEquals(active,
|
||||||
transportKeyManager.canSendOutgoingStreams(contactId));
|
transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
// The tag should not be recognised
|
// The tag should not be recognised
|
||||||
@@ -257,7 +351,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
List<byte[]> tags = new ArrayList<>();
|
List<byte[]> tags = new ArrayList<>();
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
|
oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey,
|
||||||
1000, alice, true);
|
1000, alice, true);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of time period 1000)
|
// Get the current time (the start of time period 1000)
|
||||||
@@ -270,8 +364,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
with(PROTOCOL_VERSION), with(i));
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction(tags));
|
will(new EncodeTagAction(tags));
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Updated the transport keys (the keys are unaffected)
|
||||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
@@ -291,8 +385,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of time period 1000
|
// The timestamp is at the start of time period 1000
|
||||||
long timestamp = timePeriodLength * 1000;
|
long timestamp = timePeriodLength * 1000;
|
||||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||||
rootKey, timestamp, alice, true));
|
txn, contactId, rootKey, timestamp, alice, true));
|
||||||
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
// Use the first tag (previous time period, stream number 0)
|
// Use the first tag (previous time period, stream number 0)
|
||||||
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
|
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
|
||||||
@@ -312,11 +406,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedToCurrentPeriod() throws Exception {
|
public void testKeysAreUpdatedToCurrentPeriod() throws Exception {
|
||||||
TransportKeys transportKeys = createTransportKeys(1000, 0, true);
|
TransportKeys transportKeys = createTransportKeys(1000, 0, true);
|
||||||
Collection<TransportKeySet> loaded = singletonList(
|
Collection<TransportKeySet> loaded = singletonList(
|
||||||
new TransportKeySet(keySetId, contactId, transportKeys));
|
new TransportKeySet(keySetId, contactId, null, transportKeys));
|
||||||
TransportKeys rotated = createTransportKeys(1001, 0, true);
|
TransportKeys updated = createTransportKeys(1001, 0, true);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
Transaction txn1 = new Transaction(null, false);
|
Transaction txn1 = new Transaction(null, false);
|
||||||
|
|
||||||
@@ -327,8 +421,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
// Load the transport keys
|
// Load the transport keys
|
||||||
oneOf(db).getTransportKeys(txn, transportId);
|
oneOf(db).getTransportKeys(txn, transportId);
|
||||||
will(returnValue(loaded));
|
will(returnValue(loaded));
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Update the transport keys (the keys are unaffected)
|
||||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
@@ -337,21 +431,21 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
with(PROTOCOL_VERSION), with(i));
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Schedule key rotation at the start of the next time period
|
// Schedule a key update at the start of the next time period
|
||||||
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
||||||
with(timePeriodLength), with(MILLISECONDS));
|
with(timePeriodLength), with(MILLISECONDS));
|
||||||
will(new RunAction());
|
will(new RunAction());
|
||||||
oneOf(dbExecutor).execute(with(any(Runnable.class)));
|
oneOf(dbExecutor).execute(with(any(Runnable.class)));
|
||||||
will(new RunAction());
|
will(new RunAction());
|
||||||
// Start a transaction for key rotation
|
// Start a transaction for updating keys
|
||||||
oneOf(db).transaction(with(false), withDbRunnable(txn1));
|
oneOf(db).transaction(with(false), withDbRunnable(txn1));
|
||||||
// Get the current time (the start of time period 1001)
|
// Get the current time (the start of time period 1001)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(timePeriodLength * 1001));
|
will(returnValue(timePeriodLength * 1001));
|
||||||
// Rotate the transport keys
|
// Update the transport keys
|
||||||
oneOf(transportCrypto).rotateTransportKeys(
|
oneOf(transportCrypto).updateTransportKeys(
|
||||||
with(any(TransportKeys.class)), with(1001L));
|
with(any(TransportKeys.class)), with(1001L));
|
||||||
will(returnValue(rotated));
|
will(returnValue(updated));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(transportCrypto).encodeTag(
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
@@ -359,10 +453,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
with(PROTOCOL_VERSION), with(i));
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys that were rotated
|
// Save the keys that were updated
|
||||||
oneOf(db).updateTransportKeys(txn1, singletonList(
|
oneOf(db).updateTransportKeys(txn1, singletonList(
|
||||||
new TransportKeySet(keySetId, contactId, rotated)));
|
new TransportKeySet(keySetId, contactId, null, updated)));
|
||||||
// Schedule key rotation at the start of the next time period
|
// Schedule a key update at the start of the next time period
|
||||||
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
||||||
with(timePeriodLength), with(MILLISECONDS));
|
with(timePeriodLength), with(MILLISECONDS));
|
||||||
}});
|
}});
|
||||||
@@ -380,7 +474,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
TransportKeys transportKeys = createTransportKeys(1000, 0, false);
|
TransportKeys transportKeys = createTransportKeys(1000, 0, false);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
expectAddContactNoRotation(alice, false, transportKeys, txn);
|
expectAddContactKeysNotUpdated(alice, false, transportKeys, txn);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Activate the keys
|
// Activate the keys
|
||||||
@@ -394,8 +488,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of time period 1000
|
// The timestamp is at the start of time period 1000
|
||||||
long timestamp = timePeriodLength * 1000;
|
long timestamp = timePeriodLength * 1000;
|
||||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||||
rootKey, timestamp, alice, false));
|
txn, contactId, rootKey, timestamp, alice, false));
|
||||||
// The keys are inactive so no stream context should be returned
|
// The keys are inactive so no stream context should be returned
|
||||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||||
@@ -422,7 +516,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
List<byte[]> tags = new ArrayList<>();
|
List<byte[]> tags = new ArrayList<>();
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
|
oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey,
|
||||||
1000, alice, false);
|
1000, alice, false);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of time period 1000)
|
// Get the current time (the start of time period 1000)
|
||||||
@@ -435,8 +529,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
with(PROTOCOL_VERSION), with(i));
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction(tags));
|
will(new EncodeTagAction(tags));
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Update the transport keys (the keys are unaffected)
|
||||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
@@ -460,8 +554,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of time period 1000
|
// The timestamp is at the start of time period 1000
|
||||||
long timestamp = timePeriodLength * 1000;
|
long timestamp = timePeriodLength * 1000;
|
||||||
assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
|
assertEquals(keySetId, transportKeyManager.addContactWithRotationKeys(
|
||||||
rootKey, timestamp, alice, false));
|
txn, contactId, rootKey, timestamp, alice, false));
|
||||||
// The keys are inactive so no stream context should be returned
|
// The keys are inactive so no stream context should be returned
|
||||||
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||||
@@ -486,10 +580,10 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
assertEquals(0L, ctx.getStreamNumber());
|
assertEquals(0L, ctx.getStreamNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expectAddContactNoRotation(boolean alice, boolean active,
|
private void expectAddContactKeysNotUpdated(boolean alice, boolean active,
|
||||||
TransportKeys transportKeys, Transaction txn) throws Exception {
|
TransportKeys transportKeys, Transaction txn) throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(transportCrypto).deriveTransportKeys(transportId, rootKey,
|
oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey,
|
||||||
1000, alice, active);
|
1000, alice, active);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of time period 1000)
|
// Get the current time (the start of time period 1000)
|
||||||
@@ -502,8 +596,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
with(PROTOCOL_VERSION), with(i));
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Upate the transport keys (the keys are unaffected)
|
||||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
@@ -524,6 +618,21 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
|
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("SameParameterValue")
|
||||||
|
private TransportKeys createHandshakeKeys(long timePeriod,
|
||||||
|
long streamCounter, boolean alice) {
|
||||||
|
IncomingKeys inPrev = new IncomingKeys(tagKey, headerKey,
|
||||||
|
timePeriod - 1);
|
||||||
|
IncomingKeys inCurr = new IncomingKeys(tagKey, headerKey,
|
||||||
|
timePeriod);
|
||||||
|
IncomingKeys inNext = new IncomingKeys(tagKey, headerKey,
|
||||||
|
timePeriod + 1);
|
||||||
|
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
|
||||||
|
timePeriod, streamCounter, true);
|
||||||
|
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr,
|
||||||
|
rootKey, alice);
|
||||||
|
}
|
||||||
|
|
||||||
private class EncodeTagAction implements Action {
|
private class EncodeTagAction implements Action {
|
||||||
|
|
||||||
private final Collection<byte[]> tags;
|
private final Collection<byte[]> tags;
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
versionCode 10106
|
versionCode 10107
|
||||||
versionName "1.1.6"
|
versionName "1.1.7"
|
||||||
applicationId "org.briarproject.briar.android"
|
applicationId "org.briarproject.briar.android"
|
||||||
buildConfigField "String", "GitHash",
|
buildConfigField "String", "GitHash",
|
||||||
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
||||||
|
|||||||
@@ -73,13 +73,12 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.briarproject.briar.android.login.PasswordActivity"
|
android:name="org.briarproject.briar.android.login.StartupActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name">
|
||||||
android:windowSoftInputMode="stateVisible">
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.briarproject.briar.android.login.SetupActivity"
|
android:name="org.briarproject.briar.android.account.SetupActivity"
|
||||||
android:label="@string/setup_title"
|
android:label="@string/setup_title"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
</activity>
|
</activity>
|
||||||
@@ -94,11 +93,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".android.login.OpenDatabaseActivity"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:launchMode="singleTop"/>
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
android:theme="@style/BriarTheme.NoActionBar"
|
android:theme="@style/BriarTheme.NoActionBar"
|
||||||
@@ -420,7 +414,7 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.login.UnlockActivity"
|
android:name=".android.account.UnlockActivity"
|
||||||
android:label="@string/lock_unlock"
|
android:label="@string/lock_unlock"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:theme="@style/BriarTheme.NoActionBar"/>
|
android:theme="@style/BriarTheme.NoActionBar"/>
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.logging.LogRecord;
|
import java.util.logging.LogRecord;
|
||||||
|
|
||||||
@@ -11,6 +14,8 @@ import java.util.logging.LogRecord;
|
|||||||
*/
|
*/
|
||||||
public interface BriarApplication {
|
public interface BriarApplication {
|
||||||
|
|
||||||
|
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
|
||||||
|
|
||||||
Collection<LogRecord> getRecentLogRecords();
|
Collection<LogRecord> getRecentLogRecords();
|
||||||
|
|
||||||
AndroidComponent getApplicationComponent();
|
AndroidComponent getApplicationComponent();
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
|
|||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.logout.HideUiActivity;
|
import org.briarproject.briar.android.logout.HideUiActivity;
|
||||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.android.LockManager;
|
import org.briarproject.briar.api.android.LockManager;
|
||||||
|
|
||||||
@@ -48,6 +47,7 @@ import static java.util.logging.Level.INFO;
|
|||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||||
|
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID;
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_ID;
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_ID;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID;
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID;
|
||||||
@@ -182,7 +182,7 @@ public class BriarService extends Service {
|
|||||||
NotificationManager nm = (NotificationManager) o;
|
NotificationManager nm = (NotificationManager) o;
|
||||||
nm.notify(FAILURE_NOTIFICATION_ID, b.build());
|
nm.notify(FAILURE_NOTIFICATION_ID, b.build());
|
||||||
// Bring the dashboard to the front to clear the back stack
|
// Bring the dashboard to the front to clear the back stack
|
||||||
i = new Intent(BriarService.this, NavDrawerActivity.class);
|
i = new Intent(BriarService.this, ENTRY_ACTIVITY);
|
||||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i.putExtra(EXTRA_STARTUP_FAILED, true);
|
i.putExtra(EXTRA_STARTUP_FAILED, true);
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public class StartupFailureActivity extends BaseActivity implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void runOnDbThread(@NonNull Runnable runnable) {
|
public void runOnDbThread(@NonNull Runnable runnable) {
|
||||||
throw new AssertionError("Deprecated and should not be used");
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.design.widget.TextInputEditText;
|
import android.support.design.widget.TextInputEditText;
|
||||||
@@ -21,6 +21,7 @@ import static android.view.inputmethod.EditorInfo.IME_ACTION_NONE;
|
|||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.setError;
|
import static org.briarproject.briar.android.util.UiUtils.setError;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.showSoftKeyboard;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -63,6 +64,12 @@ public class AuthorNameFragment extends SetupFragment {
|
|||||||
return TAG;
|
return TAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
showSoftKeyboard(authorNameInput);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getHelpText() {
|
protected String getHelpText() {
|
||||||
return getString(R.string.setup_name_explanation);
|
return getString(R.string.setup_name_explanation);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -13,8 +13,8 @@ import android.widget.ProgressBar;
|
|||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.account.PowerView.OnCheckedChangedListener;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.login.PowerView.OnCheckedChangedListener;
|
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -15,8 +15,11 @@ import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
|
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
|
||||||
|
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -86,7 +89,7 @@ public class SetupActivity extends BaseActivity
|
|||||||
|
|
||||||
void showPasswordFragment() {
|
void showPasswordFragment() {
|
||||||
if (authorName == null) throw new IllegalStateException();
|
if (authorName == null) throw new IllegalStateException();
|
||||||
showNextFragment(PasswordFragment.newInstance());
|
showNextFragment(SetPasswordFragment.newInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(23)
|
@TargetApi(23)
|
||||||
@@ -97,8 +100,9 @@ public class SetupActivity extends BaseActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
void showApp() {
|
void showApp() {
|
||||||
Intent i = new Intent(this, OpenDatabaseActivity.class);
|
Intent i = new Intent(this, ENTRY_ACTIVITY);
|
||||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME |
|
||||||
|
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out);
|
overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out);
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface SetupController extends PasswordController {
|
public interface SetupController {
|
||||||
|
|
||||||
void setSetupActivity(SetupActivity setupActivity);
|
void setSetupActivity(SetupActivity setupActivity);
|
||||||
|
|
||||||
@@ -11,6 +11,8 @@ public interface SetupController extends PasswordController {
|
|||||||
|
|
||||||
void setAuthorName(String authorName);
|
void setAuthorName(String authorName);
|
||||||
|
|
||||||
|
float estimatePasswordStrength(String password);
|
||||||
|
|
||||||
void setPassword(String password);
|
void setPassword(String password);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
@@ -15,12 +15,15 @@ import java.util.logging.Logger;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class SetupControllerImpl extends PasswordControllerImpl
|
public class SetupControllerImpl implements SetupController {
|
||||||
implements SetupController {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(SetupControllerImpl.class.getName());
|
Logger.getLogger(SetupControllerImpl.class.getName());
|
||||||
|
|
||||||
|
private final AccountManager accountManager;
|
||||||
|
private final PasswordStrengthEstimator strengthEstimator;
|
||||||
|
@IoExecutor
|
||||||
|
private final Executor ioExecutor;
|
||||||
@Nullable
|
@Nullable
|
||||||
private volatile SetupActivity setupActivity;
|
private volatile SetupActivity setupActivity;
|
||||||
|
|
||||||
@@ -28,7 +31,9 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
|||||||
SetupControllerImpl(AccountManager accountManager,
|
SetupControllerImpl(AccountManager accountManager,
|
||||||
@IoExecutor Executor ioExecutor,
|
@IoExecutor Executor ioExecutor,
|
||||||
PasswordStrengthEstimator strengthEstimator) {
|
PasswordStrengthEstimator strengthEstimator) {
|
||||||
super(accountManager, ioExecutor, strengthEstimator);
|
this.accountManager = accountManager;
|
||||||
|
this.strengthEstimator = strengthEstimator;
|
||||||
|
this.ioExecutor = ioExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -51,6 +56,11 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
|||||||
setupActivity.setAuthorName(authorName);
|
setupActivity.setAuthorName(authorName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float estimatePasswordStrength(String password) {
|
||||||
|
return strengthEstimator.estimateStrength(password);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPassword(String password) {
|
public void setPassword(String password) {
|
||||||
SetupActivity setupActivity = this.setupActivity;
|
SetupActivity setupActivity = this.setupActivity;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user