mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
1 Commits
elliptic-c
...
gradle-lib
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa79a31911 |
@@ -27,7 +27,7 @@ dependencies {
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
|
||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
}
|
||||
@@ -55,8 +55,6 @@ dependencyVerification {
|
||||
}
|
||||
|
||||
ext.torBinaryDir = 'src/main/res/raw'
|
||||
ext.torVersion = '0.2.9.12'
|
||||
ext.geoipVersion = '2017-09-06'
|
||||
ext.torDownloadUrl = 'https://briarproject.org/build/'
|
||||
|
||||
def torBinaries = [
|
||||
|
||||
@@ -5,15 +5,15 @@ targetCompatibility = 1.8
|
||||
apply plugin: 'witness'
|
||||
|
||||
dependencies {
|
||||
implementation "com.google.dagger:dagger:2.0.2"
|
||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||
testImplementation "junit:junit:$junitVersion"
|
||||
testImplementation "org.jmock:jmock:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-core:$hamcrestVersion"
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
|
||||
@@ -12,19 +12,18 @@ public interface ContactGroupFactory {
|
||||
/**
|
||||
* Creates a group that is not shared with any contacts.
|
||||
*/
|
||||
Group createLocalGroup(ClientId clientId, int clientVersion);
|
||||
Group createLocalGroup(ClientId clientId);
|
||||
|
||||
/**
|
||||
* Creates a group for the given client to share with the given contact.
|
||||
*/
|
||||
Group createContactGroup(ClientId clientId, int clientVersion,
|
||||
Contact contact);
|
||||
Group createContactGroup(ClientId clientId, Contact contact);
|
||||
|
||||
/**
|
||||
* Creates a group for the given client to share between the given authors
|
||||
* identified by their AuthorIds.
|
||||
*/
|
||||
Group createContactGroup(ClientId clientId, int clientVersion,
|
||||
AuthorId authorId1, AuthorId authorId2);
|
||||
Group createContactGroup(ClientId clientId, AuthorId authorId1,
|
||||
AuthorId authorId2);
|
||||
|
||||
}
|
||||
|
||||
@@ -12,32 +12,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
@NotNullByDefault
|
||||
public interface ContactExchangeTask {
|
||||
|
||||
/**
|
||||
* The current version of the contact exchange protocol
|
||||
*/
|
||||
int PROTOCOL_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's header key from the master secret.
|
||||
*/
|
||||
String ALICE_KEY_LABEL =
|
||||
"org.briarproject.bramble.contact/ALICE_HEADER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's header key from the master secret.
|
||||
*/
|
||||
String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's key binding nonce from the master secret.
|
||||
*/
|
||||
String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's key binding nonce from the master secret.
|
||||
*/
|
||||
String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE";
|
||||
|
||||
/**
|
||||
* Exchanges contact information with a remote peer.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.briarproject.bramble.api.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
@@ -24,35 +27,115 @@ public interface CryptoComponent {
|
||||
KeyParser getMessageKeyParser();
|
||||
|
||||
/**
|
||||
* Derives another secret key from the given secret key.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of the derived
|
||||
* key, to prevent it from being repurposed or colliding with a key derived
|
||||
* for another purpose
|
||||
* Derives a stream header key from the given master secret.
|
||||
* @param alice whether the key is for use by Alice or Bob.
|
||||
*/
|
||||
SecretKey deriveKey(String label, SecretKey k, byte[]... inputs);
|
||||
SecretKey deriveHeaderKey(SecretKey master, boolean alice);
|
||||
|
||||
/**
|
||||
* Derives a message authentication code key from the given master secret.
|
||||
* @param alice whether the key is for use by Alice or Bob.
|
||||
*/
|
||||
SecretKey deriveMacKey(SecretKey master, boolean alice);
|
||||
|
||||
/**
|
||||
* Derives a nonce from the given master secret for one of the parties to
|
||||
* sign.
|
||||
* @param alice whether the nonce is for use by Alice or Bob.
|
||||
*/
|
||||
byte[] deriveSignatureNonce(SecretKey master, boolean alice);
|
||||
|
||||
/**
|
||||
* Derives a commitment to the provided public key.
|
||||
* <p/>
|
||||
* Part of BQP.
|
||||
*
|
||||
* @param publicKey the public key
|
||||
* @return the commitment to the provided public key.
|
||||
*/
|
||||
byte[] deriveKeyCommitment(byte[] publicKey);
|
||||
|
||||
/**
|
||||
* Derives a common shared secret from two public keys and one of the
|
||||
* corresponding private keys.
|
||||
* <p/>
|
||||
* Part of BQP.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of this shared
|
||||
* secret, to prevent it from being repurposed or colliding with a shared
|
||||
* secret derived for another purpose
|
||||
* @param theirPublicKey the public key of the remote party
|
||||
* @param ourKeyPair the key pair of the local party
|
||||
* @param theirPublicKey the ephemeral public key of the remote party
|
||||
* @param ourKeyPair our ephemeral keypair
|
||||
* @param alice true if ourKeyPair belongs to Alice
|
||||
* @return the shared secret
|
||||
*/
|
||||
SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||
KeyPair ourKeyPair, byte[]... inputs)
|
||||
throws GeneralSecurityException;
|
||||
SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
|
||||
boolean alice) throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Derives the content of a confirmation record.
|
||||
* <p/>
|
||||
* Part of BQP.
|
||||
*
|
||||
* @param sharedSecret the common shared secret
|
||||
* @param theirPayload the commit payload from the remote party
|
||||
* @param ourPayload the commit payload we sent
|
||||
* @param theirPublicKey the ephemeral public key of the remote party
|
||||
* @param ourKeyPair our ephemeral keypair
|
||||
* @param alice true if ourKeyPair belongs to Alice
|
||||
* @param aliceRecord true if the confirmation record is for use by Alice
|
||||
* @return the confirmation record
|
||||
*/
|
||||
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||
byte[] theirPayload, byte[] ourPayload,
|
||||
byte[] theirPublicKey, KeyPair ourKeyPair,
|
||||
boolean alice, boolean aliceRecord);
|
||||
|
||||
/**
|
||||
* Derives a master secret from the given shared secret.
|
||||
* <p/>
|
||||
* Part of BQP.
|
||||
*
|
||||
* @param sharedSecret the common shared secret
|
||||
* @return the master secret
|
||||
*/
|
||||
SecretKey deriveMasterSecret(SecretKey sharedSecret);
|
||||
|
||||
/**
|
||||
* Derives a master secret from two public keys and one of the corresponding
|
||||
* private keys.
|
||||
* <p/>
|
||||
* This is a helper method that calls
|
||||
* deriveMasterSecret(deriveSharedSecret(theirPublicKey, ourKeyPair, alice))
|
||||
*
|
||||
* @param theirPublicKey the ephemeral public key of the remote party
|
||||
* @param ourKeyPair our ephemeral keypair
|
||||
* @param alice true if ourKeyPair belongs to Alice
|
||||
* @return the shared secret
|
||||
*/
|
||||
SecretKey deriveMasterSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
|
||||
boolean alice) throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Derives initial transport keys for the given transport in the given
|
||||
* rotation period from the given master secret.
|
||||
* @param alice whether the keys are for use by Alice or Bob.
|
||||
*/
|
||||
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
|
||||
long rotationPeriod, boolean alice);
|
||||
|
||||
/**
|
||||
* Rotates the given transport keys to the given rotation period. If the
|
||||
* keys are for a future rotation period they are not rotated.
|
||||
*/
|
||||
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
|
||||
|
||||
/** Encodes the pseudo-random tag that is used to recognise a stream. */
|
||||
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||
long streamNumber);
|
||||
|
||||
/**
|
||||
* Signs the given byte[] with the given ECDSA private key.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of this
|
||||
* signature, to prevent it from being repurposed or colliding with a
|
||||
* signature created for another purpose
|
||||
* @param label A label specific to this signature
|
||||
* to ensure that the signature cannot be repurposed
|
||||
*/
|
||||
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||
throws GeneralSecurityException;
|
||||
@@ -70,9 +153,8 @@ public interface CryptoComponent {
|
||||
* Verifies that the given signature is valid for the signed data
|
||||
* and the given ECDSA public key.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of this
|
||||
* signature, to prevent it from being repurposed or colliding with a
|
||||
* signature created for another purpose
|
||||
* @param label A label that was specific to this signature
|
||||
* to ensure that the signature cannot be repurposed
|
||||
* @return true if the signature was valid, false otherwise.
|
||||
*/
|
||||
boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||
@@ -93,22 +175,23 @@ public interface CryptoComponent {
|
||||
* Returns the hash of the given inputs. The inputs are unambiguously
|
||||
* combined by prefixing each input with its length.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of this hash, to
|
||||
* prevent it from being repurposed or colliding with a hash created for
|
||||
* another purpose
|
||||
* @param label A label specific to this hash to ensure that hashes
|
||||
* calculated for distinct purposes don't collide.
|
||||
*/
|
||||
byte[] hash(String label, byte[]... inputs);
|
||||
|
||||
/**
|
||||
* Returns the length of hashes produced by
|
||||
* the {@link CryptoComponent#hash(String, byte[]...)} method.
|
||||
*/
|
||||
int getHashLength();
|
||||
|
||||
/**
|
||||
* Returns a message authentication code with the given key over the
|
||||
* given inputs. The inputs are unambiguously combined by prefixing each
|
||||
* input with its length.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of this MAC, to
|
||||
* prevent it from being repurposed or colliding with a MAC created for
|
||||
* another purpose
|
||||
*/
|
||||
byte[] mac(String label, SecretKey macKey, byte[]... inputs);
|
||||
byte[] mac(SecretKey macKey, byte[]... inputs);
|
||||
|
||||
/**
|
||||
* Encrypts and authenticates the given plaintext so it can be written to
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package org.briarproject.bramble.api.crypto;
|
||||
|
||||
/**
|
||||
* Crypto operations for the key agreement protocol - see
|
||||
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BQP.md
|
||||
*/
|
||||
public interface KeyAgreementCrypto {
|
||||
|
||||
/**
|
||||
* Hash label for public key commitment.
|
||||
*/
|
||||
String COMMIT_LABEL = "org.briarproject.bramble.keyagreement/COMMIT";
|
||||
|
||||
/**
|
||||
* Key derivation label for confirmation record.
|
||||
*/
|
||||
String CONFIRMATION_KEY_LABEL =
|
||||
"org.briarproject.bramble.keyagreement/CONFIRMATION_KEY";
|
||||
|
||||
/**
|
||||
* MAC label for confirmation record.
|
||||
*/
|
||||
String CONFIRMATION_MAC_LABEL =
|
||||
"org.briarproject.bramble.keyagreement/CONFIRMATION_MAC";
|
||||
|
||||
/**
|
||||
* Derives a commitment to the provided public key.
|
||||
*
|
||||
* @param publicKey the public key
|
||||
* @return the commitment to the provided public key.
|
||||
*/
|
||||
byte[] deriveKeyCommitment(PublicKey publicKey);
|
||||
|
||||
/**
|
||||
* Derives the content of a confirmation record.
|
||||
*
|
||||
* @param sharedSecret the common shared secret
|
||||
* @param theirPayload the key exchange payload of the remote party
|
||||
* @param ourPayload the key exchange payload of the local party
|
||||
* @param theirPublicKey the ephemeral public key of the remote party
|
||||
* @param ourKeyPair our ephemeral key pair of the local party
|
||||
* @param alice true if the local party is Alice
|
||||
* @param aliceRecord true if the confirmation record is for use by Alice
|
||||
* @return the confirmation record
|
||||
*/
|
||||
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||
byte[] theirPayload, byte[] ourPayload,
|
||||
PublicKey theirPublicKey, KeyPair ourKeyPair,
|
||||
boolean alice, boolean aliceRecord);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.briarproject.bramble.api.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
|
||||
/**
|
||||
* Crypto operations for the transport security protocol - see
|
||||
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BTP.md
|
||||
*/
|
||||
public interface TransportCrypto {
|
||||
|
||||
/**
|
||||
* Derives initial transport keys for the given transport in the given
|
||||
* rotation period from the given master secret.
|
||||
*
|
||||
* @param alice whether the keys are for use by Alice or Bob.
|
||||
*/
|
||||
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
|
||||
long rotationPeriod, boolean alice);
|
||||
|
||||
/**
|
||||
* Rotates the given transport keys to the given rotation period. If the
|
||||
* keys are for the given period or any later period they are not rotated.
|
||||
*/
|
||||
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
|
||||
|
||||
/**
|
||||
* Encodes the pseudo-random tag that is used to recognise a stream.
|
||||
*/
|
||||
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||
long streamNumber);
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public class AuthorId extends UniqueId {
|
||||
/**
|
||||
* Label for hashing authors to calculate their identities.
|
||||
*/
|
||||
public static final String LABEL = "org.briarproject.bramble/AUTHOR_ID";
|
||||
public static final String LABEL = "org.briarproject.bramble.AUTHOR_ID";
|
||||
|
||||
public AuthorId(byte[] id) {
|
||||
super(id);
|
||||
|
||||
@@ -5,7 +5,7 @@ public interface KeyAgreementConstants {
|
||||
/**
|
||||
* The current version of the BQP protocol.
|
||||
*/
|
||||
byte PROTOCOL_VERSION = 3;
|
||||
byte PROTOCOL_VERSION = 2;
|
||||
|
||||
/**
|
||||
* The length of the record header in bytes.
|
||||
@@ -22,10 +22,7 @@ public interface KeyAgreementConstants {
|
||||
*/
|
||||
int COMMIT_LENGTH = 16;
|
||||
|
||||
/**
|
||||
* The connection timeout in milliseconds.
|
||||
*/
|
||||
long CONNECTION_TIMEOUT = 20 * 1000;
|
||||
long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds
|
||||
|
||||
/**
|
||||
* The transport identifier for Bluetooth.
|
||||
@@ -36,16 +33,4 @@ public interface KeyAgreementConstants {
|
||||
* The transport identifier for LAN.
|
||||
*/
|
||||
int TRANSPORT_ID_LAN = 1;
|
||||
|
||||
/**
|
||||
* Label for deriving the shared secret.
|
||||
*/
|
||||
String SHARED_SECRET_LABEL =
|
||||
"org.briarproject.bramble.keyagreement/SHARED_SECRET";
|
||||
|
||||
/**
|
||||
* Label for deriving the master secret.
|
||||
*/
|
||||
String MASTER_SECRET_LABEL =
|
||||
"org.briarproject.bramble.keyagreement/MASTER_SECRET";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.briarproject.bramble.api.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
/**
|
||||
* Manages tasks for conducting key agreements with remote peers.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
public interface KeyAgreementTaskFactory {
|
||||
|
||||
/**
|
||||
* Gets the current key agreement task.
|
||||
*/
|
||||
KeyAgreementTask createTask();
|
||||
}
|
||||
@@ -17,11 +17,6 @@ public interface TransportPropertyManager {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties");
|
||||
|
||||
/**
|
||||
* The current version of the transport property client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Stores the given properties received while adding a contact - they will
|
||||
* be superseded by any properties synced from the contact.
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
public interface GroupFactory {
|
||||
|
||||
/**
|
||||
* Creates a group with the given client ID, client version and descriptor.
|
||||
* Creates a group with the given client ID and descriptor.
|
||||
*/
|
||||
Group createGroup(ClientId c, int clientVersion, byte[] descriptor);
|
||||
Group createGroup(ClientId c, byte[] descriptor);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class GroupId extends UniqueId {
|
||||
/**
|
||||
* Label for hashing groups to calculate their identifiers.
|
||||
*/
|
||||
public static final String LABEL = "org.briarproject.bramble/GROUP_ID";
|
||||
public static final String LABEL = "org.briarproject.bramble.GROUP_ID";
|
||||
|
||||
public GroupId(byte[] id) {
|
||||
super(id);
|
||||
|
||||
@@ -16,7 +16,7 @@ public class MessageId extends UniqueId {
|
||||
/**
|
||||
* Label for hashing messages to calculate their identifiers.
|
||||
*/
|
||||
public static final String LABEL = "org.briarproject.bramble/MESSAGE_ID";
|
||||
public static final String LABEL = "org.briarproject.bramble.MESSAGE_ID";
|
||||
|
||||
public MessageId(byte[] id) {
|
||||
super(id);
|
||||
|
||||
@@ -7,7 +7,7 @@ public interface TransportConstants {
|
||||
/**
|
||||
* The current version of the transport protocol.
|
||||
*/
|
||||
int PROTOCOL_VERSION = 4;
|
||||
int PROTOCOL_VERSION = 3;
|
||||
|
||||
/**
|
||||
* The length of the pseudo-random tag in bytes.
|
||||
@@ -80,32 +80,4 @@ public interface TransportConstants {
|
||||
* The size of the reordering window.
|
||||
*/
|
||||
int REORDERING_WINDOW_SIZE = 32;
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's initial tag key from the master secret.
|
||||
*/
|
||||
String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's initial tag key from the master secret.
|
||||
*/
|
||||
String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's initial header key from the master secret.
|
||||
*/
|
||||
String ALICE_HEADER_LABEL =
|
||||
"org.briarproject.bramble.transport/ALICE_HEADER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's initial header key from the master secret.
|
||||
*/
|
||||
String BOB_HEADER_LABEL =
|
||||
"org.briarproject.bramble.transport/BOB_HEADER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving the next period's key in key rotation.
|
||||
*/
|
||||
String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE";
|
||||
|
||||
}
|
||||
|
||||
@@ -9,23 +9,21 @@ apply plugin: 'witness'
|
||||
dependencies {
|
||||
implementation project(path: ':bramble-api', configuration: 'default')
|
||||
implementation 'com.madgag.spongycastle:core:1.58.0.0'
|
||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||
implementation 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
|
||||
implementation 'org.bitlet:weupnp:0.1.4'
|
||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||
|
||||
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
apt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||
testImplementation "org.whispersystems:curve25519-java:0.4.1"
|
||||
testImplementation "junit:junit:$junitVersion"
|
||||
testImplementation "org.jmock:jmock:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-core:$hamcrestVersion"
|
||||
|
||||
testApt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
testApt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
@@ -47,14 +45,12 @@ dependencyVerification {
|
||||
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
|
||||
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
|
||||
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||
'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -32,25 +32,23 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createLocalGroup(ClientId clientId, int clientVersion) {
|
||||
return groupFactory.createGroup(clientId, clientVersion,
|
||||
LOCAL_GROUP_DESCRIPTOR);
|
||||
public Group createLocalGroup(ClientId clientId) {
|
||||
return groupFactory.createGroup(clientId, LOCAL_GROUP_DESCRIPTOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createContactGroup(ClientId clientId, int clientVersion,
|
||||
Contact contact) {
|
||||
public Group createContactGroup(ClientId clientId, Contact contact) {
|
||||
AuthorId local = contact.getLocalAuthorId();
|
||||
AuthorId remote = contact.getAuthor().getId();
|
||||
byte[] descriptor = createGroupDescriptor(local, remote);
|
||||
return groupFactory.createGroup(clientId, clientVersion, descriptor);
|
||||
return groupFactory.createGroup(clientId, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createContactGroup(ClientId clientId, int clientVersion,
|
||||
AuthorId authorId1, AuthorId authorId2) {
|
||||
public Group createContactGroup(ClientId clientId, AuthorId authorId1,
|
||||
AuthorId authorId2) {
|
||||
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
|
||||
return groupFactory.createGroup(clientId, clientVersion, descriptor);
|
||||
return groupFactory.createGroup(clientId, descriptor);
|
||||
}
|
||||
|
||||
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
|
||||
|
||||
@@ -141,10 +141,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
}
|
||||
|
||||
// Derive the header keys for the transport streams
|
||||
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL,
|
||||
masterSecret, new byte[] {PROTOCOL_VERSION});
|
||||
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret,
|
||||
new byte[] {PROTOCOL_VERSION});
|
||||
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
|
||||
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
|
||||
|
||||
// Create the readers
|
||||
InputStream streamReader =
|
||||
@@ -158,10 +156,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
||||
|
||||
// Derive the nonces to be signed
|
||||
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
|
||||
new byte[] {PROTOCOL_VERSION});
|
||||
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
|
||||
new byte[] {PROTOCOL_VERSION});
|
||||
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
|
||||
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
|
||||
|
||||
// Exchange pseudonyms, signed nonces, and timestamps
|
||||
long localTimestamp = clock.currentTimeMillis();
|
||||
@@ -200,8 +196,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
|
||||
try {
|
||||
// Add the contact
|
||||
ContactId contactId = addContact(remoteAuthor, timestamp,
|
||||
remoteProperties);
|
||||
ContactId contactId = addContact(remoteAuthor, masterSecret,
|
||||
timestamp, alice, remoteProperties);
|
||||
// Reuse the connection as a transport connection
|
||||
connectionManager.manageOutgoingConnection(contactId, transportId,
|
||||
conn);
|
||||
@@ -298,15 +294,15 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
return remote;
|
||||
}
|
||||
|
||||
private ContactId addContact(Author remoteAuthor, long timestamp,
|
||||
private ContactId addContact(Author remoteAuthor, SecretKey master,
|
||||
long timestamp, boolean alice,
|
||||
Map<TransportId, TransportProperties> remoteProperties)
|
||||
throws DbException {
|
||||
ContactId contactId;
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
contactId = contactManager.addContact(txn, remoteAuthor,
|
||||
localAuthor.getId(), masterSecret, timestamp, alice,
|
||||
true, true);
|
||||
localAuthor.getId(), master, timestamp, alice, true, true);
|
||||
transportPropertyManager.addRemoteProperties(txn, contactId,
|
||||
remoteProperties);
|
||||
db.commitTransaction(txn);
|
||||
@@ -316,7 +312,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
private void tryToClose(DuplexTransportConnection conn, boolean exception) {
|
||||
private void tryToClose(DuplexTransportConnection conn,
|
||||
boolean exception) {
|
||||
try {
|
||||
LOG.info("Closing connection");
|
||||
conn.getReader().dispose(exception, true);
|
||||
|
||||
@@ -10,7 +10,11 @@ import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
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.util.ByteUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||
@@ -26,6 +30,7 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
@@ -39,8 +44,14 @@ import java.util.logging.Logger;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
class CryptoComponentImpl implements CryptoComponent {
|
||||
|
||||
@@ -54,6 +65,39 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
||||
private static final int PBKDF_TARGET_MILLIS = 500;
|
||||
private static final int PBKDF_SAMPLES = 30;
|
||||
private static final int HASH_SIZE = 256 / 8;
|
||||
|
||||
private static byte[] ascii(String s) {
|
||||
return s.getBytes(Charset.forName("US-ASCII"));
|
||||
}
|
||||
|
||||
// KDF labels for contact exchange stream header key derivation
|
||||
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
|
||||
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
|
||||
// KDF labels for contact exchange signature nonce derivation
|
||||
private static final byte[] A_SIG_NONCE = ascii("ALICE_SIGNATURE_NONCE");
|
||||
private static final byte[] B_SIG_NONCE = ascii("BOB_SIGNATURE_NONCE");
|
||||
// Hash label for BQP public key commitment derivation
|
||||
private static final String COMMIT =
|
||||
"org.briarproject.bramble.COMMIT";
|
||||
// Hash label for shared secret derivation
|
||||
private static final String SHARED_SECRET =
|
||||
"org.briarproject.bramble.SHARED_SECRET";
|
||||
// KDF label for BQP confirmation key derivation
|
||||
private static final byte[] CONFIRMATION_KEY = ascii("CONFIRMATION_KEY");
|
||||
// KDF label for master key derivation
|
||||
private static final byte[] MASTER_KEY = ascii("MASTER_KEY");
|
||||
// KDF labels for tag key derivation
|
||||
private static final byte[] A_TAG = ascii("ALICE_TAG_KEY");
|
||||
private static final byte[] B_TAG = ascii("BOB_TAG_KEY");
|
||||
// KDF labels for header key derivation
|
||||
private static final byte[] A_HEADER = ascii("ALICE_HEADER_KEY");
|
||||
private static final byte[] B_HEADER = ascii("BOB_HEADER_KEY");
|
||||
// KDF labels for MAC key derivation
|
||||
private static final byte[] A_MAC = ascii("ALICE_MAC_KEY");
|
||||
private static final byte[] B_MAC = ascii("BOB_MAC_KEY");
|
||||
// KDF label for key rotation
|
||||
private static final byte[] ROTATE = ascii("ROTATE");
|
||||
|
||||
private final SecureRandom secureRandom;
|
||||
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
||||
@@ -219,23 +263,179 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String label, SecretKey k, byte[]... inputs) {
|
||||
byte[] mac = mac(label, k, inputs);
|
||||
if (mac.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||
return new SecretKey(mac);
|
||||
public SecretKey deriveHeaderKey(SecretKey master,
|
||||
boolean alice) {
|
||||
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||
KeyPair ourKeyPair, byte[]... inputs)
|
||||
throws GeneralSecurityException {
|
||||
public SecretKey deriveMacKey(SecretKey master, boolean alice) {
|
||||
return new SecretKey(macKdf(master, alice ? A_MAC : B_MAC));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveSignatureNonce(SecretKey master,
|
||||
boolean alice) {
|
||||
return macKdf(master, alice ? A_SIG_NONCE : B_SIG_NONCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveKeyCommitment(byte[] publicKey) {
|
||||
byte[] hash = hash(COMMIT, publicKey);
|
||||
// The output is the first COMMIT_LENGTH bytes of the hash
|
||||
byte[] commitment = new byte[COMMIT_LENGTH];
|
||||
System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH);
|
||||
return commitment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveSharedSecret(byte[] theirPublicKey,
|
||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
||||
PrivateKey ourPriv = ourKeyPair.getPrivate();
|
||||
byte[][] hashInputs = new byte[inputs.length + 1][];
|
||||
hashInputs[0] = performRawKeyAgreement(ourPriv, theirPublicKey);
|
||||
System.arraycopy(inputs, 0, hashInputs, 1, inputs.length);
|
||||
byte[] hash = hash(label, hashInputs);
|
||||
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||
return new SecretKey(hash);
|
||||
PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
|
||||
byte[] raw = performRawKeyAgreement(ourPriv, theirPub);
|
||||
byte[] alicePub, bobPub;
|
||||
if (alice) {
|
||||
alicePub = ourKeyPair.getPublic().getEncoded();
|
||||
bobPub = theirPublicKey;
|
||||
} else {
|
||||
alicePub = theirPublicKey;
|
||||
bobPub = ourKeyPair.getPublic().getEncoded();
|
||||
}
|
||||
return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||
byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey,
|
||||
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
|
||||
SecretKey ck = new SecretKey(macKdf(sharedSecret, CONFIRMATION_KEY));
|
||||
byte[] alicePayload, alicePub, bobPayload, bobPub;
|
||||
if (alice) {
|
||||
alicePayload = ourPayload;
|
||||
alicePub = ourKeyPair.getPublic().getEncoded();
|
||||
bobPayload = theirPayload;
|
||||
bobPub = theirPublicKey;
|
||||
} else {
|
||||
alicePayload = theirPayload;
|
||||
alicePub = theirPublicKey;
|
||||
bobPayload = ourPayload;
|
||||
bobPub = ourKeyPair.getPublic().getEncoded();
|
||||
}
|
||||
if (aliceRecord)
|
||||
return macKdf(ck, alicePayload, alicePub, bobPayload, bobPub);
|
||||
else
|
||||
return macKdf(ck, bobPayload, bobPub, alicePayload, alicePub);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveMasterSecret(SecretKey sharedSecret) {
|
||||
return new SecretKey(macKdf(sharedSecret, MASTER_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
||||
return deriveMasterSecret(deriveSharedSecret(
|
||||
theirPublicKey, ourKeyPair, alice));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeys deriveTransportKeys(TransportId t,
|
||||
SecretKey master, long rotationPeriod, boolean alice) {
|
||||
// Keys for the previous period are derived from the master secret
|
||||
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
|
||||
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
|
||||
SecretKey outTagPrev = deriveTagKey(master, t, alice);
|
||||
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
|
||||
// Derive the keys for the current and next periods
|
||||
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
|
||||
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
|
||||
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
|
||||
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
|
||||
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
|
||||
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
|
||||
// Initialise the reordering windows and stream counters
|
||||
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
|
||||
rotationPeriod - 1);
|
||||
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
|
||||
rotationPeriod);
|
||||
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
|
||||
rotationPeriod + 1);
|
||||
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
|
||||
rotationPeriod);
|
||||
// Collect and return the keys
|
||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeys rotateTransportKeys(TransportKeys k,
|
||||
long rotationPeriod) {
|
||||
if (k.getRotationPeriod() >= rotationPeriod) return k;
|
||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||
long startPeriod = outCurr.getRotationPeriod();
|
||||
// Rotate the keys
|
||||
for (long p = startPeriod + 1; p <= rotationPeriod; 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);
|
||||
}
|
||||
// Collect and return the keys
|
||||
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
||||
outCurr);
|
||||
}
|
||||
|
||||
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
|
||||
byte[] period = new byte[INT_64_BYTES];
|
||||
ByteUtils.writeUint64(rotationPeriod, period, 0);
|
||||
return new SecretKey(macKdf(k, ROTATE, period));
|
||||
}
|
||||
|
||||
private SecretKey deriveTagKey(SecretKey master, TransportId t,
|
||||
boolean alice) {
|
||||
byte[] id = StringUtils.toUtf8(t.getString());
|
||||
return new SecretKey(macKdf(master, alice ? A_TAG : B_TAG, id));
|
||||
}
|
||||
|
||||
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
|
||||
boolean alice) {
|
||||
byte[] id = StringUtils.toUtf8(t.getString());
|
||||
return new SecretKey(macKdf(master, alice ? A_HEADER : B_HEADER, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||
long streamNumber) {
|
||||
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
// Initialise the PRF
|
||||
Digest prf = new Blake2sDigest(tagKey.getBytes());
|
||||
// The output of the PRF must be long enough to use as a tag
|
||||
int macLength = prf.getDigestSize();
|
||||
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
||||
// The input is the protocol version as a 16-bit integer, followed by
|
||||
// the stream number as a 64-bit integer
|
||||
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
|
||||
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
|
||||
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
|
||||
byte[] streamNumberBytes = new byte[INT_64_BYTES];
|
||||
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
|
||||
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
|
||||
byte[] mac = new byte[macLength];
|
||||
prf.doFinal(mac, 0);
|
||||
// The output is the first TAG_LENGTH bytes of the MAC
|
||||
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -313,13 +513,14 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] mac(String label, SecretKey macKey, byte[]... inputs) {
|
||||
byte[] labelBytes = StringUtils.toUtf8(label);
|
||||
public int getHashLength() {
|
||||
return HASH_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] mac(SecretKey macKey, byte[]... inputs) {
|
||||
Digest mac = new Blake2sDigest(macKey.getBytes());
|
||||
byte[] length = new byte[INT_32_BYTES];
|
||||
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
||||
mac.update(length, 0, length.length);
|
||||
mac.update(labelBytes, 0, labelBytes.length);
|
||||
for (byte[] input : inputs) {
|
||||
ByteUtils.writeUint32(input.length, length, 0);
|
||||
mac.update(length, 0, length.length);
|
||||
@@ -411,6 +612,30 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
return AsciiArmour.wrap(b, lineLength);
|
||||
}
|
||||
|
||||
// Key derivation function based on a pseudo-random function - see
|
||||
// NIST SP 800-108, section 5.1
|
||||
private byte[] macKdf(SecretKey key, byte[]... inputs) {
|
||||
// Initialise the PRF
|
||||
Digest prf = new Blake2sDigest(key.getBytes());
|
||||
// The output of the PRF must be long enough to use as a key
|
||||
int macLength = prf.getDigestSize();
|
||||
if (macLength < SecretKey.LENGTH) throw new IllegalStateException();
|
||||
// Calculate the PRF over the concatenated length-prefixed inputs
|
||||
byte[] length = new byte[INT_32_BYTES];
|
||||
for (byte[] input : inputs) {
|
||||
ByteUtils.writeUint32(input.length, length, 0);
|
||||
prf.update(length, 0, length.length);
|
||||
prf.update(input, 0, input.length);
|
||||
}
|
||||
byte[] mac = new byte[macLength];
|
||||
prf.doFinal(mac, 0);
|
||||
// The output is the first SecretKey.LENGTH bytes of the MAC
|
||||
if (mac.length == SecretKey.LENGTH) return mac;
|
||||
byte[] truncated = new byte[SecretKey.LENGTH];
|
||||
System.arraycopy(mac, 0, truncated, 0, truncated.length);
|
||||
return truncated;
|
||||
}
|
||||
|
||||
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
|
||||
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
||||
byte[] utf8 = StringUtils.toUtf8(password);
|
||||
|
||||
@@ -3,11 +3,9 @@ package org.briarproject.bramble.crypto;
|
||||
import org.briarproject.bramble.TimeLoggingExecutor;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
|
||||
@@ -76,12 +74,6 @@ public class CryptoModule {
|
||||
return new PasswordStrengthEstimatorImpl();
|
||||
}
|
||||
|
||||
@Provides
|
||||
TransportCrypto provideTransportCrypto(
|
||||
TransportCryptoImpl transportCrypto) {
|
||||
return transportCrypto;
|
||||
}
|
||||
|
||||
@Provides
|
||||
StreamDecrypterFactory provideStreamDecrypterFactory(
|
||||
Provider<AuthenticatedCipher> cipherProvider) {
|
||||
@@ -89,17 +81,9 @@ public class CryptoModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
StreamEncrypterFactory provideStreamEncrypterFactory(
|
||||
CryptoComponent crypto, TransportCrypto transportCrypto,
|
||||
StreamEncrypterFactory provideStreamEncrypterFactory(CryptoComponent crypto,
|
||||
Provider<AuthenticatedCipher> cipherProvider) {
|
||||
return new StreamEncrypterFactoryImpl(crypto, transportCrypto,
|
||||
cipherProvider);
|
||||
}
|
||||
|
||||
@Provides
|
||||
KeyAgreementCrypto provideKeyAgreementCrypto(
|
||||
KeyAgreementCryptoImpl keyAgreementCrypto) {
|
||||
return keyAgreementCrypto;
|
||||
return new StreamEncrypterFactoryImpl(crypto, cipherProvider);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
|
||||
class KeyAgreementCryptoImpl implements KeyAgreementCrypto {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
@Inject
|
||||
KeyAgreementCryptoImpl(CryptoComponent crypto) {
|
||||
this.crypto = crypto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveKeyCommitment(PublicKey publicKey) {
|
||||
byte[] hash = crypto.hash(COMMIT_LABEL, publicKey.getEncoded());
|
||||
// The output is the first COMMIT_LENGTH bytes of the hash
|
||||
byte[] commitment = new byte[COMMIT_LENGTH];
|
||||
System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH);
|
||||
return commitment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||
byte[] theirPayload, byte[] ourPayload, PublicKey theirPublicKey,
|
||||
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
|
||||
SecretKey ck = crypto.deriveKey(CONFIRMATION_KEY_LABEL, sharedSecret);
|
||||
byte[] alicePayload, alicePub, bobPayload, bobPub;
|
||||
if (alice) {
|
||||
alicePayload = ourPayload;
|
||||
alicePub = ourKeyPair.getPublic().getEncoded();
|
||||
bobPayload = theirPayload;
|
||||
bobPub = theirPublicKey.getEncoded();
|
||||
} else {
|
||||
alicePayload = theirPayload;
|
||||
alicePub = theirPublicKey.getEncoded();
|
||||
bobPayload = ourPayload;
|
||||
bobPub = ourKeyPair.getPublic().getEncoded();
|
||||
}
|
||||
if (aliceRecord) {
|
||||
return crypto.mac(CONFIRMATION_MAC_LABEL, ck, alicePayload,
|
||||
alicePub, bobPayload, bobPub);
|
||||
} else {
|
||||
return crypto.mac(CONFIRMATION_MAC_LABEL, ck, bobPayload, bobPub,
|
||||
alicePayload, alicePub);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.transport.StreamContext;
|
||||
|
||||
@@ -23,15 +22,12 @@ import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENG
|
||||
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final TransportCrypto transportCrypto;
|
||||
private final Provider<AuthenticatedCipher> cipherProvider;
|
||||
|
||||
@Inject
|
||||
StreamEncrypterFactoryImpl(CryptoComponent crypto,
|
||||
TransportCrypto transportCrypto,
|
||||
Provider<AuthenticatedCipher> cipherProvider) {
|
||||
this.crypto = crypto;
|
||||
this.transportCrypto = transportCrypto;
|
||||
this.cipherProvider = cipherProvider;
|
||||
}
|
||||
|
||||
@@ -41,8 +37,7 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||
AuthenticatedCipher cipher = cipherProvider.get();
|
||||
long streamNumber = ctx.getStreamNumber();
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
|
||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
||||
SecretKey frameKey = crypto.generateSecretKey();
|
||||
|
||||
@@ -1,135 +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.IncomingKeys;
|
||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.spongycastle.crypto.Digest;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_TAG_LABEL;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_LABEL;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_TAG_LABEL;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
class TransportCryptoImpl implements TransportCrypto {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
@Inject
|
||||
TransportCryptoImpl(CryptoComponent crypto) {
|
||||
this.crypto = crypto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeys deriveTransportKeys(TransportId t,
|
||||
SecretKey master, long rotationPeriod, boolean alice) {
|
||||
// Keys for the previous period are derived from the master secret
|
||||
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
|
||||
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
|
||||
SecretKey outTagPrev = deriveTagKey(master, t, alice);
|
||||
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
|
||||
// Derive the keys for the current and next periods
|
||||
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
|
||||
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
|
||||
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
|
||||
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
|
||||
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
|
||||
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
|
||||
// Initialise the reordering windows and stream counters
|
||||
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
|
||||
rotationPeriod - 1);
|
||||
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
|
||||
rotationPeriod);
|
||||
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
|
||||
rotationPeriod + 1);
|
||||
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
|
||||
rotationPeriod);
|
||||
// Collect and return the keys
|
||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeys rotateTransportKeys(TransportKeys k,
|
||||
long rotationPeriod) {
|
||||
if (k.getRotationPeriod() >= rotationPeriod) return k;
|
||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||
long startPeriod = outCurr.getRotationPeriod();
|
||||
// Rotate the keys
|
||||
for (long p = startPeriod + 1; p <= rotationPeriod; 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);
|
||||
}
|
||||
// Collect and return the keys
|
||||
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
||||
outCurr);
|
||||
}
|
||||
|
||||
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
|
||||
byte[] period = new byte[INT_64_BYTES];
|
||||
ByteUtils.writeUint64(rotationPeriod, period, 0);
|
||||
return crypto.deriveKey(ROTATE_LABEL, k, period);
|
||||
}
|
||||
|
||||
private SecretKey deriveTagKey(SecretKey master, TransportId t,
|
||||
boolean alice) {
|
||||
String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
|
||||
byte[] id = StringUtils.toUtf8(t.getString());
|
||||
return crypto.deriveKey(label, master, id);
|
||||
}
|
||||
|
||||
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
|
||||
boolean alice) {
|
||||
String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL;
|
||||
byte[] id = StringUtils.toUtf8(t.getString());
|
||||
return crypto.deriveKey(label, master, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||
long streamNumber) {
|
||||
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
// Initialise the PRF
|
||||
Digest prf = new Blake2sDigest(tagKey.getBytes());
|
||||
// The output of the PRF must be long enough to use as a tag
|
||||
int macLength = prf.getDigestSize();
|
||||
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
||||
// The input is the protocol version as a 16-bit integer, followed by
|
||||
// the stream number as a 64-bit integer
|
||||
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
|
||||
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
|
||||
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
|
||||
byte[] streamNumberBytes = new byte[INT_64_BYTES];
|
||||
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
|
||||
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
|
||||
byte[] mac = new byte[macLength];
|
||||
prf.doFinal(mac, 0);
|
||||
// The output is the first TAG_LENGTH bytes of the MAC
|
||||
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
private final ReentrantReadWriteLock lock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
|
||||
private volatile int shutdownHandle = -1;
|
||||
|
||||
@Inject
|
||||
DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus,
|
||||
ShutdownManager shutdown) {
|
||||
@@ -101,20 +103,22 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
|
||||
@Override
|
||||
public boolean open() throws DbException {
|
||||
boolean reopened = db.open();
|
||||
shutdown.addShutdownHook(() -> {
|
||||
Runnable shutdownHook = () -> {
|
||||
try {
|
||||
close();
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
});
|
||||
};
|
||||
boolean reopened = db.open();
|
||||
shutdownHandle = shutdown.addShutdownHook(shutdownHook);
|
||||
return reopened;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws DbException {
|
||||
if (closed.getAndSet(true)) return;
|
||||
shutdown.removeShutdownHook(shutdownHandle);
|
||||
db.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,23 +22,21 @@ import javax.inject.Inject;
|
||||
class H2Database extends JdbcDatabase {
|
||||
|
||||
private static final String HASH_TYPE = "BINARY(32)";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
private static final String BINARY_TYPE = "BINARY";
|
||||
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
|
||||
@Inject
|
||||
H2Database(DatabaseConfig config, Clock clock) {
|
||||
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
clock);
|
||||
super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE, clock);
|
||||
this.config = config;
|
||||
File dir = config.getDatabaseDirectory();
|
||||
String path = new File(dir, "db").getAbsolutePath();
|
||||
url = "jdbc:h2:split:" + path + ";CIPHER=AES;MULTI_THREADED=1"
|
||||
+ ";WRITE_DELAY=0";
|
||||
+ ";WRITE_DELAY=0;DB_CLOSE_ON_EXIT=false";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Contains all the HSQLDB-specific code for the database.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
class HyperSqlDatabase extends JdbcDatabase {
|
||||
|
||||
private static final String HASH_TYPE = "BINARY(32)";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
private static final String BINARY_TYPE = "BINARY";
|
||||
private static final String COUNTER_TYPE =
|
||||
"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1)";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
|
||||
@Inject
|
||||
HyperSqlDatabase(DatabaseConfig config, Clock clock) {
|
||||
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
clock);
|
||||
this.config = config;
|
||||
File dir = config.getDatabaseDirectory();
|
||||
String path = new File(dir, "db").getAbsolutePath();
|
||||
url = "jdbc:hsqldb:file:" + path
|
||||
+ ";sql.enforce_size=false;allow_empty_batch=true"
|
||||
+ ";encrypt_lobs=true;crypt_type=AES";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() throws DbException {
|
||||
boolean reopen = config.databaseExists();
|
||||
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws DbException {
|
||||
try {
|
||||
super.closeAllConnections();
|
||||
Connection c = createConnection();
|
||||
Statement s = c.createStatement();
|
||||
s.executeQuery("SHUTDOWN");
|
||||
s.close();
|
||||
c.close();
|
||||
} catch (SQLException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFreeSpace() throws DbException {
|
||||
File dir = config.getDatabaseDirectory();
|
||||
long maxSize = config.getMaxSize();
|
||||
long free = dir.getFreeSpace();
|
||||
long used = getDiskSpace(dir);
|
||||
long quota = maxSize - used;
|
||||
return Math.min(free, quota);
|
||||
}
|
||||
|
||||
private long getDiskSpace(File f) {
|
||||
if (f.isDirectory()) {
|
||||
long total = 0;
|
||||
File[] children = f.listFiles();
|
||||
if (children != null)
|
||||
for (File child : children) total += getDiskSpace(child);
|
||||
return total;
|
||||
} else if (f.isFile()) {
|
||||
return f.length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection createConnection() throws SQLException {
|
||||
SecretKey key = config.getEncryptionKey();
|
||||
if (key == null) throw new IllegalStateException();
|
||||
String hex = StringUtils.toHexString(key.getBytes());
|
||||
return DriverManager.getConnection(url + ";crypt_key=" + hex);
|
||||
}
|
||||
}
|
||||
@@ -68,32 +68,32 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
||||
@NotNullByDefault
|
||||
abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final int SCHEMA_VERSION = 31;
|
||||
private static final int MIN_SCHEMA_VERSION = 31;
|
||||
private static final int SCHEMA_VERSION = 30;
|
||||
private static final int MIN_SCHEMA_VERSION = 30;
|
||||
|
||||
private static final String CREATE_SETTINGS =
|
||||
"CREATE TABLE settings"
|
||||
+ " (namespace _STRING NOT NULL,"
|
||||
+ " settingKey _STRING NOT NULL,"
|
||||
+ " value _STRING NOT NULL,"
|
||||
+ " PRIMARY KEY (namespace, settingKey))";
|
||||
+ " (namespace VARCHAR NOT NULL,"
|
||||
+ " key VARCHAR NOT NULL,"
|
||||
+ " value VARCHAR NOT NULL,"
|
||||
+ " PRIMARY KEY (namespace, key))";
|
||||
|
||||
private static final String CREATE_LOCAL_AUTHORS =
|
||||
"CREATE TABLE localAuthors"
|
||||
+ " (authorId _HASH NOT NULL,"
|
||||
+ " name _STRING NOT NULL,"
|
||||
+ " publicKey _BINARY NOT NULL,"
|
||||
+ " privateKey _BINARY NOT NULL,"
|
||||
+ " (authorId HASH NOT NULL,"
|
||||
+ " name VARCHAR NOT NULL,"
|
||||
+ " publicKey BINARY NOT NULL,"
|
||||
+ " privateKey BINARY NOT NULL,"
|
||||
+ " created BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (authorId))";
|
||||
|
||||
private static final String CREATE_CONTACTS =
|
||||
"CREATE TABLE contacts"
|
||||
+ " (contactId _COUNTER,"
|
||||
+ " authorId _HASH NOT NULL,"
|
||||
+ " name _STRING NOT NULL,"
|
||||
+ " publicKey _BINARY NOT NULL,"
|
||||
+ " localAuthorId _HASH NOT NULL,"
|
||||
+ " (contactId COUNTER,"
|
||||
+ " authorId HASH NOT NULL,"
|
||||
+ " name VARCHAR NOT NULL,"
|
||||
+ " publicKey BINARY NOT NULL,"
|
||||
+ " localAuthorId HASH NOT NULL,"
|
||||
+ " verified BOOLEAN NOT NULL,"
|
||||
+ " active BOOLEAN NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId),"
|
||||
@@ -103,17 +103,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_GROUPS =
|
||||
"CREATE TABLE groups"
|
||||
+ " (groupId _HASH NOT NULL,"
|
||||
+ " clientId _STRING NOT NULL,"
|
||||
+ " descriptor _BINARY NOT NULL,"
|
||||
+ " (groupId HASH NOT NULL,"
|
||||
+ " clientId VARCHAR NOT NULL,"
|
||||
+ " descriptor BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (groupId))";
|
||||
|
||||
private static final String CREATE_GROUP_METADATA =
|
||||
"CREATE TABLE groupMetadata"
|
||||
+ " (groupId _HASH NOT NULL,"
|
||||
+ " metaKey _STRING NOT NULL,"
|
||||
+ " value _BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (groupId, metaKey),"
|
||||
+ " (groupId HASH NOT NULL,"
|
||||
+ " key VARCHAR NOT NULL,"
|
||||
+ " value BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (groupId, key),"
|
||||
+ " FOREIGN KEY (groupId)"
|
||||
+ " REFERENCES groups (groupId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
@@ -121,7 +121,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
private static final String CREATE_GROUP_VISIBILITIES =
|
||||
"CREATE TABLE groupVisibilities"
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " groupId _HASH NOT NULL,"
|
||||
+ " groupId HASH NOT NULL,"
|
||||
+ " shared BOOLEAN NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, groupId),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
@@ -133,8 +133,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_MESSAGES =
|
||||
"CREATE TABLE messages"
|
||||
+ " (messageId _HASH NOT NULL,"
|
||||
+ " groupId _HASH NOT NULL,"
|
||||
+ " (messageId HASH NOT NULL,"
|
||||
+ " groupId HASH NOT NULL,"
|
||||
+ " timestamp BIGINT NOT NULL,"
|
||||
+ " state INT NOT NULL,"
|
||||
+ " shared BOOLEAN NOT NULL,"
|
||||
@@ -147,19 +147,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_MESSAGE_METADATA =
|
||||
"CREATE TABLE messageMetadata"
|
||||
+ " (messageId _HASH NOT NULL,"
|
||||
+ " metaKey _STRING NOT NULL,"
|
||||
+ " value _BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (messageId, metaKey),"
|
||||
+ " (messageId HASH NOT NULL,"
|
||||
+ " key VARCHAR NOT NULL,"
|
||||
+ " value BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (messageId, key),"
|
||||
+ " FOREIGN KEY (messageId)"
|
||||
+ " REFERENCES messages (messageId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
private static final String CREATE_MESSAGE_DEPENDENCIES =
|
||||
"CREATE TABLE messageDependencies"
|
||||
+ " (groupId _HASH NOT NULL,"
|
||||
+ " messageId _HASH NOT NULL,"
|
||||
+ " dependencyId _HASH NOT NULL," // Not a foreign key
|
||||
+ " (groupId HASH NOT NULL,"
|
||||
+ " messageId HASH NOT NULL,"
|
||||
+ " dependencyId HASH NOT NULL," // Not a foreign key
|
||||
+ " FOREIGN KEY (groupId)"
|
||||
+ " REFERENCES groups (groupId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
@@ -169,7 +169,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_OFFERS =
|
||||
"CREATE TABLE offers"
|
||||
+ " (messageId _HASH NOT NULL," // Not a foreign key
|
||||
+ " (messageId HASH NOT NULL," // Not a foreign key
|
||||
+ " contactId INT NOT NULL,"
|
||||
+ " PRIMARY KEY (messageId, contactId),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
@@ -178,7 +178,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_STATUSES =
|
||||
"CREATE TABLE statuses"
|
||||
+ " (messageId _HASH NOT NULL,"
|
||||
+ " (messageId HASH NOT NULL,"
|
||||
+ " contactId INT NOT NULL,"
|
||||
+ " ack BOOLEAN NOT NULL,"
|
||||
+ " seen BOOLEAN NOT NULL,"
|
||||
@@ -195,20 +195,20 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_TRANSPORTS =
|
||||
"CREATE TABLE transports"
|
||||
+ " (transportId _STRING NOT NULL,"
|
||||
+ " (transportId VARCHAR NOT NULL,"
|
||||
+ " maxLatency INT NOT NULL,"
|
||||
+ " PRIMARY KEY (transportId))";
|
||||
|
||||
private static final String CREATE_INCOMING_KEYS =
|
||||
"CREATE TABLE incomingKeys"
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " transportId _STRING NOT NULL,"
|
||||
+ " rotationPeriod BIGINT NOT NULL,"
|
||||
+ " tagKey _SECRET NOT NULL,"
|
||||
+ " headerKey _SECRET NOT NULL,"
|
||||
+ " transportId VARCHAR NOT NULL,"
|
||||
+ " period BIGINT NOT NULL,"
|
||||
+ " tagKey SECRET NOT NULL,"
|
||||
+ " headerKey SECRET NOT NULL,"
|
||||
+ " base BIGINT NOT NULL,"
|
||||
+ " bitmap _BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, transportId, rotationPeriod),"
|
||||
+ " bitmap BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, transportId, period),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
+ " REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
@@ -219,10 +219,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
private static final String CREATE_OUTGOING_KEYS =
|
||||
"CREATE TABLE outgoingKeys"
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " transportId _STRING NOT NULL,"
|
||||
+ " rotationPeriod BIGINT NOT NULL,"
|
||||
+ " tagKey _SECRET NOT NULL,"
|
||||
+ " headerKey _SECRET NOT NULL,"
|
||||
+ " transportId VARCHAR NOT NULL,"
|
||||
+ " period BIGINT NOT NULL,"
|
||||
+ " tagKey SECRET NOT NULL,"
|
||||
+ " headerKey SECRET NOT NULL,"
|
||||
+ " stream BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, transportId),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
@@ -260,8 +260,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
Logger.getLogger(JdbcDatabase.class.getName());
|
||||
|
||||
// Different database libraries use different names for certain types
|
||||
private final String hashType, secretType, binaryType;
|
||||
private final String counterType, stringType;
|
||||
private final String hashType, binaryType, counterType, secretType;
|
||||
private final Clock clock;
|
||||
|
||||
// Locking: connectionsLock
|
||||
@@ -276,13 +275,12 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
private final Lock connectionsLock = new ReentrantLock();
|
||||
private final Condition connectionsChanged = connectionsLock.newCondition();
|
||||
|
||||
JdbcDatabase(String hashType, String secretType, String binaryType,
|
||||
String counterType, String stringType, Clock clock) {
|
||||
JdbcDatabase(String hashType, String binaryType, String counterType,
|
||||
String secretType, Clock clock) {
|
||||
this.hashType = hashType;
|
||||
this.secretType = secretType;
|
||||
this.binaryType = binaryType;
|
||||
this.counterType = counterType;
|
||||
this.stringType = stringType;
|
||||
this.secretType = secretType;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@@ -385,17 +383,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
private String insertTypeNames(String s) {
|
||||
s = s.replaceAll("_HASH", hashType);
|
||||
s = s.replaceAll("_SECRET", secretType);
|
||||
s = s.replaceAll("_BINARY", binaryType);
|
||||
s = s.replaceAll("_COUNTER", counterType);
|
||||
s = s.replaceAll("_STRING", stringType);
|
||||
s = s.replaceAll("HASH", hashType);
|
||||
s = s.replaceAll("BINARY", binaryType);
|
||||
s = s.replaceAll("COUNTER", counterType);
|
||||
s = s.replaceAll("SECRET", secretType);
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection startTransaction() throws DbException {
|
||||
Connection txn;
|
||||
Connection txn = null;
|
||||
connectionsLock.lock();
|
||||
try {
|
||||
if (closed) throw new DbClosedException();
|
||||
@@ -503,8 +500,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
try {
|
||||
// Create a contact row
|
||||
String sql = "INSERT INTO contacts"
|
||||
+ " (authorId, name, publicKey, localAuthorId,"
|
||||
+ " verified, active)"
|
||||
+ " (authorId, name, publicKey, localAuthorId, verified, active)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, remote.getId().getBytes());
|
||||
@@ -723,7 +719,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
try {
|
||||
// Store the incoming keys
|
||||
String sql = "INSERT INTO incomingKeys (contactId, transportId,"
|
||||
+ " rotationPeriod, tagKey, headerKey, base, bitmap)"
|
||||
+ " period, tagKey, headerKey, base, bitmap)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
@@ -758,8 +754,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (rows != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
// Store the outgoing keys
|
||||
sql = "INSERT INTO outgoingKeys (contactId, transportId,"
|
||||
+ " rotationPeriod, tagKey, headerKey, stream)"
|
||||
sql = "INSERT INTO outgoingKeys (contactId, transportId, period,"
|
||||
+ " tagKey, headerKey, stream)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
@@ -1339,7 +1335,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " JOIN messageMetadata AS md"
|
||||
+ " ON m.messageId = md.messageId"
|
||||
+ " WHERE state = ? AND groupId = ?"
|
||||
+ " AND metaKey = ? AND value = ?";
|
||||
+ " AND key = ? AND value = ?";
|
||||
for (Entry<String, byte[]> e : query.entrySet()) {
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, DELIVERED.getValue());
|
||||
@@ -1371,7 +1367,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT m.messageId, metaKey, value"
|
||||
String sql = "SELECT m.messageId, key, value"
|
||||
+ " FROM messages AS m"
|
||||
+ " JOIN messageMetadata AS md"
|
||||
+ " ON m.messageId = md.messageId"
|
||||
@@ -1421,7 +1417,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT metaKey, value FROM groupMetadata"
|
||||
String sql = "SELECT key, value FROM groupMetadata"
|
||||
+ " WHERE groupId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getBytes());
|
||||
@@ -1444,7 +1440,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT metaKey, value FROM messageMetadata AS md"
|
||||
String sql = "SELECT key, value FROM messageMetadata AS md"
|
||||
+ " JOIN messages AS m"
|
||||
+ " ON m.messageId = md.messageId"
|
||||
+ " WHERE m.state = ? AND md.messageId = ?";
|
||||
@@ -1470,7 +1466,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT metaKey, value FROM messageMetadata AS md"
|
||||
String sql = "SELECT key, value FROM messageMetadata AS md"
|
||||
+ " JOIN messages AS m"
|
||||
+ " ON m.messageId = md.messageId"
|
||||
+ " WHERE (m.state = ? OR m.state = ?)"
|
||||
@@ -1908,8 +1904,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT settingKey, value FROM settings"
|
||||
+ " WHERE namespace = ?";
|
||||
String sql = "SELECT key, value FROM settings WHERE namespace = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, namespace);
|
||||
rs = ps.executeQuery();
|
||||
@@ -1932,11 +1927,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
// Retrieve the incoming keys
|
||||
String sql = "SELECT rotationPeriod, tagKey, headerKey,"
|
||||
+ " base, bitmap"
|
||||
String sql = "SELECT period, tagKey, headerKey, base, bitmap"
|
||||
+ " FROM incomingKeys"
|
||||
+ " WHERE transportId = ?"
|
||||
+ " ORDER BY contactId, rotationPeriod";
|
||||
+ " ORDER BY contactId, period";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, t.getString());
|
||||
rs = ps.executeQuery();
|
||||
@@ -1953,10 +1947,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
rs.close();
|
||||
ps.close();
|
||||
// Retrieve the outgoing keys in the same order
|
||||
sql = "SELECT contactId, rotationPeriod, tagKey, headerKey, stream"
|
||||
sql = "SELECT contactId, period, tagKey, headerKey, stream"
|
||||
+ " FROM outgoingKeys"
|
||||
+ " WHERE transportId = ?"
|
||||
+ " ORDER BY contactId, rotationPeriod";
|
||||
+ " ORDER BY contactId, period";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, t.getString());
|
||||
rs = ps.executeQuery();
|
||||
@@ -1993,8 +1987,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE outgoingKeys SET stream = stream + 1"
|
||||
+ " WHERE contactId = ? AND transportId = ?"
|
||||
+ " AND rotationPeriod = ?";
|
||||
+ " WHERE contactId = ? AND transportId = ? AND period = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setString(2, t.getString());
|
||||
@@ -2088,7 +2081,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
// Delete any keys that are being removed
|
||||
if (!removed.isEmpty()) {
|
||||
String sql = "DELETE FROM " + tableName
|
||||
+ " WHERE " + columnName + " = ? AND metaKey = ?";
|
||||
+ " WHERE " + columnName + " = ? AND key = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
for (String key : removed) {
|
||||
@@ -2107,7 +2100,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (retained.isEmpty()) return;
|
||||
// Update any keys that already exist
|
||||
String sql = "UPDATE " + tableName + " SET value = ?"
|
||||
+ " WHERE " + columnName + " = ? AND metaKey = ?";
|
||||
+ " WHERE " + columnName + " = ? AND key = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(2, id);
|
||||
for (Entry<String, byte[]> e : retained.entrySet()) {
|
||||
@@ -2124,7 +2117,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
// Insert any keys that don't already exist
|
||||
sql = "INSERT INTO " + tableName
|
||||
+ " (" + columnName + ", metaKey, value)"
|
||||
+ " (" + columnName + ", key, value)"
|
||||
+ " VALUES (?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
@@ -2156,7 +2149,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
try {
|
||||
// Update any settings that already exist
|
||||
String sql = "UPDATE settings SET value = ?"
|
||||
+ " WHERE namespace = ? AND settingKey = ?";
|
||||
+ " WHERE namespace = ? AND key = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
for (Entry<String, String> e : s.entrySet()) {
|
||||
ps.setString(1, e.getValue());
|
||||
@@ -2171,7 +2164,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (rows > 1) throw new DbStateException();
|
||||
}
|
||||
// Insert any settings that don't already exist
|
||||
sql = "INSERT INTO settings (namespace, settingKey, value)"
|
||||
sql = "INSERT INTO settings (namespace, key, value)"
|
||||
+ " VALUES (?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
int updateIndex = 0, inserted = 0;
|
||||
@@ -2535,8 +2528,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE incomingKeys SET base = ?, bitmap = ?"
|
||||
+ " WHERE contactId = ? AND transportId = ?"
|
||||
+ " AND rotationPeriod = ?";
|
||||
+ " WHERE contactId = ? AND transportId = ? AND period = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, base);
|
||||
ps.setBytes(2, bitmap);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||
@@ -46,7 +46,7 @@ class KeyAgreementConnector {
|
||||
|
||||
private final Callbacks callbacks;
|
||||
private final Clock clock;
|
||||
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||
private final CryptoComponent crypto;
|
||||
private final PluginManager pluginManager;
|
||||
private final CompletionService<KeyAgreementConnection> connect;
|
||||
|
||||
@@ -58,11 +58,11 @@ class KeyAgreementConnector {
|
||||
private volatile boolean alice = false;
|
||||
|
||||
KeyAgreementConnector(Callbacks callbacks, Clock clock,
|
||||
KeyAgreementCrypto keyAgreementCrypto, PluginManager pluginManager,
|
||||
CryptoComponent crypto, PluginManager pluginManager,
|
||||
Executor ioExecutor) {
|
||||
this.callbacks = callbacks;
|
||||
this.clock = clock;
|
||||
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||
this.crypto = crypto;
|
||||
this.pluginManager = pluginManager;
|
||||
connect = new ExecutorCompletionService<>(ioExecutor);
|
||||
}
|
||||
@@ -70,8 +70,8 @@ class KeyAgreementConnector {
|
||||
public Payload listen(KeyPair localKeyPair) {
|
||||
LOG.info("Starting BQP listeners");
|
||||
// Derive commitment
|
||||
byte[] commitment = keyAgreementCrypto.deriveKeyCommitment(
|
||||
localKeyPair.getPublic());
|
||||
byte[] commitment = crypto.deriveKeyCommitment(
|
||||
localKeyPair.getPublic().getEncoded());
|
||||
// Start all listeners and collect their descriptors
|
||||
List<TransportDescriptor> descriptors = new ArrayList<>();
|
||||
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -13,9 +22,13 @@ import dagger.Provides;
|
||||
public class KeyAgreementModule {
|
||||
|
||||
@Provides
|
||||
KeyAgreementTask provideKeyAgreementTask(
|
||||
KeyAgreementTaskImpl keyAgreementTask) {
|
||||
return keyAgreementTask;
|
||||
@Singleton
|
||||
KeyAgreementTaskFactory provideKeyAgreementTaskFactory(Clock clock,
|
||||
CryptoComponent crypto, EventBus eventBus,
|
||||
@IoExecutor Executor ioExecutor, PayloadEncoder payloadEncoder,
|
||||
PluginManager pluginManager) {
|
||||
return new KeyAgreementTaskFactoryImpl(clock, crypto, eventBus,
|
||||
ioExecutor, payloadEncoder, pluginManager);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
@@ -14,10 +11,6 @@ import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
||||
|
||||
/**
|
||||
* Implementation of the BQP protocol.
|
||||
* <p/>
|
||||
@@ -64,7 +57,6 @@ class KeyAgreementProtocol {
|
||||
|
||||
private final Callbacks callbacks;
|
||||
private final CryptoComponent crypto;
|
||||
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||
private final PayloadEncoder payloadEncoder;
|
||||
private final KeyAgreementTransport transport;
|
||||
private final Payload theirPayload, ourPayload;
|
||||
@@ -72,13 +64,11 @@ class KeyAgreementProtocol {
|
||||
private final boolean alice;
|
||||
|
||||
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
|
||||
KeyAgreementCrypto keyAgreementCrypto,
|
||||
PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
|
||||
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
|
||||
boolean alice) {
|
||||
this.callbacks = callbacks;
|
||||
this.crypto = crypto;
|
||||
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||
this.payloadEncoder = payloadEncoder;
|
||||
this.transport = transport;
|
||||
this.theirPayload = theirPayload;
|
||||
@@ -96,7 +86,7 @@ class KeyAgreementProtocol {
|
||||
*/
|
||||
SecretKey perform() throws AbortException, IOException {
|
||||
try {
|
||||
PublicKey theirPublicKey;
|
||||
byte[] theirPublicKey;
|
||||
if (alice) {
|
||||
sendKey();
|
||||
// Alice waits here until Bob obtains her payload.
|
||||
@@ -114,7 +104,7 @@ class KeyAgreementProtocol {
|
||||
receiveConfirm(s, theirPublicKey);
|
||||
sendConfirm(s, theirPublicKey);
|
||||
}
|
||||
return crypto.deriveKey(MASTER_SECRET_LABEL, s);
|
||||
return crypto.deriveMasterSecret(s);
|
||||
} catch (AbortException e) {
|
||||
sendAbort(e.getCause() != null);
|
||||
throw e;
|
||||
@@ -125,41 +115,27 @@ class KeyAgreementProtocol {
|
||||
transport.sendKey(ourKeyPair.getPublic().getEncoded());
|
||||
}
|
||||
|
||||
private PublicKey receiveKey() throws AbortException {
|
||||
byte[] publicKeyBytes = transport.receiveKey();
|
||||
private byte[] receiveKey() throws AbortException {
|
||||
byte[] publicKey = transport.receiveKey();
|
||||
callbacks.initialRecordReceived();
|
||||
KeyParser keyParser = crypto.getAgreementKeyParser();
|
||||
try {
|
||||
PublicKey publicKey = keyParser.parsePublicKey(publicKeyBytes);
|
||||
byte[] expected = keyAgreementCrypto.deriveKeyCommitment(publicKey);
|
||||
if (!Arrays.equals(expected, theirPayload.getCommitment()))
|
||||
throw new AbortException();
|
||||
return publicKey;
|
||||
} catch (GeneralSecurityException e) {
|
||||
byte[] expected = crypto.deriveKeyCommitment(publicKey);
|
||||
if (!Arrays.equals(expected, theirPayload.getCommitment()))
|
||||
throw new AbortException();
|
||||
}
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
private SecretKey deriveSharedSecret(PublicKey theirPublicKey)
|
||||
private SecretKey deriveSharedSecret(byte[] theirPublicKey)
|
||||
throws AbortException {
|
||||
try {
|
||||
byte[] ourPublicKeyBytes = ourKeyPair.getPublic().getEncoded();
|
||||
byte[] theirPublicKeyBytes = theirPublicKey.getEncoded();
|
||||
byte[][] inputs = {
|
||||
new byte[] {PROTOCOL_VERSION},
|
||||
alice ? ourPublicKeyBytes : theirPublicKeyBytes,
|
||||
alice ? theirPublicKeyBytes : ourPublicKeyBytes
|
||||
};
|
||||
return crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||
theirPublicKey, ourKeyPair, inputs);
|
||||
return crypto.deriveSharedSecret(theirPublicKey, ourKeyPair, alice);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new AbortException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendConfirm(SecretKey s, PublicKey theirPublicKey)
|
||||
private void sendConfirm(SecretKey s, byte[] theirPublicKey)
|
||||
throws IOException {
|
||||
byte[] confirm = keyAgreementCrypto.deriveConfirmationRecord(s,
|
||||
byte[] confirm = crypto.deriveConfirmationRecord(s,
|
||||
payloadEncoder.encode(theirPayload),
|
||||
payloadEncoder.encode(ourPayload),
|
||||
theirPublicKey, ourKeyPair,
|
||||
@@ -167,10 +143,10 @@ class KeyAgreementProtocol {
|
||||
transport.sendConfirm(confirm);
|
||||
}
|
||||
|
||||
private void receiveConfirm(SecretKey s, PublicKey theirPublicKey)
|
||||
private void receiveConfirm(SecretKey s, byte[] theirPublicKey)
|
||||
throws AbortException {
|
||||
byte[] confirm = transport.receiveConfirm();
|
||||
byte[] expected = keyAgreementCrypto.deriveConfirmationRecord(s,
|
||||
byte[] expected = crypto.deriveConfirmationRecord(s,
|
||||
payloadEncoder.encode(theirPayload),
|
||||
payloadEncoder.encode(ourPayload),
|
||||
theirPublicKey, ourKeyPair,
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class KeyAgreementTaskFactoryImpl implements KeyAgreementTaskFactory {
|
||||
|
||||
private final Clock clock;
|
||||
private final CryptoComponent crypto;
|
||||
private final EventBus eventBus;
|
||||
private final Executor ioExecutor;
|
||||
private final PayloadEncoder payloadEncoder;
|
||||
private final PluginManager pluginManager;
|
||||
|
||||
@Inject
|
||||
KeyAgreementTaskFactoryImpl(Clock clock, CryptoComponent crypto,
|
||||
EventBus eventBus, @IoExecutor Executor ioExecutor,
|
||||
PayloadEncoder payloadEncoder, PluginManager pluginManager) {
|
||||
this.clock = clock;
|
||||
this.crypto = crypto;
|
||||
this.eventBus = eventBus;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.payloadEncoder = payloadEncoder;
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyAgreementTask createTask() {
|
||||
return new KeyAgreementTaskImpl(clock, crypto, eventBus, payloadEncoder,
|
||||
pluginManager, ioExecutor);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
@@ -15,7 +14,6 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent
|
||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
|
||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
|
||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
@@ -25,8 +23,6 @@ import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -39,7 +35,6 @@ class KeyAgreementTaskImpl extends Thread implements
|
||||
Logger.getLogger(KeyAgreementTaskImpl.class.getName());
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||
private final EventBus eventBus;
|
||||
private final PayloadEncoder payloadEncoder;
|
||||
private final KeyPair localKeyPair;
|
||||
@@ -48,17 +43,14 @@ class KeyAgreementTaskImpl extends Thread implements
|
||||
private Payload localPayload;
|
||||
private Payload remotePayload;
|
||||
|
||||
@Inject
|
||||
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
|
||||
KeyAgreementCrypto keyAgreementCrypto, EventBus eventBus,
|
||||
PayloadEncoder payloadEncoder, PluginManager pluginManager,
|
||||
@IoExecutor Executor ioExecutor) {
|
||||
EventBus eventBus, PayloadEncoder payloadEncoder,
|
||||
PluginManager pluginManager, Executor ioExecutor) {
|
||||
this.crypto = crypto;
|
||||
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||
this.eventBus = eventBus;
|
||||
this.payloadEncoder = payloadEncoder;
|
||||
localKeyPair = crypto.generateAgreementKeyPair();
|
||||
connector = new KeyAgreementConnector(this, clock, keyAgreementCrypto,
|
||||
connector = new KeyAgreementConnector(this, clock, crypto,
|
||||
pluginManager, ioExecutor);
|
||||
}
|
||||
|
||||
@@ -108,8 +100,8 @@ class KeyAgreementTaskImpl extends Thread implements
|
||||
// Run BQP protocol over the connection
|
||||
LOG.info("Starting BQP protocol");
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
|
||||
keyAgreementCrypto, payloadEncoder, transport, remotePayload,
|
||||
localPayload, localKeyPair, alice);
|
||||
payloadEncoder, transport, remotePayload, localPayload,
|
||||
localKeyPair, alice);
|
||||
try {
|
||||
SecretKey master = protocol.perform();
|
||||
KeyAgreementResult result =
|
||||
|
||||
@@ -58,8 +58,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
this.metadataParser = metadataParser;
|
||||
this.contactGroupFactory = contactGroupFactory;
|
||||
this.clock = clock;
|
||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
|
||||
CLIENT_VERSION);
|
||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -288,8 +287,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
}
|
||||
|
||||
private Group getContactGroup(Contact c) {
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, c);
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID, c);
|
||||
}
|
||||
|
||||
private void storeMessage(Transaction txn, GroupId g, TransportId t,
|
||||
|
||||
@@ -6,16 +6,11 @@ import org.briarproject.bramble.api.sync.ClientId;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.GroupFactory;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.sync.GroupId.LABEL;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class GroupFactoryImpl implements GroupFactory {
|
||||
@@ -28,12 +23,9 @@ class GroupFactoryImpl implements GroupFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createGroup(ClientId c, int clientVersion, byte[] descriptor) {
|
||||
byte[] clientVersionBytes = new byte[INT_32_BYTES];
|
||||
ByteUtils.writeUint32(clientVersion, clientVersionBytes, 0);
|
||||
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
|
||||
StringUtils.toUtf8(c.getString()), clientVersionBytes,
|
||||
descriptor);
|
||||
public Group createGroup(ClientId c, byte[] descriptor) {
|
||||
byte[] hash = crypto.hash(GroupId.LABEL,
|
||||
StringUtils.toUtf8(c.getString()), descriptor);
|
||||
return new Group(new GroupId(hash), c, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,8 @@ import org.briarproject.bramble.util.ByteUtils;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.sync.MessageId.LABEL;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -34,9 +32,9 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
throw new IllegalArgumentException();
|
||||
byte[] timeBytes = new byte[ByteUtils.INT_64_BYTES];
|
||||
ByteUtils.writeUint64(timestamp, timeBytes, 0);
|
||||
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
|
||||
g.getBytes(), timeBytes, body);
|
||||
MessageId id = new MessageId(hash);
|
||||
byte[] idHash =
|
||||
crypto.hash(MessageId.LABEL, g.getBytes(), timeBytes, body);
|
||||
MessageId id = new MessageId(idHash);
|
||||
byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length];
|
||||
System.arraycopy(g.getBytes(), 0, raw, 0, UniqueId.LENGTH);
|
||||
ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
@@ -20,18 +20,17 @@ class TransportKeyManagerFactoryImpl implements
|
||||
TransportKeyManagerFactory {
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final TransportCrypto transportCrypto;
|
||||
private final CryptoComponent crypto;
|
||||
private final Executor dbExecutor;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
TransportKeyManagerFactoryImpl(DatabaseComponent db,
|
||||
TransportCrypto transportCrypto,
|
||||
TransportKeyManagerFactoryImpl(DatabaseComponent db, CryptoComponent crypto,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
@Scheduler ScheduledExecutorService scheduler, Clock clock) {
|
||||
this.db = db;
|
||||
this.transportCrypto = transportCrypto;
|
||||
this.crypto = crypto;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.scheduler = scheduler;
|
||||
this.clock = clock;
|
||||
@@ -40,8 +39,8 @@ class TransportKeyManagerFactoryImpl implements
|
||||
@Override
|
||||
public TransportKeyManager createTransportKeyManager(
|
||||
TransportId transportId, long maxLatency) {
|
||||
return new TransportKeyManagerImpl(db, transportCrypto, dbExecutor,
|
||||
scheduler, clock, transportId, maxLatency);
|
||||
return new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler,
|
||||
clock, transportId, maxLatency);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
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.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
@@ -41,7 +41,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
Logger.getLogger(TransportKeyManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final TransportCrypto transportCrypto;
|
||||
private final CryptoComponent crypto;
|
||||
private final Executor dbExecutor;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Clock clock;
|
||||
@@ -54,12 +54,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
private final Map<ContactId, MutableOutgoingKeys> outContexts;
|
||||
private final Map<ContactId, MutableTransportKeys> keys;
|
||||
|
||||
TransportKeyManagerImpl(DatabaseComponent db,
|
||||
TransportCrypto transportCrypto, Executor dbExecutor,
|
||||
@Scheduler ScheduledExecutorService scheduler, Clock clock,
|
||||
TransportId transportId, long maxLatency) {
|
||||
TransportKeyManagerImpl(DatabaseComponent db, CryptoComponent crypto,
|
||||
Executor dbExecutor, @Scheduler ScheduledExecutorService scheduler,
|
||||
Clock clock, TransportId transportId, long maxLatency) {
|
||||
this.db = db;
|
||||
this.transportCrypto = transportCrypto;
|
||||
this.crypto = crypto;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.scheduler = scheduler;
|
||||
this.clock = clock;
|
||||
@@ -100,8 +99,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
|
||||
ContactId c = e.getKey();
|
||||
TransportKeys k = e.getValue();
|
||||
TransportKeys k1 =
|
||||
transportCrypto.rotateTransportKeys(k, rotationPeriod);
|
||||
TransportKeys k1 = crypto.rotateTransportKeys(k, rotationPeriod);
|
||||
if (k1.getRotationPeriod() > k.getRotationPeriod())
|
||||
rotationResult.rotated.put(c, k1);
|
||||
rotationResult.current.put(c, k1);
|
||||
@@ -129,7 +127,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
||||
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||
crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
inContexts.put(new Bytes(tag), tagCtx);
|
||||
}
|
||||
@@ -164,11 +162,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
// Work out what rotation period the timestamp belongs to
|
||||
long rotationPeriod = timestamp / rotationPeriodLength;
|
||||
// Derive the transport keys
|
||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, rotationPeriod, alice);
|
||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
||||
rotationPeriod, alice);
|
||||
// Rotate the keys to the current rotation period if necessary
|
||||
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
|
||||
k = transportCrypto.rotateTransportKeys(k, rotationPeriod);
|
||||
k = crypto.rotateTransportKeys(k, rotationPeriod);
|
||||
// Initialise mutable state for the contact
|
||||
addKeys(c, new MutableTransportKeys(k));
|
||||
// Write the keys back to the DB
|
||||
@@ -236,8 +234,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
// Add tags for any stream numbers added to the window
|
||||
for (long streamNumber : change.getAdded()) {
|
||||
byte[] addTag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
|
||||
PROTOCOL_VERSION, streamNumber);
|
||||
crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
inContexts.put(new Bytes(addTag), new TagContext(
|
||||
tagCtx.contactId, inKeys, streamNumber));
|
||||
}
|
||||
@@ -245,7 +243,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
for (long streamNumber : change.getRemoved()) {
|
||||
if (streamNumber == tagCtx.streamNumber) continue;
|
||||
byte[] removeTag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(removeTag, inKeys.getTagKey(),
|
||||
crypto.encodeTag(removeTag, inKeys.getTagKey(),
|
||||
PROTOCOL_VERSION, streamNumber);
|
||||
inContexts.remove(new Bytes(removeTag));
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import net.i2p.crypto.eddsa.EdDSASecurityProvider;
|
||||
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
||||
|
||||
import org.spongycastle.asn1.sec.SECNamedCurves;
|
||||
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||
import org.spongycastle.asn1.x9.X9ECParameters;
|
||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.spongycastle.crypto.BasicAgreement;
|
||||
import org.spongycastle.crypto.Digest;
|
||||
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
|
||||
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
||||
import org.spongycastle.crypto.params.ECDomainParameters;
|
||||
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.spongycastle.crypto.params.ParametersWithRandom;
|
||||
import org.spongycastle.crypto.signers.DSADigestSigner;
|
||||
import org.spongycastle.crypto.signers.DSAKCalculator;
|
||||
@@ -22,23 +19,14 @@ import org.spongycastle.crypto.signers.HMacDSAKCalculator;
|
||||
import org.spongycastle.math.ec.ECCurve;
|
||||
import org.spongycastle.math.ec.ECPoint;
|
||||
import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
|
||||
import org.whispersystems.curve25519.Curve25519;
|
||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Signature;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
|
||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
||||
|
||||
// Not a JUnit test
|
||||
public class EllipticCurvePerformanceTest {
|
||||
|
||||
@@ -49,9 +37,8 @@ public class EllipticCurvePerformanceTest {
|
||||
"secp256k1", "secp256r1", "secp384r1", "secp521r1");
|
||||
private static final List<String> BRAINPOOL_NAMES = Arrays.asList(
|
||||
"brainpoolp256r1", "brainpoolp384r1", "brainpoolp512r1");
|
||||
private static final Provider ED_PROVIDER = new EdDSASecurityProvider();
|
||||
|
||||
public static void main(String[] args) throws GeneralSecurityException {
|
||||
public static void main(String[] args) {
|
||||
for (String name : SEC_NAMES) {
|
||||
ECDomainParameters params =
|
||||
convertParams(SECNamedCurves.getByName(name));
|
||||
@@ -64,32 +51,43 @@ public class EllipticCurvePerformanceTest {
|
||||
runTest(name + " default", params);
|
||||
runTest(name + " constant", constantTime(params));
|
||||
}
|
||||
runTest("ours", PARAMETERS);
|
||||
runCurve25519Test();
|
||||
runEd25519Test();
|
||||
runTest("ours", EllipticCurveConstants.PARAMETERS);
|
||||
}
|
||||
|
||||
private static void runTest(String name, ECDomainParameters params) {
|
||||
// Generate two key pairs using the given parameters
|
||||
ECKeyGenerationParameters generatorParams =
|
||||
new ECKeyGenerationParameters(params, random);
|
||||
ECKeyPairGenerator generator = new ECKeyPairGenerator();
|
||||
generator.init(new ECKeyGenerationParameters(params, random));
|
||||
generator.init(generatorParams);
|
||||
AsymmetricCipherKeyPair keyPair1 = generator.generateKeyPair();
|
||||
ECPublicKeyParameters public1 =
|
||||
(ECPublicKeyParameters) keyPair1.getPublic();
|
||||
ECPrivateKeyParameters private1 =
|
||||
(ECPrivateKeyParameters) keyPair1.getPrivate();
|
||||
AsymmetricCipherKeyPair keyPair2 = generator.generateKeyPair();
|
||||
// Time some ECDH and ECDHC key agreements
|
||||
long agreementMedian = runAgreementTest(keyPair1, keyPair2, false);
|
||||
long agreementWithCofactorMedian =
|
||||
runAgreementTest(keyPair1, keyPair2, true);
|
||||
// Time some signatures
|
||||
ECPublicKeyParameters public2 =
|
||||
(ECPublicKeyParameters) keyPair2.getPublic();
|
||||
// Time some ECDH key agreements
|
||||
List<Long> samples = new ArrayList<>();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
|
||||
long start = System.nanoTime();
|
||||
agreement.init(private1);
|
||||
agreement.calculateAgreement(public2);
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
long agreementMedian = median(samples);
|
||||
// Time some signatures
|
||||
List<byte[]> signatures = new ArrayList<>();
|
||||
samples.clear();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
Digest digest = new Blake2sDigest();
|
||||
DSAKCalculator calculator = new HMacDSAKCalculator(digest);
|
||||
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
||||
calculator), digest);
|
||||
long start = System.nanoTime();
|
||||
signer.init(true,
|
||||
new ParametersWithRandom(keyPair1.getPrivate(), random));
|
||||
signer.init(true, new ParametersWithRandom(private1, random));
|
||||
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||
signatures.add(signer.generateSignature());
|
||||
samples.add(System.nanoTime() - start);
|
||||
@@ -103,83 +101,17 @@ public class EllipticCurvePerformanceTest {
|
||||
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
||||
calculator), digest);
|
||||
long start = System.nanoTime();
|
||||
signer.init(false, keyPair1.getPublic());
|
||||
signer.init(false, public1);
|
||||
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||
if (!signer.verifySignature(signatures.get(i)))
|
||||
throw new AssertionError();
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
long verificationMedian = median(samples);
|
||||
System.out.println(String.format("%s: %,d %,d %,d %,d", name,
|
||||
agreementMedian, agreementWithCofactorMedian,
|
||||
signatureMedian, verificationMedian));
|
||||
}
|
||||
|
||||
private static long runAgreementTest(AsymmetricCipherKeyPair keyPair1,
|
||||
AsymmetricCipherKeyPair keyPair2, boolean withCofactor) {
|
||||
List<Long> samples = new ArrayList<>();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
BasicAgreement agreement = createAgreement(withCofactor);
|
||||
long start = System.nanoTime();
|
||||
agreement.init(keyPair1.getPrivate());
|
||||
agreement.calculateAgreement(keyPair2.getPublic());
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
return median(samples);
|
||||
}
|
||||
|
||||
private static BasicAgreement createAgreement(boolean withCofactor) {
|
||||
if (withCofactor) return new ECDHCBasicAgreement();
|
||||
else return new ECDHBasicAgreement();
|
||||
}
|
||||
|
||||
private static void runCurve25519Test() {
|
||||
Curve25519 curve25519 = Curve25519.getInstance("java");
|
||||
Curve25519KeyPair keyPair1 = curve25519.generateKeyPair();
|
||||
Curve25519KeyPair keyPair2 = curve25519.generateKeyPair();
|
||||
// Time some key agreements
|
||||
List<Long> samples = new ArrayList<>();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
long start = System.nanoTime();
|
||||
curve25519.calculateAgreement(keyPair1.getPublicKey(),
|
||||
keyPair2.getPrivateKey());
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
long agreementMedian = median(samples);
|
||||
System.out.println(String.format("Curve25519: %,d - - -",
|
||||
agreementMedian));
|
||||
}
|
||||
|
||||
private static void runEd25519Test() throws GeneralSecurityException {
|
||||
KeyPair keyPair = new KeyPairGenerator().generateKeyPair();
|
||||
// Time some signatures
|
||||
List<Long> samples = new ArrayList<>();
|
||||
List<byte[]> signatures = new ArrayList<>();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
Signature signature =
|
||||
Signature.getInstance(SIGNATURE_ALGORITHM, ED_PROVIDER);
|
||||
long start = System.nanoTime();
|
||||
signature.initSign(keyPair.getPrivate(), random);
|
||||
signature.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||
signatures.add(signature.sign());
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
long signatureMedian = median(samples);
|
||||
// Time some signature verifications
|
||||
samples.clear();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
Signature signature =
|
||||
Signature.getInstance(SIGNATURE_ALGORITHM, ED_PROVIDER);
|
||||
long start = System.nanoTime();
|
||||
signature.initVerify(keyPair.getPublic());
|
||||
signature.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||
if (!signature.verify(signatures.get(i)))
|
||||
throw new AssertionError();
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
long verificationMedian = median(samples);
|
||||
System.out.println(String.format("Ed25519: - - %,d %,d",
|
||||
signatureMedian, verificationMedian));
|
||||
System.out.println(name + ": "
|
||||
+ agreementMedian + " "
|
||||
+ signatureMedian + " "
|
||||
+ verificationMedian);
|
||||
}
|
||||
|
||||
private static long median(List<Long> list) {
|
||||
|
||||
@@ -3,32 +3,40 @@ package org.briarproject.bramble.crypto;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
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.junit.Assert.assertArrayEquals;
|
||||
|
||||
public class KeyAgreementTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testDeriveSharedSecret() throws Exception {
|
||||
CryptoComponent crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
public void testDeriveMasterSecret() throws Exception {
|
||||
SecureRandomProvider
|
||||
secureRandomProvider = new TestSecureRandomProvider();
|
||||
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||
byte[] aPub = aPair.getPublic().getEncoded();
|
||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||
Random random = new Random();
|
||||
byte[][] inputs = new byte[random.nextInt(10) + 1][];
|
||||
for (int i = 0; i < inputs.length; i++)
|
||||
inputs[i] = getRandomBytes(random.nextInt(256));
|
||||
SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||
bPair.getPublic(), aPair, inputs);
|
||||
SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||
aPair.getPublic(), bPair, inputs);
|
||||
byte[] bPub = bPair.getPublic().getEncoded();
|
||||
SecretKey aMaster = crypto.deriveMasterSecret(aPub, bPair, true);
|
||||
SecretKey bMaster = crypto.deriveMasterSecret(bPub, aPair, false);
|
||||
assertArrayEquals(aMaster.getBytes(), bMaster.getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeriveSharedSecret() throws Exception {
|
||||
SecureRandomProvider
|
||||
secureRandomProvider = new TestSecureRandomProvider();
|
||||
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||
byte[] aPub = aPair.getPublic().getEncoded();
|
||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||
byte[] bPub = bPair.getPublic().getEncoded();
|
||||
SecretKey aShared = crypto.deriveSharedSecret(bPub, aPair, true);
|
||||
SecretKey bShared = crypto.deriveSharedSecret(aPub, bPair, false);
|
||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package org.briarproject.bramble.crypto;
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -16,34 +16,35 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class KeyDerivationTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponent crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
private final TransportCrypto transportCrypto =
|
||||
new TransportCryptoImpl(crypto);
|
||||
private final TransportId transportId = new TransportId("id");
|
||||
private final SecretKey master = getSecretKey();
|
||||
private final CryptoComponent crypto;
|
||||
private final SecretKey master;
|
||||
|
||||
public KeyDerivationTest() {
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
master = TestUtils.getSecretKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeysAreDistinct() {
|
||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
assertAllDifferent(k);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrentKeysMatchCurrentKeysOfContact() {
|
||||
// Start in rotation period 123
|
||||
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, false);
|
||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
||||
123, false);
|
||||
// Alice's incoming keys should equal Bob's outgoing keys
|
||||
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||
@@ -55,8 +56,8 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
|
||||
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
|
||||
// Rotate into the future
|
||||
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||
kB = transportCrypto.rotateTransportKeys(kB, 456);
|
||||
kA = crypto.rotateTransportKeys(kA, 456);
|
||||
kB = crypto.rotateTransportKeys(kB, 456);
|
||||
// Alice's incoming keys should equal Bob's outgoing keys
|
||||
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||
@@ -72,23 +73,22 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testPreviousKeysMatchPreviousKeysOfContact() {
|
||||
// Start in rotation period 123
|
||||
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, false);
|
||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
||||
123, false);
|
||||
// Compare Alice's previous keys in period 456 with Bob's current keys
|
||||
// in period 455
|
||||
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||
kB = transportCrypto.rotateTransportKeys(kB, 455);
|
||||
kA = crypto.rotateTransportKeys(kA, 456);
|
||||
kB = crypto.rotateTransportKeys(kB, 455);
|
||||
// Alice's previous incoming keys should equal Bob's outgoing keys
|
||||
assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(),
|
||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||
assertArrayEquals(
|
||||
kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
|
||||
assertArrayEquals(kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
|
||||
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
|
||||
// Compare Alice's current keys in period 456 with Bob's previous keys
|
||||
// in period 457
|
||||
kB = transportCrypto.rotateTransportKeys(kB, 457);
|
||||
kB = crypto.rotateTransportKeys(kB, 457);
|
||||
// Alice's outgoing keys should equal Bob's previous incoming keys
|
||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
||||
kB.getPreviousIncomingKeys().getTagKey().getBytes());
|
||||
@@ -99,14 +99,14 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testNextKeysMatchNextKeysOfContact() {
|
||||
// Start in rotation period 123
|
||||
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, false);
|
||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
||||
123, false);
|
||||
// Compare Alice's current keys in period 456 with Bob's next keys in
|
||||
// period 455
|
||||
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||
kB = transportCrypto.rotateTransportKeys(kB, 455);
|
||||
kA = crypto.rotateTransportKeys(kA, 456);
|
||||
kB = crypto.rotateTransportKeys(kB, 455);
|
||||
// Alice's outgoing keys should equal Bob's next incoming keys
|
||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
||||
kB.getNextIncomingKeys().getTagKey().getBytes());
|
||||
@@ -114,7 +114,7 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
kB.getNextIncomingKeys().getHeaderKey().getBytes());
|
||||
// Compare Alice's next keys in period 456 with Bob's current keys
|
||||
// in period 457
|
||||
kB = transportCrypto.rotateTransportKeys(kB, 457);
|
||||
kB = crypto.rotateTransportKeys(kB, 457);
|
||||
// Alice's next incoming keys should equal Bob's outgoing keys
|
||||
assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(),
|
||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||
@@ -124,12 +124,12 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testMasterKeyAffectsOutput() {
|
||||
SecretKey master1 = getSecretKey();
|
||||
SecretKey master1 = TestUtils.getSecretKey();
|
||||
assertFalse(Arrays.equals(master.getBytes(), master1.getBytes()));
|
||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
|
||||
master1, 123, true);
|
||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
TransportKeys k1 = crypto.deriveTransportKeys(transportId, master1,
|
||||
123, true);
|
||||
assertAllDifferent(k, k1);
|
||||
}
|
||||
|
||||
@@ -137,10 +137,10 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
public void testTransportIdAffectsOutput() {
|
||||
TransportId transportId1 = new TransportId("id1");
|
||||
assertFalse(transportId.getString().equals(transportId1.getString()));
|
||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
|
||||
master, 123, true);
|
||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
TransportKeys k1 = crypto.deriveTransportKeys(transportId1, master,
|
||||
123, true);
|
||||
assertAllDifferent(k, k1);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,49 +4,42 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class MacTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponent crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
private final SecretKey key1 = getSecretKey(), key2 = getSecretKey();
|
||||
private final String label1 = getRandomString(123);
|
||||
private final String label2 = getRandomString(123);
|
||||
private final byte[] input1 = getRandomBytes(123);
|
||||
private final byte[] input2 = getRandomBytes(234);
|
||||
private final byte[] input3 = new byte[0];
|
||||
private final SecretKey k = TestUtils.getSecretKey();
|
||||
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
||||
private final byte[] inputBytes1 = TestUtils.getRandomBytes(234);
|
||||
private final byte[] inputBytes2 = new byte[0];
|
||||
|
||||
public MacTest() {
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdenticalKeysAndInputsProduceIdenticalMacs() {
|
||||
// Calculate the MAC twice - the results should be identical
|
||||
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||
byte[] mac1 = crypto.mac(label1, key1, input1, input2, input3);
|
||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
||||
byte[] mac1 = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
||||
assertArrayEquals(mac, mac1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentLabelsProduceDifferentMacs() {
|
||||
// Calculate the MAC with each label - the results should be different
|
||||
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||
byte[] mac1 = crypto.mac(label2, key1, input1, input2, input3);
|
||||
assertFalse(Arrays.equals(mac, mac1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentKeysProduceDifferentMacs() {
|
||||
// Generate second random key
|
||||
SecretKey k1 = TestUtils.getSecretKey();
|
||||
// Calculate the MAC with each key - the results should be different
|
||||
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||
byte[] mac1 = crypto.mac(label1, key2, input1, input2, input3);
|
||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
||||
byte[] mac1 = crypto.mac(k1, inputBytes, inputBytes1, inputBytes2);
|
||||
assertFalse(Arrays.equals(mac, mac1));
|
||||
}
|
||||
|
||||
@@ -54,8 +47,8 @@ public class MacTest extends BrambleTestCase {
|
||||
public void testDifferentInputsProduceDifferentMacs() {
|
||||
// Calculate the MAC with the inputs in different orders - the results
|
||||
// should be different
|
||||
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||
byte[] mac1 = crypto.mac(label1, key1, input3, input2, input1);
|
||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
||||
byte[] mac1 = crypto.mac(k, inputBytes2, inputBytes1, inputBytes);
|
||||
assertFalse(Arrays.equals(mac, mac1));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ package org.briarproject.bramble.crypto;
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
@@ -13,25 +14,25 @@ import java.util.Set;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
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.test.TestUtils.getSecretKey;
|
||||
|
||||
public class TagEncodingTest extends BrambleMockTestCase {
|
||||
public class TagEncodingTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
|
||||
private final TransportCrypto transportCrypto =
|
||||
new TransportCryptoImpl(crypto);
|
||||
private final SecretKey tagKey = getSecretKey();
|
||||
private final CryptoComponent crypto;
|
||||
private final SecretKey tagKey;
|
||||
private final long streamNumber = 1234567890;
|
||||
|
||||
public TagEncodingTest() {
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
tagKey = TestUtils.getSecretKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyAffectsTag() throws Exception {
|
||||
Set<Bytes> set = new HashSet<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
SecretKey tagKey = getSecretKey();
|
||||
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
SecretKey tagKey = TestUtils.getSecretKey();
|
||||
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber);
|
||||
assertTrue(set.add(new Bytes(tag)));
|
||||
}
|
||||
}
|
||||
@@ -41,8 +42,7 @@ public class TagEncodingTest extends BrambleMockTestCase {
|
||||
Set<Bytes> set = new HashSet<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i,
|
||||
streamNumber);
|
||||
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber);
|
||||
assertTrue(set.add(new Bytes(tag)));
|
||||
}
|
||||
}
|
||||
@@ -52,8 +52,7 @@ public class TagEncodingTest extends BrambleMockTestCase {
|
||||
Set<Bytes> set = new HashSet<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION,
|
||||
streamNumber + i);
|
||||
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i);
|
||||
assertTrue(set.add(new Bytes(tag)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,380 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.sql.Types.BINARY;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public abstract class BasicDatabaseTest extends BrambleTestCase {
|
||||
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File db = new File(testDir, "db");
|
||||
|
||||
protected abstract String getBinaryType();
|
||||
|
||||
protected abstract String getDriverName();
|
||||
|
||||
protected abstract Connection openConnection(File db, boolean encrypt)
|
||||
throws SQLException;
|
||||
|
||||
protected abstract void shutdownDatabase(File db, boolean encrypt)
|
||||
throws SQLException;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testDir.mkdirs();
|
||||
Class.forName(getDriverName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertUpdateAndDelete() throws Exception {
|
||||
Connection connection = openConnection(db, false);
|
||||
try {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Generate an ID and two names
|
||||
byte[] id = TestUtils.getRandomId();
|
||||
String oldName = StringUtils.getRandomString(50);
|
||||
String newName = StringUtils.getRandomString(50);
|
||||
// Insert the ID and old name into the table
|
||||
insertRow(connection, id, oldName);
|
||||
// Check that the old name can be retrieved using the ID
|
||||
assertTrue(rowExists(connection, id));
|
||||
assertEquals(oldName, getName(connection, id));
|
||||
// Update the name
|
||||
updateRow(connection, id, newName);
|
||||
// Check that the new name can be retrieved using the ID
|
||||
assertTrue(rowExists(connection, id));
|
||||
assertEquals(newName, getName(connection, id));
|
||||
// Delete the row from the table
|
||||
assertTrue(deleteRow(connection, id));
|
||||
// Check that the row no longer exists
|
||||
assertFalse(rowExists(connection, id));
|
||||
// Deleting the row again should have no effect
|
||||
assertFalse(deleteRow(connection, id));
|
||||
} finally {
|
||||
connection.close();
|
||||
shutdownDatabase(db, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchInsertUpdateAndDelete() throws Exception {
|
||||
Connection connection = openConnection(db, false);
|
||||
try {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Generate some IDs and two sets of names
|
||||
byte[][] ids = new byte[BATCH_SIZE][];
|
||||
String[] oldNames = new String[BATCH_SIZE];
|
||||
String[] newNames = new String[BATCH_SIZE];
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
ids[i] = TestUtils.getRandomId();
|
||||
oldNames[i] = StringUtils.getRandomString(50);
|
||||
newNames[i] = StringUtils.getRandomString(50);
|
||||
}
|
||||
// Insert the IDs and old names into the table as a batch
|
||||
insertBatch(connection, ids, oldNames);
|
||||
// Update the names as a batch
|
||||
updateBatch(connection, ids, newNames);
|
||||
// Check that the new names can be retrieved using the IDs
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
assertTrue(rowExists(connection, ids[i]));
|
||||
assertEquals(newNames[i], getName(connection, ids[i]));
|
||||
}
|
||||
// Delete the rows as a batch
|
||||
boolean[] deleted = deleteBatch(connection, ids);
|
||||
// Check that the rows no longer exist
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
assertTrue(deleted[i]);
|
||||
assertFalse(rowExists(connection, ids[i]));
|
||||
}
|
||||
// Deleting the rows again should have no effect
|
||||
deleted = deleteBatch(connection, ids);
|
||||
for (int i = 0; i < BATCH_SIZE; i++) assertFalse(deleted[i]);
|
||||
} finally {
|
||||
connection.close();
|
||||
shutdownDatabase(db, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortOrder() throws Exception {
|
||||
byte[] first = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
byte[] second = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 127
|
||||
};
|
||||
byte[] third = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, (byte) 255
|
||||
};
|
||||
Connection connection = openConnection(db, false);
|
||||
try {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Insert the rows
|
||||
insertRow(connection, first, "first");
|
||||
insertRow(connection, second, "second");
|
||||
insertRow(connection, third, "third");
|
||||
insertRow(connection, null, "null");
|
||||
// Check the ordering of the < operator: null is not comparable
|
||||
assertNull(getPredecessor(connection, first));
|
||||
assertEquals("first", getPredecessor(connection, second));
|
||||
assertEquals("second", getPredecessor(connection, third));
|
||||
assertNull(getPredecessor(connection, null));
|
||||
// Check the ordering of ORDER BY: nulls come first
|
||||
List<String> names = getNames(connection);
|
||||
assertEquals(4, names.size());
|
||||
assertEquals("null", names.get(0));
|
||||
assertEquals("first", names.get(1));
|
||||
assertEquals("second", names.get(2));
|
||||
assertEquals("third", names.get(3));
|
||||
} finally {
|
||||
connection.close();
|
||||
shutdownDatabase(db, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataIsFoundWithoutEncryption() throws Exception {
|
||||
testEncryption(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataIsNotFoundWithEncryption() throws Exception {
|
||||
testEncryption(true);
|
||||
}
|
||||
|
||||
private void testEncryption(boolean encrypt) throws Exception {
|
||||
byte[] sequence = new byte[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
|
||||
Connection connection = openConnection(db, encrypt);
|
||||
try {
|
||||
createTable(connection);
|
||||
insertRow(connection, sequence, "abcdefg");
|
||||
} finally {
|
||||
connection.close();
|
||||
shutdownDatabase(db, encrypt);
|
||||
}
|
||||
try {
|
||||
if (findSequence(testDir, sequence) == encrypt) fail();
|
||||
} finally {
|
||||
shutdownDatabase(db, encrypt);
|
||||
}
|
||||
}
|
||||
|
||||
private void createTable(Connection connection) throws SQLException {
|
||||
Statement s = connection.createStatement();
|
||||
String sql = "CREATE TABLE foo (uniqueId " + getBinaryType() + ","
|
||||
+ " name VARCHAR(100) NOT NULL)";
|
||||
s.executeUpdate(sql);
|
||||
s.close();
|
||||
}
|
||||
|
||||
private void insertRow(Connection connection, byte[] id, String name)
|
||||
throws SQLException {
|
||||
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
ps.setString(2, name);
|
||||
int affected = ps.executeUpdate();
|
||||
assertEquals(1, affected);
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private boolean rowExists(Connection connection, byte[] id)
|
||||
throws SQLException {
|
||||
assertNotNull(id);
|
||||
String sql = "SELECT * FROM foo WHERE uniqueID = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
boolean found = rs.next();
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return found;
|
||||
}
|
||||
|
||||
private String getName(Connection connection, byte[] id)
|
||||
throws SQLException {
|
||||
assertNotNull(id);
|
||||
String sql = "SELECT name FROM foo WHERE uniqueID = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
assertTrue(rs.next());
|
||||
String name = rs.getString(1);
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return name;
|
||||
}
|
||||
|
||||
private void updateRow(Connection connection, byte[] id, String name)
|
||||
throws SQLException {
|
||||
String sql = "UPDATE foo SET name = ? WHERE uniqueId = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(2, BINARY);
|
||||
else ps.setBytes(2, id);
|
||||
ps.setString(1, name);
|
||||
assertEquals(1, ps.executeUpdate());
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private boolean deleteRow(Connection connection, byte[] id)
|
||||
throws SQLException {
|
||||
String sql = "DELETE FROM foo WHERE uniqueId = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
int affected = ps.executeUpdate();
|
||||
ps.close();
|
||||
return affected == 1;
|
||||
}
|
||||
|
||||
private void insertBatch(Connection connection, byte[][] ids,
|
||||
String[] names) throws SQLException {
|
||||
assertEquals(ids.length, names.length);
|
||||
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
if (ids[i] == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, ids[i]);
|
||||
ps.setString(2, names[i]);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
for (int affected : batchAffected) assertEquals(1, affected);
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private void updateBatch(Connection connection, byte[][] ids,
|
||||
String[] names) throws SQLException {
|
||||
assertEquals(ids.length, names.length);
|
||||
String sql = "UPDATE foo SET name = ? WHERE uniqueId = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
if (ids[i] == null) ps.setNull(2, BINARY);
|
||||
else ps.setBytes(2, ids[i]);
|
||||
ps.setString(1, names[i]);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
for (int affected : batchAffected) assertEquals(1, affected);
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private boolean[] deleteBatch(Connection connection, byte[][] ids)
|
||||
throws SQLException {
|
||||
String sql = "DELETE FROM foo WHERE uniqueId = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (byte[] id : ids) {
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
boolean[] ret = new boolean[ids.length];
|
||||
for (int i = 0; i < batchAffected.length; i++)
|
||||
ret[i] = batchAffected[i] == 1;
|
||||
ps.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
private String getPredecessor(Connection connection, byte[] id)
|
||||
throws SQLException {
|
||||
String sql = "SELECT name FROM foo WHERE uniqueId < ?"
|
||||
+ " ORDER BY uniqueId DESC";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ps.setMaxRows(1);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
String name = rs.next() ? rs.getString(1) : null;
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return name;
|
||||
}
|
||||
|
||||
private List<String> getNames(Connection connection) throws SQLException {
|
||||
String sql = "SELECT name FROM foo ORDER BY uniqueId NULLS FIRST";
|
||||
List<String> names = new ArrayList<>();
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) names.add(rs.getString(1));
|
||||
rs.close();
|
||||
ps.close();
|
||||
return names;
|
||||
}
|
||||
|
||||
private boolean findSequence(File f, byte[] sequence) throws IOException {
|
||||
if (f.isDirectory()) {
|
||||
File[] children = f.listFiles();
|
||||
if (children != null)
|
||||
for (File child : children)
|
||||
if (findSequence(child, sequence)) return true;
|
||||
return false;
|
||||
} else if (f.isFile()) {
|
||||
FileInputStream in = new FileInputStream(f);
|
||||
try {
|
||||
int offset = 0;
|
||||
while (true) {
|
||||
int read = in.read();
|
||||
if (read == -1) return false;
|
||||
if (((byte) read) == sequence[offset]) {
|
||||
offset++;
|
||||
if (offset == sequence.length) return true;
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,345 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BasicH2Test extends BasicDatabaseTest {
|
||||
import static java.sql.Types.BINARY;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
private final SecretKey key = TestUtils.getSecretKey();
|
||||
public class BasicH2Test extends BrambleTestCase {
|
||||
|
||||
@Override
|
||||
protected String getBinaryType() {
|
||||
return "BINARY(32)";
|
||||
private static final String CREATE_TABLE =
|
||||
"CREATE TABLE foo (uniqueId BINARY(32), name VARCHAR NOT NULL)";
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File db = new File(testDir, "db");
|
||||
private final String url = "jdbc:h2:" + db.getAbsolutePath();
|
||||
|
||||
private Connection connection = null;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testDir.mkdirs();
|
||||
Class.forName("org.h2.Driver");
|
||||
connection = DriverManager.getConnection(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDriverName() {
|
||||
return "org.h2.Driver";
|
||||
@Test
|
||||
public void testInsertUpdateAndDelete() throws Exception {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Generate an ID and two names
|
||||
byte[] id = TestUtils.getRandomId();
|
||||
String oldName = StringUtils.getRandomString(50);
|
||||
String newName = StringUtils.getRandomString(50);
|
||||
// Insert the ID and old name into the table
|
||||
insertRow(id, oldName);
|
||||
// Check that the old name can be retrieved using the ID
|
||||
assertTrue(rowExists(id));
|
||||
assertEquals(oldName, getName(id));
|
||||
// Update the name
|
||||
updateRow(id, newName);
|
||||
// Check that the new name can be retrieved using the ID
|
||||
assertTrue(rowExists(id));
|
||||
assertEquals(newName, getName(id));
|
||||
// Delete the row from the table
|
||||
assertTrue(deleteRow(id));
|
||||
// Check that the row no longer exists
|
||||
assertFalse(rowExists(id));
|
||||
// Deleting the row again should have no effect
|
||||
assertFalse(deleteRow(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection openConnection(File db, boolean encrypt)
|
||||
throws SQLException {
|
||||
String url = "jdbc:h2:split:" + db.getAbsolutePath();
|
||||
Properties props = new Properties();
|
||||
props.setProperty("user", "user");
|
||||
if (encrypt) {
|
||||
url += ";CIPHER=AES";
|
||||
String hex = StringUtils.toHexString(key.getBytes());
|
||||
props.setProperty("password", hex + " password");
|
||||
@Test
|
||||
public void testBatchInsertUpdateAndDelete() throws Exception {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Generate some IDs and two sets of names
|
||||
byte[][] ids = new byte[BATCH_SIZE][];
|
||||
String[] oldNames = new String[BATCH_SIZE];
|
||||
String[] newNames = new String[BATCH_SIZE];
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
ids[i] = TestUtils.getRandomId();
|
||||
oldNames[i] = StringUtils.getRandomString(50);
|
||||
newNames[i] = StringUtils.getRandomString(50);
|
||||
}
|
||||
return DriverManager.getConnection(url, props);
|
||||
// Insert the IDs and old names into the table as a batch
|
||||
insertBatch(ids, oldNames);
|
||||
// Update the names as a batch
|
||||
updateBatch(ids, newNames);
|
||||
// Check that the new names can be retrieved using the IDs
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
assertTrue(rowExists(ids[i]));
|
||||
assertEquals(newNames[i], getName(ids[i]));
|
||||
}
|
||||
// Delete the rows as a batch
|
||||
boolean[] deleted = deleteBatch(ids);
|
||||
// Check that the rows no longer exist
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
assertTrue(deleted[i]);
|
||||
assertFalse(rowExists(ids[i]));
|
||||
}
|
||||
// Deleting the rows again should have no effect
|
||||
deleted = deleteBatch(ids);
|
||||
for (int i = 0; i < BATCH_SIZE; i++) assertFalse(deleted[i]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdownDatabase(File db, boolean encrypt)
|
||||
throws SQLException {
|
||||
// The DB is closed automatically when the connection is closed
|
||||
@Test
|
||||
public void testSortOrder() throws Exception {
|
||||
byte[] first = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
byte[] second = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 127
|
||||
};
|
||||
byte[] third = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, (byte) 255
|
||||
};
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Insert the rows
|
||||
insertRow(first, "first");
|
||||
insertRow(second, "second");
|
||||
insertRow(third, "third");
|
||||
insertRow(null, "null");
|
||||
// Check the ordering of the < operator: the null ID is not comparable
|
||||
assertNull(getPredecessor(first));
|
||||
assertEquals("first", getPredecessor(second));
|
||||
assertEquals("second", getPredecessor(third));
|
||||
assertNull(getPredecessor(null));
|
||||
// Check the ordering of ORDER BY: nulls come first
|
||||
List<String> names = getNames();
|
||||
assertEquals(4, names.size());
|
||||
assertEquals("null", names.get(0));
|
||||
assertEquals("first", names.get(1));
|
||||
assertEquals("second", names.get(2));
|
||||
assertEquals("third", names.get(3));
|
||||
}
|
||||
|
||||
private void createTable(Connection connection) throws SQLException {
|
||||
try {
|
||||
Statement s = connection.createStatement();
|
||||
s.executeUpdate(CREATE_TABLE);
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void insertRow(byte[] id, String name) throws SQLException {
|
||||
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
ps.setString(2, name);
|
||||
int affected = ps.executeUpdate();
|
||||
assertEquals(1, affected);
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean rowExists(byte[] id) throws SQLException {
|
||||
assertNotNull(id);
|
||||
String sql = "SELECT NULL FROM foo WHERE uniqueID = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
boolean found = rs.next();
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return found;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private String getName(byte[] id) throws SQLException {
|
||||
assertNotNull(id);
|
||||
String sql = "SELECT name FROM foo WHERE uniqueID = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
assertTrue(rs.next());
|
||||
String name = rs.getString(1);
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return name;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRow(byte[] id, String name) throws SQLException {
|
||||
String sql = "UPDATE foo SET name = ? WHERE uniqueId = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(2, BINARY);
|
||||
else ps.setBytes(2, id);
|
||||
ps.setString(1, name);
|
||||
assertEquals(1, ps.executeUpdate());
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deleteRow(byte[] id) throws SQLException {
|
||||
String sql = "DELETE FROM foo WHERE uniqueId = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
int affected = ps.executeUpdate();
|
||||
ps.close();
|
||||
return affected == 1;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void insertBatch(byte[][] ids, String[] names) throws SQLException {
|
||||
assertEquals(ids.length, names.length);
|
||||
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
if (ids[i] == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, ids[i]);
|
||||
ps.setString(2, names[i]);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
for (int affected : batchAffected) assertEquals(1, affected);
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBatch(byte[][] ids, String[] names) throws SQLException {
|
||||
assertEquals(ids.length, names.length);
|
||||
String sql = "UPDATE foo SET name = ? WHERE uniqueId = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
if (ids[i] == null) ps.setNull(2, BINARY);
|
||||
else ps.setBytes(2, ids[i]);
|
||||
ps.setString(1, names[i]);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
for (int affected : batchAffected) assertEquals(1, affected);
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean[] deleteBatch(byte[][] ids) throws SQLException {
|
||||
String sql = "DELETE FROM foo WHERE uniqueId = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (byte[] id : ids) {
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
boolean[] ret = new boolean[ids.length];
|
||||
for (int i = 0; i < batchAffected.length; i++)
|
||||
ret[i] = batchAffected[i] == 1;
|
||||
ps.close();
|
||||
return ret;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private String getPredecessor(byte[] id) throws SQLException {
|
||||
String sql = "SELECT name FROM foo WHERE uniqueId < ?"
|
||||
+ " ORDER BY uniqueId DESC LIMIT ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ps.setInt(2, 1);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
String name = rs.next() ? rs.getString(1) : null;
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return name;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getNames() throws SQLException {
|
||||
String sql = "SELECT name FROM foo ORDER BY uniqueId";
|
||||
List<String> names = new ArrayList<>();
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) names.add(rs.getString(1));
|
||||
rs.close();
|
||||
ps.close();
|
||||
return names;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
if (connection != null) connection.close();
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class BasicHyperSqlTest extends BasicDatabaseTest {
|
||||
|
||||
private final SecretKey key = TestUtils.getSecretKey();
|
||||
|
||||
@Override
|
||||
protected String getBinaryType() {
|
||||
return "BINARY(32)";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDriverName() {
|
||||
return "org.hsqldb.jdbc.JDBCDriver";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection openConnection(File db, boolean encrypt)
|
||||
throws SQLException {
|
||||
String url = "jdbc:hsqldb:file:" + db.getAbsolutePath() +
|
||||
";sql.enforce_size=false;allow_empty_batch=true";
|
||||
if (encrypt) {
|
||||
String hex = StringUtils.toHexString(key.getBytes());
|
||||
url += ";encrypt_lobs=true;crypt_type=AES;crypt_key=" + hex;
|
||||
}
|
||||
return DriverManager.getConnection(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdownDatabase(File db, boolean encrypt)
|
||||
throws SQLException {
|
||||
Connection c = openConnection(db, encrypt);
|
||||
Statement s = c.createStatement();
|
||||
s.executeQuery("SHUTDOWN");
|
||||
s.close();
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
@@ -47,10 +47,11 @@ import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -73,13 +74,7 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final Database<Object> database = context.mock(Database.class);
|
||||
private final ShutdownManager shutdown =
|
||||
context.mock(ShutdownManager.class);
|
||||
private final EventBus eventBus = context.mock(EventBus.class);
|
||||
public class DatabaseComponentImplTest extends BrambleTestCase {
|
||||
|
||||
private final Object txn = new Object();
|
||||
private final ClientId clientId;
|
||||
@@ -130,8 +125,13 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testSimpleCalls() throws Exception {
|
||||
int shutdownHandle = 12345;
|
||||
Mockery context = new Mockery();
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// open()
|
||||
oneOf(database).open();
|
||||
@@ -194,6 +194,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
// endTransaction()
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// close()
|
||||
oneOf(shutdown).removeShutdownHook(shutdownHandle);
|
||||
oneOf(database).close();
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
@@ -220,11 +221,18 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
db.close();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalMessagesAreNotStoredUnlessGroupExists()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -244,10 +252,17 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLocalMessage() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -279,11 +294,18 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVariousMethodsThrowExceptionIfContactIsMissing()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Check whether the contact is in the DB (which it's not)
|
||||
exactly(18).of(database).startTransaction();
|
||||
@@ -478,11 +500,18 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVariousMethodsThrowExceptionIfLocalAuthorIsMissing()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Check whether the pseudonym is in the DB (which it's not)
|
||||
exactly(3).of(database).startTransaction();
|
||||
@@ -523,11 +552,18 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVariousMethodsThrowExceptionIfGroupIsMissing()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Check whether the group is in the DB (which it's not)
|
||||
exactly(8).of(database).startTransaction();
|
||||
@@ -621,11 +657,18 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVariousMethodsThrowExceptionIfMessageIsMissing()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Check whether the message is in the DB (which it's not)
|
||||
exactly(11).of(database).startTransaction();
|
||||
@@ -749,11 +792,18 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVariousMethodsThrowExceptionIfTransportIsMissing()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// startTransaction()
|
||||
oneOf(database).startTransaction();
|
||||
@@ -840,12 +890,19 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateAck() throws Exception {
|
||||
Collection<MessageId> messagesToAck = Arrays.asList(messageId,
|
||||
messageId1);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -868,6 +925,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -875,6 +934,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
byte[] raw1 = new byte[size];
|
||||
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
||||
Collection<byte[]> messages = Arrays.asList(raw, raw1);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -905,12 +969,19 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateOffer() throws Exception {
|
||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -936,12 +1007,19 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateRequest() throws Exception {
|
||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -964,6 +1042,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -971,6 +1051,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
byte[] raw1 = new byte[size];
|
||||
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
||||
Collection<byte[]> messages = Arrays.asList(raw, raw1);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1002,10 +1087,17 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveAck() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1028,10 +1120,17 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveMessage() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1076,10 +1175,17 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveDuplicateMessage() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1106,10 +1212,17 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveMessageWithoutVisibleGroup() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1129,6 +1242,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1136,6 +1251,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
|
||||
MessageId messageId3 = new MessageId(TestUtils.getRandomId());
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1176,10 +1296,17 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveRequest() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1203,10 +1330,17 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangingVisibilityCallsListeners() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1236,11 +1370,18 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotChangingVisibilityDoesNotCallListeners()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1262,6 +1403,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1269,6 +1412,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
TransportKeys transportKeys = createTransportKeys();
|
||||
Map<ContactId, TransportKeys> keys = Collections.singletonMap(
|
||||
contactId, transportKeys);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// startTransaction()
|
||||
oneOf(database).startTransaction();
|
||||
@@ -1298,6 +1446,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
private TransportKeys createTransportKeys() {
|
||||
@@ -1330,6 +1480,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
Settings merged = new Settings();
|
||||
merged.put("foo", "bar");
|
||||
merged.put("baz", "qux");
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// startTransaction()
|
||||
oneOf(database).startTransaction();
|
||||
@@ -1359,6 +1514,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
@@ -1388,6 +1545,12 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
private void testCannotStartTransactionDuringTransaction(
|
||||
boolean firstTxnReadOnly, boolean secondTxnReadOnly)
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1397,12 +1560,22 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
shutdown);
|
||||
|
||||
assertNotNull(db.startTransaction(firstTxnReadOnly));
|
||||
db.startTransaction(secondTxnReadOnly);
|
||||
fail();
|
||||
try {
|
||||
db.startTransaction(secondTxnReadOnly);
|
||||
fail();
|
||||
} finally {
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotAddLocalIdentityAsContact() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1426,10 +1599,18 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotAddDuplicateContact() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1455,12 +1636,18 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMessageDependencies() throws Exception {
|
||||
int shutdownHandle = 12345;
|
||||
Mockery context = new Mockery();
|
||||
Database<Object> database = context.mock(Database.class);
|
||||
ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
EventBus eventBus = context.mock(EventBus.class);
|
||||
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
|
||||
context.checking(new Expectations() {{
|
||||
// open()
|
||||
@@ -1506,6 +1693,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
// endTransaction()
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// close()
|
||||
oneOf(shutdown).removeShutdownHook(shutdownHandle);
|
||||
oneOf(database).close();
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
@@ -1526,5 +1714,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
db.close();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
public class HyperSqlDatabaseTest extends JdbcDatabaseTest {
|
||||
|
||||
public HyperSqlDatabaseTest() throws Exception {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdbcDatabase createDatabase(DatabaseConfig config, Clock clock) {
|
||||
return new HyperSqlDatabase(config, clock);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
public class TransactionIsolationTest extends BrambleTestCase {
|
||||
|
||||
private static final String DROP_TABLE = "DROP TABLE foo IF EXISTS";
|
||||
private static final String CREATE_TABLE = "CREATE TABLE foo"
|
||||
@@ -34,9 +34,9 @@ public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File db = new File(testDir, "db");
|
||||
private final String withMvcc = "jdbc:h2:split:" + db.getAbsolutePath()
|
||||
private final String withMvcc = "jdbc:h2:" + db.getAbsolutePath()
|
||||
+ ";MV_STORE=TRUE;MVCC=TRUE";
|
||||
private final String withoutMvcc = "jdbc:h2:split:" + db.getAbsolutePath()
|
||||
private final String withoutMvcc = "jdbc:h2:" + db.getAbsolutePath()
|
||||
+ ";MV_STORE=FALSE;MVCC=FALSE;LOCK_MODE=1";
|
||||
|
||||
@Before
|
||||
@@ -1,14 +1,13 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.auto.Mock;
|
||||
import org.jmock.integration.junit4.JUnitRuleMockery;
|
||||
@@ -17,11 +16,6 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
@@ -34,33 +28,34 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
setImposteriser(ClassImposteriser.INSTANCE);
|
||||
}};
|
||||
|
||||
private final PublicKey alicePubKey =
|
||||
context.mock(PublicKey.class, "alice");
|
||||
private final byte[] alicePubKeyBytes = getRandomBytes(32);
|
||||
private final byte[] aliceCommit = getRandomBytes(COMMIT_LENGTH);
|
||||
private final byte[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8);
|
||||
private final byte[] aliceConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||
private static final byte[] ALICE_PUBKEY = TestUtils.getRandomBytes(32);
|
||||
private static final byte[] ALICE_COMMIT =
|
||||
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
||||
private static final byte[] ALICE_PAYLOAD =
|
||||
TestUtils.getRandomBytes(COMMIT_LENGTH + 8);
|
||||
|
||||
private final PublicKey bobPubKey = context.mock(PublicKey.class, "bob");
|
||||
private final byte[] bobPubKeyBytes = getRandomBytes(32);
|
||||
private final byte[] bobCommit = getRandomBytes(COMMIT_LENGTH);
|
||||
private final byte[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19);
|
||||
private final byte[] bobConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||
private static final byte[] BOB_PUBKEY = TestUtils.getRandomBytes(32);
|
||||
private static final byte[] BOB_COMMIT =
|
||||
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
||||
private static final byte[] BOB_PAYLOAD =
|
||||
TestUtils.getRandomBytes(COMMIT_LENGTH + 19);
|
||||
|
||||
private final PublicKey badPubKey = context.mock(PublicKey.class, "bad");
|
||||
private final byte[] badPubKeyBytes = getRandomBytes(32);
|
||||
private final byte[] badCommit = getRandomBytes(COMMIT_LENGTH);
|
||||
private final byte[] badConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||
private static final byte[] ALICE_CONFIRM =
|
||||
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
||||
private static final byte[] BOB_CONFIRM =
|
||||
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
||||
|
||||
private static final byte[] BAD_PUBKEY = TestUtils.getRandomBytes(32);
|
||||
private static final byte[] BAD_COMMIT =
|
||||
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
||||
private static final byte[] BAD_CONFIRM =
|
||||
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
||||
|
||||
@Mock
|
||||
KeyAgreementProtocol.Callbacks callbacks;
|
||||
@Mock
|
||||
CryptoComponent crypto;
|
||||
@Mock
|
||||
KeyAgreementCrypto keyAgreementCrypto;
|
||||
@Mock
|
||||
KeyParser keyParser;
|
||||
@Mock
|
||||
PayloadEncoder payloadEncoder;
|
||||
@Mock
|
||||
KeyAgreementTransport transport;
|
||||
@@ -70,72 +65,60 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testAliceProtocol() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(bobCommit, null);
|
||||
Payload ourPayload = new Payload(aliceCommit, null);
|
||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
SecretKey sharedSecret = getSecretKey();
|
||||
SecretKey masterSecret = getSecretKey();
|
||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
||||
SecretKey masterSecret = TestUtils.getSecretKey();
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, true);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(payloadEncoder).encode(ourPayload);
|
||||
will(returnValue(alicePayload));
|
||||
will(returnValue(ALICE_PAYLOAD));
|
||||
allowing(payloadEncoder).encode(theirPayload);
|
||||
will(returnValue(bobPayload));
|
||||
will(returnValue(BOB_PAYLOAD));
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
allowing(alicePubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
allowing(bobPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
will(returnValue(ALICE_PUBKEY));
|
||||
|
||||
// Alice sends her public key
|
||||
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
||||
|
||||
// Alice receives Bob's public key
|
||||
oneOf(callbacks).connectionWaiting();
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
will(returnValue(BOB_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
|
||||
will(returnValue(bobPubKey));
|
||||
|
||||
// Alice verifies Bob's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(bobPubKey);
|
||||
will(returnValue(bobCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
|
||||
will(returnValue(BOB_COMMIT));
|
||||
|
||||
// Alice computes shared secret
|
||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||
alicePubKeyBytes, bobPubKeyBytes);
|
||||
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
|
||||
will(returnValue(sharedSecret));
|
||||
|
||||
// Alice sends her confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||
true, true);
|
||||
will(returnValue(aliceConfirm));
|
||||
oneOf(transport).sendConfirm(aliceConfirm);
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
|
||||
will(returnValue(ALICE_CONFIRM));
|
||||
oneOf(transport).sendConfirm(ALICE_CONFIRM);
|
||||
|
||||
// Alice receives Bob's confirmation record
|
||||
oneOf(transport).receiveConfirm();
|
||||
will(returnValue(bobConfirm));
|
||||
will(returnValue(BOB_CONFIRM));
|
||||
|
||||
// Alice verifies Bob's confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||
true, false);
|
||||
will(returnValue(bobConfirm));
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
|
||||
will(returnValue(BOB_CONFIRM));
|
||||
|
||||
// Alice computes master secret
|
||||
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||
oneOf(crypto).deriveMasterSecret(sharedSecret);
|
||||
will(returnValue(masterSecret));
|
||||
}});
|
||||
|
||||
@@ -146,71 +129,59 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testBobProtocol() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(aliceCommit, null);
|
||||
Payload ourPayload = new Payload(bobCommit, null);
|
||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
SecretKey sharedSecret = getSecretKey();
|
||||
SecretKey masterSecret = getSecretKey();
|
||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
||||
SecretKey masterSecret = TestUtils.getSecretKey();
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, false);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(payloadEncoder).encode(ourPayload);
|
||||
will(returnValue(bobPayload));
|
||||
will(returnValue(BOB_PAYLOAD));
|
||||
allowing(payloadEncoder).encode(theirPayload);
|
||||
will(returnValue(alicePayload));
|
||||
will(returnValue(ALICE_PAYLOAD));
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
allowing(alicePubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
allowing(bobPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
will(returnValue(BOB_PUBKEY));
|
||||
|
||||
// Bob receives Alice's public key
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
will(returnValue(ALICE_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
|
||||
will(returnValue(alicePubKey));
|
||||
|
||||
// Bob verifies Alice's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(alicePubKey);
|
||||
will(returnValue(aliceCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
|
||||
will(returnValue(ALICE_COMMIT));
|
||||
|
||||
// Bob sends his public key
|
||||
oneOf(transport).sendKey(bobPubKeyBytes);
|
||||
oneOf(transport).sendKey(BOB_PUBKEY);
|
||||
|
||||
// Bob computes shared secret
|
||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||
alicePubKeyBytes, bobPubKeyBytes);
|
||||
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
|
||||
will(returnValue(sharedSecret));
|
||||
|
||||
// Bob receives Alices's confirmation record
|
||||
oneOf(transport).receiveConfirm();
|
||||
will(returnValue(aliceConfirm));
|
||||
will(returnValue(ALICE_CONFIRM));
|
||||
|
||||
// Bob verifies Alice's confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||
false, true);
|
||||
will(returnValue(aliceConfirm));
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
|
||||
will(returnValue(ALICE_CONFIRM));
|
||||
|
||||
// Bob sends his confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||
false, false);
|
||||
will(returnValue(bobConfirm));
|
||||
oneOf(transport).sendConfirm(bobConfirm);
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
|
||||
will(returnValue(BOB_CONFIRM));
|
||||
oneOf(transport).sendConfirm(BOB_CONFIRM);
|
||||
|
||||
// Bob computes master secret
|
||||
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||
oneOf(crypto).deriveMasterSecret(sharedSecret);
|
||||
will(returnValue(masterSecret));
|
||||
}});
|
||||
|
||||
@@ -221,44 +192,38 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test(expected = AbortException.class)
|
||||
public void testAliceProtocolAbortOnBadKey() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(bobCommit, null);
|
||||
Payload ourPayload = new Payload(aliceCommit, null);
|
||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, true);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
will(returnValue(ALICE_PUBKEY));
|
||||
|
||||
// Alice sends her public key
|
||||
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
||||
|
||||
// Alice receives a bad public key
|
||||
oneOf(callbacks).connectionWaiting();
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(badPubKeyBytes));
|
||||
will(returnValue(BAD_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
|
||||
will(returnValue(badPubKey));
|
||||
|
||||
// Alice verifies Bob's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(badPubKey);
|
||||
will(returnValue(badCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
|
||||
will(returnValue(BAD_COMMIT));
|
||||
|
||||
// Alice aborts
|
||||
oneOf(transport).sendAbort(false);
|
||||
|
||||
// Alice never computes shared secret
|
||||
never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey,
|
||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||
alicePubKeyBytes, bobPubKeyBytes);
|
||||
never(crypto).deriveSharedSecret(BAD_PUBKEY, ourKeyPair, true);
|
||||
}});
|
||||
|
||||
// execute
|
||||
@@ -268,38 +233,34 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test(expected = AbortException.class)
|
||||
public void testBobProtocolAbortOnBadKey() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(aliceCommit, null);
|
||||
Payload ourPayload = new Payload(bobCommit, null);
|
||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, false);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
will(returnValue(BOB_PUBKEY));
|
||||
|
||||
// Bob receives a bad public key
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(badPubKeyBytes));
|
||||
will(returnValue(BAD_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
|
||||
will(returnValue(badPubKey));
|
||||
|
||||
// Bob verifies Alice's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(badPubKey);
|
||||
will(returnValue(badCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
|
||||
will(returnValue(BAD_COMMIT));
|
||||
|
||||
// Bob aborts
|
||||
oneOf(transport).sendAbort(false);
|
||||
|
||||
// Bob never sends his public key
|
||||
never(transport).sendKey(bobPubKeyBytes);
|
||||
never(transport).sendKey(BOB_PUBKEY);
|
||||
}});
|
||||
|
||||
// execute
|
||||
@@ -309,72 +270,62 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test(expected = AbortException.class)
|
||||
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(bobCommit, null);
|
||||
Payload ourPayload = new Payload(aliceCommit, null);
|
||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
SecretKey sharedSecret = getSecretKey();
|
||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, true);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(payloadEncoder).encode(ourPayload);
|
||||
will(returnValue(alicePayload));
|
||||
will(returnValue(ALICE_PAYLOAD));
|
||||
allowing(payloadEncoder).encode(theirPayload);
|
||||
will(returnValue(bobPayload));
|
||||
will(returnValue(BOB_PAYLOAD));
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
allowing(bobPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
will(returnValue(ALICE_PUBKEY));
|
||||
|
||||
// Alice sends her public key
|
||||
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
||||
|
||||
// Alice receives Bob's public key
|
||||
oneOf(callbacks).connectionWaiting();
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
will(returnValue(BOB_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
|
||||
will(returnValue(bobPubKey));
|
||||
|
||||
// Alice verifies Bob's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(bobPubKey);
|
||||
will(returnValue(bobCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
|
||||
will(returnValue(BOB_COMMIT));
|
||||
|
||||
// Alice computes shared secret
|
||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||
alicePubKeyBytes, bobPubKeyBytes);
|
||||
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
|
||||
will(returnValue(sharedSecret));
|
||||
|
||||
// Alice sends her confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||
true, true);
|
||||
will(returnValue(aliceConfirm));
|
||||
oneOf(transport).sendConfirm(aliceConfirm);
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
|
||||
will(returnValue(ALICE_CONFIRM));
|
||||
oneOf(transport).sendConfirm(ALICE_CONFIRM);
|
||||
|
||||
// Alice receives a bad confirmation record
|
||||
oneOf(transport).receiveConfirm();
|
||||
will(returnValue(badConfirm));
|
||||
will(returnValue(BAD_CONFIRM));
|
||||
|
||||
// Alice verifies Bob's confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||
true, false);
|
||||
will(returnValue(bobConfirm));
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
|
||||
will(returnValue(BOB_CONFIRM));
|
||||
|
||||
// Alice aborts
|
||||
oneOf(transport).sendAbort(false);
|
||||
|
||||
// Alice never computes master secret
|
||||
never(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||
never(crypto).deriveMasterSecret(sharedSecret);
|
||||
}});
|
||||
|
||||
// execute
|
||||
@@ -384,66 +335,56 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test(expected = AbortException.class)
|
||||
public void testBobProtocolAbortOnBadConfirm() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(aliceCommit, null);
|
||||
Payload ourPayload = new Payload(bobCommit, null);
|
||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
SecretKey sharedSecret = getSecretKey();
|
||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, false);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(payloadEncoder).encode(ourPayload);
|
||||
will(returnValue(bobPayload));
|
||||
will(returnValue(BOB_PAYLOAD));
|
||||
allowing(payloadEncoder).encode(theirPayload);
|
||||
will(returnValue(alicePayload));
|
||||
will(returnValue(ALICE_PAYLOAD));
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
allowing(alicePubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
will(returnValue(BOB_PUBKEY));
|
||||
|
||||
// Bob receives Alice's public key
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
will(returnValue(ALICE_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
|
||||
will(returnValue(alicePubKey));
|
||||
|
||||
// Bob verifies Alice's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(alicePubKey);
|
||||
will(returnValue(aliceCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
|
||||
will(returnValue(ALICE_COMMIT));
|
||||
|
||||
// Bob sends his public key
|
||||
oneOf(transport).sendKey(bobPubKeyBytes);
|
||||
oneOf(transport).sendKey(BOB_PUBKEY);
|
||||
|
||||
// Bob computes shared secret
|
||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||
alicePubKeyBytes, bobPubKeyBytes);
|
||||
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
|
||||
will(returnValue(sharedSecret));
|
||||
|
||||
// Bob receives a bad confirmation record
|
||||
oneOf(transport).receiveConfirm();
|
||||
will(returnValue(badConfirm));
|
||||
will(returnValue(BAD_CONFIRM));
|
||||
|
||||
// Bob verifies Alice's confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||
false, true);
|
||||
will(returnValue(aliceConfirm));
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
|
||||
will(returnValue(ALICE_CONFIRM));
|
||||
|
||||
// Bob aborts
|
||||
oneOf(transport).sendAbort(false);
|
||||
|
||||
// Bob never sends his confirmation record
|
||||
never(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||
false, false);
|
||||
never(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
|
||||
}});
|
||||
|
||||
// execute
|
||||
|
||||
@@ -34,7 +34,6 @@ import java.util.Map;
|
||||
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.properties.TransportPropertyManager.CLIENT_ID;
|
||||
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_VERSION;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
@@ -79,8 +78,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private TransportPropertyManagerImpl createInstance() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID,
|
||||
CLIENT_VERSION);
|
||||
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID);
|
||||
will(returnValue(localGroup));
|
||||
}});
|
||||
return new TransportPropertyManagerImpl(db, clientHelper,
|
||||
@@ -100,14 +98,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(contacts));
|
||||
// The first contact's group has already been set up
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact1);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact1);
|
||||
will(returnValue(contactGroup1));
|
||||
oneOf(db).containsGroup(txn, contactGroup1.getId());
|
||||
will(returnValue(true));
|
||||
// The second contact's group hasn't been set up
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact2);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact2);
|
||||
will(returnValue(contactGroup2));
|
||||
oneOf(db).containsGroup(txn, contactGroup2.getId());
|
||||
will(returnValue(false));
|
||||
@@ -134,8 +130,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// Create the group and share it with the contact
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(db).containsGroup(txn, contactGroup.getId());
|
||||
will(returnValue(false));
|
||||
@@ -161,8 +156,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
Group contactGroup = getGroup();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(db).removeGroup(txn, contactGroup);
|
||||
}});
|
||||
@@ -303,8 +297,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).getContact(txn, contact.getId());
|
||||
will(returnValue(contact));
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
||||
will(returnValue(contactGroup));
|
||||
}});
|
||||
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
|
||||
@@ -438,15 +431,13 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(contacts));
|
||||
// First contact: skipped because not active
|
||||
// Second contact: no updates
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact2);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact2);
|
||||
will(returnValue(contactGroup2));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup2.getId());
|
||||
will(returnValue(Collections.emptyMap()));
|
||||
// Third contact: returns an update
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact3);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact3);
|
||||
will(returnValue(contactGroup3));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup3.getId());
|
||||
@@ -516,8 +507,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
// Store the new properties in each contact's group, version 1
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(Collections.singletonList(contact)));
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
@@ -569,8 +559,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
// Store the merged properties in each contact's group, version 2
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(Collections.singletonList(contact)));
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.briarproject.bramble.sync;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
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.sync.Ack;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
@@ -22,6 +22,7 @@ import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -36,7 +37,6 @@ import javax.inject.Inject;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||
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.util.StringUtils.getRandomString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -57,7 +57,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
||||
@Inject
|
||||
RecordWriterFactory recordWriterFactory;
|
||||
@Inject
|
||||
TransportCrypto transportCrypto;
|
||||
CryptoComponent crypto;
|
||||
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
@@ -79,11 +79,9 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
||||
headerKey = TestUtils.getSecretKey();
|
||||
streamNumber = 123;
|
||||
// Create a group
|
||||
ClientId clientId = new ClientId(getRandomString(123));
|
||||
int clientVersion = 1234567890;
|
||||
ClientId clientId = new ClientId(StringUtils.getRandomString(5));
|
||||
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
|
||||
Group group = groupFactory.createGroup(clientId, clientVersion,
|
||||
descriptor);
|
||||
Group group = groupFactory.createGroup(clientId, descriptor);
|
||||
// Add two messages to the group
|
||||
long timestamp = System.currentTimeMillis();
|
||||
byte[] body = "Hello world".getBytes("UTF-8");
|
||||
@@ -119,8 +117,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
||||
private void read(byte[] connectionData) throws Exception {
|
||||
// Calculate the expected tag
|
||||
byte[] expectedTag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
|
||||
|
||||
// Read the tag
|
||||
InputStream in = new ByteArrayInputStream(connectionData);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
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.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
@@ -11,11 +11,12 @@ import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||
import org.briarproject.bramble.api.transport.StreamContext;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.RunAction;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.hamcrest.Description;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.jmock.api.Action;
|
||||
import org.jmock.api.Invocation;
|
||||
import org.junit.Test;
|
||||
@@ -40,15 +41,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
private final TransportCrypto transportCrypto =
|
||||
context.mock(TransportCrypto.class);
|
||||
private final Executor dbExecutor = context.mock(Executor.class);
|
||||
private final ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
private final Clock clock = context.mock(Clock.class);
|
||||
public class TransportKeyManagerImplTest extends BrambleTestCase {
|
||||
|
||||
private final TransportId transportId = new TransportId("id");
|
||||
private final long maxLatency = 30 * 1000; // 30 seconds
|
||||
@@ -62,6 +55,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
public void testKeysAreRotatedAtStartup() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
Map<ContactId, TransportKeys> loaded = new LinkedHashMap<>();
|
||||
TransportKeys shouldRotate = createTransportKeys(900, 0);
|
||||
TransportKeys shouldNotRotate = createTransportKeys(1000, 0);
|
||||
@@ -78,15 +79,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).getTransportKeys(txn, transportId);
|
||||
will(returnValue(loaded));
|
||||
// Rotate the transport keys
|
||||
oneOf(transportCrypto).rotateTransportKeys(shouldRotate, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(shouldRotate, 1000);
|
||||
will(returnValue(rotated));
|
||||
oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(shouldNotRotate, 1000);
|
||||
will(returnValue(shouldNotRotate));
|
||||
// Encode the tags (3 sets per contact)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(6).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Save the keys that were rotated
|
||||
@@ -97,124 +97,161 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
with(rotationPeriodLength - 1), with(MILLISECONDS));
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
transportKeyManager.start(txn);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeysAreRotatedWhenAddingContact() throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
boolean alice = true;
|
||||
TransportKeys transportKeys = createTransportKeys(999, 0);
|
||||
TransportKeys rotated = createTransportKeys(1000, 0);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
999, alice);
|
||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 999,
|
||||
alice);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (1 ms after start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000 + 1));
|
||||
// Rotate the transport keys
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(rotated));
|
||||
// Encode the tags (3 sets)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(3).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, rotated);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
// The timestamp is 1 ms before the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000 - 1;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
|
||||
throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
boolean alice = true;
|
||||
// The stream counter has been exhausted
|
||||
TransportKeys transportKeys = createTransportKeys(1000,
|
||||
MAX_32_BIT_UNSIGNED + 1);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice);
|
||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
||||
alice);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000));
|
||||
// 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));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutgoingStreamCounterIsIncremented() throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
boolean alice = true;
|
||||
// The stream counter can be used one more time before being exhausted
|
||||
TransportKeys transportKeys = createTransportKeys(1000,
|
||||
MAX_32_BIT_UNSIGNED);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice);
|
||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
||||
alice);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000));
|
||||
// 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));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||
@@ -222,9 +259,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
@@ -240,76 +277,94 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
|
||||
// The second request should return null, the counter is exhausted
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncomingStreamContextIsNullIfTagIsNotFound()
|
||||
throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
boolean alice = true;
|
||||
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice);
|
||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
||||
alice);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000));
|
||||
// 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));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
assertNull(transportKeyManager.getStreamContext(txn,
|
||||
new byte[TAG_LENGTH]));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTagIsNotRecognisedTwice() throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
boolean alice = true;
|
||||
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||
// Keep a copy of the tags
|
||||
List<byte[]> tags = new ArrayList<>();
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice);
|
||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
||||
alice);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000));
|
||||
// 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));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction(tags));
|
||||
}
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||
// Encode a new tag after sliding the window
|
||||
oneOf(transportCrypto).encodeTag(with(any(byte[].class)),
|
||||
oneOf(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION),
|
||||
with((long) REORDERING_WINDOW_SIZE));
|
||||
will(new EncodeTagAction(tags));
|
||||
@@ -318,9 +373,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
1, new byte[REORDERING_WINDOW_SIZE / 8]);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
@@ -340,10 +395,20 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
|
||||
// The second request should return null, the tag has already been used
|
||||
assertNull(transportKeyManager.getStreamContext(txn, tag));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeysAreRotatedToCurrentPeriod() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||
Map<ContactId, TransportKeys> loaded =
|
||||
Collections.singletonMap(contactId, transportKeys);
|
||||
@@ -359,13 +424,12 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).getTransportKeys(txn, transportId);
|
||||
will(returnValue(loaded));
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
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));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Schedule key rotation at the start of the next rotation period
|
||||
@@ -381,14 +445,13 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1001));
|
||||
// Rotate the transport keys
|
||||
oneOf(transportCrypto).rotateTransportKeys(
|
||||
with(any(TransportKeys.class)), with(1001L));
|
||||
oneOf(crypto).rotateTransportKeys(with(any(TransportKeys.class)),
|
||||
with(1001L));
|
||||
will(returnValue(rotated));
|
||||
// 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));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Save the keys that were rotated
|
||||
@@ -402,10 +465,12 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).endTransaction(txn1);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
transportKeyManager.start(txn);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
private TransportKeys createTransportKeys(long rotationPeriod,
|
||||
|
||||
@@ -12,16 +12,16 @@ dependencies {
|
||||
implementation 'net.java.dev.jna:jna:4.4.0'
|
||||
implementation 'net.java.dev.jna:jna-platform:4.4.0'
|
||||
|
||||
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
apt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||
testImplementation "junit:junit:$junitVersion"
|
||||
testImplementation "org.jmock:jmock:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-core:$hamcrestVersion"
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
|
||||
@@ -6,7 +6,6 @@ dependencies {
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
implementation project(path: ':bramble-android', configuration: 'default')
|
||||
|
||||
def supportVersion = '27.0.1'
|
||||
implementation "com.android.support:support-v4:$supportVersion"
|
||||
implementation("com.android.support:appcompat-v7:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
@@ -20,7 +19,7 @@ dependencies {
|
||||
}
|
||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||
implementation "com.android.support:support-annotations:$supportVersion"
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.0-beta3'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
|
||||
implementation('ch.acra:acra:4.8.5') {
|
||||
exclude module: 'support-v4'
|
||||
@@ -34,7 +33,7 @@ dependencies {
|
||||
implementation 'com.github.bumptech.glide:glide:3.8.0'
|
||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.1.0'
|
||||
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
|
||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
|
||||
@@ -43,12 +42,12 @@ dependencies {
|
||||
testImplementation 'org.robolectric:robolectric:3.5.1'
|
||||
testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
||||
testImplementation 'org.mockito:mockito-core:2.8.9'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||
testImplementation "junit:junit:$junitVersion"
|
||||
testImplementation "org.jmock:jmock:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-core:$hamcrestVersion"
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
@@ -61,8 +60,8 @@ dependencyVerification {
|
||||
'ch.acra:acra:4.8.5:acra-4.8.5.aar:afd5b28934d5166b55f261c85685ad59e8a4ebe9ca1960906afaa8c76d8dc9eb',
|
||||
'classworlds:classworlds:1.1-alpha-2:classworlds-1.1-alpha-2.jar:2bf4e59f3acd106fea6145a9a88fe8956509f8b9c0fdd11eb96fee757269e3f3',
|
||||
'com.almworks.sqlite4java:sqlite4java:0.282:sqlite4java-0.282.jar:9e1d8dd83ca6003f841e3af878ce2dc7c22497493a7bb6d1b62ec1b0d0a83c05',
|
||||
'com.android.support.constraint:constraint-layout-solver:1.1.0-beta3:constraint-layout-solver-1.1.0-beta3.jar:c9084108415046c423983bdff8cf04c8e9a5bed41b8d5329f3764c08312ee3dd',
|
||||
'com.android.support.constraint:constraint-layout:1.1.0-beta3:constraint-layout-1.1.0-beta3.aar:1754a6bd135feae485aa2ebf9e170f0f3d3282b392f8aa3067d0ed668839db79',
|
||||
'com.android.support.constraint:constraint-layout-solver:1.0.2:constraint-layout-solver-1.0.2.jar:8c62525a9bc5cff5633a96cb9b32fffeccaf41b8841aa87fc22607070dea9b8d',
|
||||
'com.android.support.constraint:constraint-layout:1.0.2:constraint-layout-1.0.2.aar:b0c688cc2b7172608f8153a689d746da40f71e52d7e2fe2bfd9df2f92db77085',
|
||||
'com.android.support:animated-vector-drawable:27.0.1:animated-vector-drawable-27.0.1.aar:365050110411c86c7eec86101b49ab53557ffe6667f60b19055f1d35c38a577b',
|
||||
'com.android.support:appcompat-v7:27.0.1:appcompat-v7-27.0.1.aar:1402c29a49db30346c21a7d40634461765b3ab826f5dd95bc4dcc76787b21851',
|
||||
'com.android.support:cardview-v7:27.0.1:cardview-v7-27.0.1.aar:43fccd44086c51eaa9d78be2fcf0dfea1556c8876a6fd325ea8d24e860054202',
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
@@ -125,7 +125,7 @@ public interface AndroidComponent
|
||||
|
||||
ContactExchangeTask contactExchangeTask();
|
||||
|
||||
KeyAgreementTask keyAgreementTask();
|
||||
KeyAgreementTaskFactory keyAgreementTaskFactory();
|
||||
|
||||
PayloadEncoder payloadEncoder();
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Application;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
|
||||
@@ -47,7 +44,6 @@ import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -61,10 +57,8 @@ import javax.inject.Inject;
|
||||
import static android.app.Notification.DEFAULT_LIGHTS;
|
||||
import static android.app.Notification.DEFAULT_SOUND;
|
||||
import static android.app.Notification.DEFAULT_VIBRATE;
|
||||
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
|
||||
import static android.content.Context.NOTIFICATION_SERVICE;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
|
||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
@@ -89,12 +83,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
private static final int BLOG_POST_NOTIFICATION_ID = 6;
|
||||
private static final int INTRODUCTION_SUCCESS_NOTIFICATION_ID = 7;
|
||||
|
||||
// Channel IDs
|
||||
private static final String CONTACT_CHANNEL_ID = "contacts";
|
||||
private static final String GROUP_CHANNEL_ID = "groups";
|
||||
private static final String FORUM_CHANNEL_ID = "forums";
|
||||
private static final String BLOG_CHANNEL_ID = "blogs";
|
||||
|
||||
private static final long SOUND_DELAY = TimeUnit.SECONDS.toMillis(2);
|
||||
|
||||
private static final Logger LOG =
|
||||
@@ -103,9 +91,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
private final Executor dbExecutor;
|
||||
private final SettingsManager settingsManager;
|
||||
private final AndroidExecutor androidExecutor;
|
||||
private final Clock clock;
|
||||
private final Context appContext;
|
||||
private final NotificationManager notificationManager;
|
||||
private final Clock clock;
|
||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||
|
||||
// The following must only be accessed on the main UI thread
|
||||
@@ -134,8 +121,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
this.androidExecutor = androidExecutor;
|
||||
this.clock = clock;
|
||||
appContext = app.getApplicationContext();
|
||||
notificationManager = (NotificationManager)
|
||||
appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,33 +132,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
} catch (DbException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
if (SDK_INT >= 26) {
|
||||
// Create notification channels
|
||||
Callable<Void> task = () -> {
|
||||
createNotificationChannel(CONTACT_CHANNEL_ID,
|
||||
R.string.contact_list_button);
|
||||
createNotificationChannel(GROUP_CHANNEL_ID,
|
||||
R.string.groups_button);
|
||||
createNotificationChannel(FORUM_CHANNEL_ID,
|
||||
R.string.forums_button);
|
||||
createNotificationChannel(BLOG_CHANNEL_ID,
|
||||
R.string.blogs_button);
|
||||
return null;
|
||||
};
|
||||
try {
|
||||
androidExecutor.runOnUiThread(task).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
private void createNotificationChannel(String channelId,
|
||||
@StringRes int name) {
|
||||
notificationManager.createNotificationChannel(
|
||||
new NotificationChannel(channelId, appContext.getString(name),
|
||||
IMPORTANCE_DEFAULT));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -198,34 +156,44 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
private void clearContactNotification() {
|
||||
contactCounts.clear();
|
||||
contactTotal = 0;
|
||||
notificationManager.cancel(PRIVATE_MESSAGE_NOTIFICATION_ID);
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.cancel(PRIVATE_MESSAGE_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void clearGroupMessageNotification() {
|
||||
groupCounts.clear();
|
||||
groupTotal = 0;
|
||||
notificationManager.cancel(GROUP_MESSAGE_NOTIFICATION_ID);
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.cancel(GROUP_MESSAGE_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void clearForumPostNotification() {
|
||||
forumCounts.clear();
|
||||
forumTotal = 0;
|
||||
notificationManager.cancel(FORUM_POST_NOTIFICATION_ID);
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.cancel(FORUM_POST_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void clearBlogPostNotification() {
|
||||
blogCounts.clear();
|
||||
blogTotal = 0;
|
||||
notificationManager.cancel(BLOG_POST_NOTIFICATION_ID);
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.cancel(BLOG_POST_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void clearIntroductionSuccessNotification() {
|
||||
introductionTotal = 0;
|
||||
notificationManager.cancel(INTRODUCTION_SUCCESS_NOTIFICATION_ID);
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.cancel(INTRODUCTION_SUCCESS_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -301,8 +269,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
if (contactTotal == 0) {
|
||||
clearContactNotification();
|
||||
} else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) {
|
||||
BriarNotificationBuilder b = new BriarNotificationBuilder(
|
||||
appContext, CONTACT_CHANNEL_ID);
|
||||
BriarNotificationBuilder b =
|
||||
new BriarNotificationBuilder(appContext);
|
||||
b.setSmallIcon(R.drawable.notification_private_message);
|
||||
b.setColorRes(R.color.briar_primary);
|
||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||
@@ -337,8 +305,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
t.addNextIntent(i);
|
||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||
}
|
||||
notificationManager.notify(PRIVATE_MESSAGE_NOTIFICATION_ID,
|
||||
b.build());
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +378,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
clearGroupMessageNotification();
|
||||
} else if (settings.getBoolean(PREF_NOTIFY_GROUP, true)) {
|
||||
BriarNotificationBuilder b =
|
||||
new BriarNotificationBuilder(appContext, GROUP_CHANNEL_ID);
|
||||
new BriarNotificationBuilder(appContext);
|
||||
b.setSmallIcon(R.drawable.notification_private_group);
|
||||
b.setColorRes(R.color.briar_primary);
|
||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||
@@ -445,8 +414,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
t.addNextIntent(i);
|
||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||
}
|
||||
notificationManager.notify(GROUP_MESSAGE_NOTIFICATION_ID,
|
||||
b.build());
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.notify(GROUP_MESSAGE_NOTIFICATION_ID, b.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +455,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
clearForumPostNotification();
|
||||
} else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) {
|
||||
BriarNotificationBuilder b =
|
||||
new BriarNotificationBuilder(appContext, FORUM_CHANNEL_ID);
|
||||
new BriarNotificationBuilder(appContext);
|
||||
b.setSmallIcon(R.drawable.notification_forum);
|
||||
b.setColorRes(R.color.briar_primary);
|
||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||
@@ -521,7 +491,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
t.addNextIntent(i);
|
||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||
}
|
||||
notificationManager.notify(FORUM_POST_NOTIFICATION_ID, b.build());
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.notify(FORUM_POST_NOTIFICATION_ID, b.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,7 +532,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
clearBlogPostNotification();
|
||||
} else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
|
||||
BriarNotificationBuilder b =
|
||||
new BriarNotificationBuilder(appContext, BLOG_CHANNEL_ID);
|
||||
new BriarNotificationBuilder(appContext);
|
||||
b.setSmallIcon(R.drawable.notification_blog);
|
||||
b.setColorRes(R.color.briar_primary);
|
||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||
@@ -583,7 +555,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
t.addNextIntent(i);
|
||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||
|
||||
notificationManager.notify(BLOG_POST_NOTIFICATION_ID, b.build());
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.notify(BLOG_POST_NOTIFICATION_ID, b.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,8 +577,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
|
||||
@UiThread
|
||||
private void updateIntroductionNotification() {
|
||||
BriarNotificationBuilder b =
|
||||
new BriarNotificationBuilder(appContext, CONTACT_CHANNEL_ID);
|
||||
BriarNotificationBuilder b = new BriarNotificationBuilder(appContext);
|
||||
b.setSmallIcon(R.drawable.notification_introduction);
|
||||
b.setColorRes(R.color.briar_primary);
|
||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||
@@ -626,8 +599,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
t.addNextIntent(i);
|
||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||
|
||||
notificationManager.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID,
|
||||
b.build());
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
@@ -25,12 +26,9 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
|
||||
import static android.app.NotificationManager.IMPORTANCE_NONE;
|
||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
|
||||
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
|
||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||
@@ -40,21 +38,9 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResul
|
||||
|
||||
public class BriarService extends Service {
|
||||
|
||||
public static String EXTRA_START_RESULT =
|
||||
"org.briarproject.briar.START_RESULT";
|
||||
public static String EXTRA_NOTIFICATION_ID =
|
||||
"org.briarproject.briar.FAILURE_NOTIFICATION_ID";
|
||||
public static String EXTRA_STARTUP_FAILED =
|
||||
"org.briarproject.briar.STARTUP_FAILED";
|
||||
|
||||
private static final int ONGOING_NOTIFICATION_ID = 1;
|
||||
private static final int FAILURE_NOTIFICATION_ID = 2;
|
||||
|
||||
// Channels are sorted by channel ID in the Settings app, so use IDs
|
||||
// that will sort below the main channels such as contacts
|
||||
private static final String ONGOING_CHANNEL_ID = "zForegroundService";
|
||||
private static final String FAILURE_CHANNEL_ID = "zStartupFailure";
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BriarService.class.getName());
|
||||
|
||||
@@ -88,26 +74,20 @@ public class BriarService extends Service {
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
// Create notification channels
|
||||
if (SDK_INT >= 26) {
|
||||
NotificationManager nm = (NotificationManager)
|
||||
getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationChannel ongoingChannel = new NotificationChannel(
|
||||
ONGOING_CHANNEL_ID,
|
||||
getString(R.string.ongoing_notification_title),
|
||||
IMPORTANCE_NONE);
|
||||
ongoingChannel.setLockscreenVisibility(VISIBILITY_SECRET);
|
||||
nm.createNotificationChannel(ongoingChannel);
|
||||
NotificationChannel failureChannel = new NotificationChannel(
|
||||
FAILURE_CHANNEL_ID,
|
||||
getString(R.string.startup_failed_notification_title),
|
||||
IMPORTANCE_DEFAULT);
|
||||
failureChannel.setLockscreenVisibility(VISIBILITY_SECRET);
|
||||
nm.createNotificationChannel(failureChannel);
|
||||
// Create mandatory notification channel
|
||||
String channelId = "foregroundService";
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
NotificationChannel channel = new NotificationChannel(channelId,
|
||||
getString(R.string.app_name),
|
||||
NotificationManager.IMPORTANCE_NONE);
|
||||
channel.setLockscreenVisibility(VISIBILITY_SECRET);
|
||||
NotificationManager nm =
|
||||
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
nm.createNotificationChannel(channel);
|
||||
}
|
||||
// Show an ongoing notification that the service is running
|
||||
NotificationCompat.Builder b =
|
||||
new NotificationCompat.Builder(this, ONGOING_CHANNEL_ID);
|
||||
new NotificationCompat.Builder(this, channelId);
|
||||
b.setSmallIcon(R.drawable.notification_ongoing);
|
||||
b.setColor(ContextCompat.getColor(this, R.color.briar_primary));
|
||||
b.setContentTitle(getText(R.string.ongoing_notification_title));
|
||||
@@ -117,7 +97,7 @@ public class BriarService extends Service {
|
||||
Intent i = new Intent(this, NavDrawerActivity.class);
|
||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
||||
if (SDK_INT >= 21) {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
b.setCategory(CATEGORY_SERVICE);
|
||||
b.setVisibility(VISIBILITY_SECRET);
|
||||
}
|
||||
@@ -143,8 +123,8 @@ public class BriarService extends Service {
|
||||
|
||||
private void showStartupFailureNotification(StartResult result) {
|
||||
androidExecutor.runOnUiThread(() -> {
|
||||
NotificationCompat.Builder b = new NotificationCompat.Builder(
|
||||
BriarService.this, FAILURE_CHANNEL_ID);
|
||||
NotificationCompat.Builder b =
|
||||
new NotificationCompat.Builder(BriarService.this);
|
||||
b.setSmallIcon(android.R.drawable.stat_notify_error);
|
||||
b.setContentTitle(getText(
|
||||
R.string.startup_failed_notification_title));
|
||||
@@ -153,8 +133,9 @@ public class BriarService extends Service {
|
||||
Intent i = new Intent(BriarService.this,
|
||||
StartupFailureActivity.class);
|
||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
i.putExtra(EXTRA_START_RESULT, result);
|
||||
i.putExtra(EXTRA_NOTIFICATION_ID, FAILURE_NOTIFICATION_ID);
|
||||
i.putExtra("briar.START_RESULT", result);
|
||||
i.putExtra("briar.FAILURE_NOTIFICATION_ID",
|
||||
FAILURE_NOTIFICATION_ID);
|
||||
b.setContentIntent(PendingIntent.getActivity(BriarService.this,
|
||||
0, i, FLAG_UPDATE_CURRENT));
|
||||
Object o = getSystemService(NOTIFICATION_SERVICE);
|
||||
@@ -163,7 +144,7 @@ public class BriarService extends Service {
|
||||
// Bring the dashboard to the front to clear the back stack
|
||||
i = new Intent(BriarService.this, NavDrawerActivity.class);
|
||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i.putExtra(EXTRA_STARTUP_FAILED, true);
|
||||
i.putExtra("briar.STARTUP_FAILED", true);
|
||||
startActivity(i);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BaseActivity;
|
||||
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
|
||||
import static org.briarproject.briar.android.BriarService.EXTRA_NOTIFICATION_ID;
|
||||
import static org.briarproject.briar.android.BriarService.EXTRA_START_RESULT;
|
||||
|
||||
public class StartupFailureActivity extends BaseActivity {
|
||||
|
||||
@@ -29,9 +27,8 @@ public class StartupFailureActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
private void handleIntent(Intent i) {
|
||||
StartResult result =
|
||||
(StartResult) i.getSerializableExtra(EXTRA_START_RESULT);
|
||||
int notificationId = i.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
|
||||
StartResult result = (StartResult) i.getSerializableExtra("briar.START_RESULT");
|
||||
int notificationId = i.getIntExtra("briar.FAILURE_NOTIFICATION_ID", -1);
|
||||
|
||||
// cancel notification
|
||||
if (notificationId > -1) {
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.View;
|
||||
@@ -139,9 +138,7 @@ public abstract class BaseActivity extends AppCompatActivity
|
||||
dialogFrag =
|
||||
ScreenFilterDialogFragment.newInstance(new ArrayList<>(apps));
|
||||
dialogFrag.setCancelable(false);
|
||||
// Show dialog unless onSaveInstanceState() has been called, see #1112
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
if (!fm.isStateSaved()) dialogFrag.show(fm, dialogFrag.getTag());
|
||||
dialogFrag.show(getSupportFragmentManager(), dialogFrag.getTag());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,7 +27,6 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
||||
import static android.os.Build.MANUFACTURER;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
||||
@@ -36,6 +35,7 @@ import static org.briarproject.briar.android.util.UiUtils.getDozeWhitelistingInt
|
||||
@SuppressLint("Registered")
|
||||
public abstract class BriarActivity extends BaseActivity {
|
||||
|
||||
public static final String KEY_STARTUP_FAILED = "briar.STARTUP_FAILED";
|
||||
public static final String GROUP_ID = "briar.GROUP_ID";
|
||||
public static final String GROUP_NAME = "briar.GROUP_NAME";
|
||||
|
||||
@@ -79,10 +79,6 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
|
||||
public void setSceneTransitionAnimation() {
|
||||
if (SDK_INT < 21) return;
|
||||
// workaround for #1007
|
||||
if (SDK_INT == 24 && MANUFACTURER.equalsIgnoreCase("Samsung")) {
|
||||
return;
|
||||
}
|
||||
Transition slide = new Slide(Gravity.RIGHT);
|
||||
slide.excludeTarget(android.R.id.statusBarBackground, true);
|
||||
slide.excludeTarget(android.R.id.navigationBarBackground, true);
|
||||
@@ -130,8 +126,8 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
b.setNegativeButton(R.string.cancel,
|
||||
(dialog, which) -> dialog.dismiss());
|
||||
b.setOnDismissListener(dialog -> {
|
||||
CheckBox checkBox =
|
||||
((AlertDialog) dialog).findViewById(R.id.checkbox);
|
||||
CheckBox checkBox = (CheckBox) ((AlertDialog) dialog)
|
||||
.findViewById(R.id.checkbox);
|
||||
if (checkBox.isChecked())
|
||||
briarController.doNotAskAgainForDozeWhiteListing();
|
||||
});
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.google.zxing.Result;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
@@ -47,7 +48,6 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
||||
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
|
||||
@@ -68,7 +68,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
||||
private static final Logger LOG = Logger.getLogger(TAG);
|
||||
|
||||
@Inject
|
||||
Provider<KeyAgreementTask> keyAgreementTaskProvider;
|
||||
KeyAgreementTaskFactory keyAgreementTaskFactory;
|
||||
@Inject
|
||||
PayloadEncoder payloadEncoder;
|
||||
@Inject
|
||||
@@ -187,7 +187,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
||||
@UiThread
|
||||
private void startListening() {
|
||||
KeyAgreementTask oldTask = task;
|
||||
KeyAgreementTask newTask = keyAgreementTaskProvider.get();
|
||||
KeyAgreementTask newTask = keyAgreementTaskFactory.createTask();
|
||||
task = newTask;
|
||||
ioExecutor.execute(() -> {
|
||||
if (oldTask != null) oldTask.stopListening();
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
package org.briarproject.briar.android.login;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.login.PowerView.OnCheckedChangedListener;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import static android.view.View.INVISIBLE;
|
||||
@@ -21,15 +19,12 @@ import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
|
||||
|
||||
@NotNullByDefault
|
||||
public class DozeFragment extends SetupFragment
|
||||
implements OnCheckedChangedListener {
|
||||
@TargetApi(23)
|
||||
public class DozeFragment extends SetupFragment {
|
||||
|
||||
private final static String TAG = DozeFragment.class.getName();
|
||||
|
||||
private DozeView dozeView;
|
||||
private HuaweiView huaweiView;
|
||||
private Button next;
|
||||
private Button dozeButton;
|
||||
private ProgressBar progressBar;
|
||||
private boolean secondAttempt = false;
|
||||
|
||||
@@ -38,22 +33,15 @@ public class DozeFragment extends SetupFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
getActivity().setTitle(getString(R.string.setup_doze_title));
|
||||
setHasOptionsMenu(false);
|
||||
View v = inflater.inflate(R.layout.fragment_setup_doze, container,
|
||||
false);
|
||||
dozeView = v.findViewById(R.id.dozeView);
|
||||
dozeView.setOnCheckedChangedListener(this);
|
||||
huaweiView = v.findViewById(R.id.huaweiView);
|
||||
huaweiView.setOnCheckedChangedListener(this);
|
||||
next = v.findViewById(R.id.next);
|
||||
dozeButton = v.findViewById(R.id.dozeButton);
|
||||
progressBar = v.findViewById(R.id.progress);
|
||||
|
||||
dozeView.setOnButtonClickListener(this::askForDozeWhitelisting);
|
||||
next.setOnClickListener(this);
|
||||
dozeButton.setOnClickListener(view -> askForDozeWhitelisting());
|
||||
|
||||
return v;
|
||||
}
|
||||
@@ -77,34 +65,25 @@ public class DozeFragment extends SetupFragment
|
||||
public void onActivityResult(int request, int result, Intent data) {
|
||||
super.onActivityResult(request, result, data);
|
||||
if (request == REQUEST_DOZE_WHITELISTING) {
|
||||
if (!dozeView.needsToBeShown() || secondAttempt) {
|
||||
dozeView.setChecked(true);
|
||||
} else if (getContext() != null) {
|
||||
if (!setupController.needsDozeWhitelisting() || secondAttempt) {
|
||||
dozeButton.setEnabled(false);
|
||||
onClick(dozeButton);
|
||||
} else {
|
||||
secondAttempt = true;
|
||||
showOnboardingDialog(getContext(), getHelpText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged() {
|
||||
if (dozeView.isChecked() && huaweiView.isChecked()) {
|
||||
next.setEnabled(true);
|
||||
} else {
|
||||
next.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("BatteryLife")
|
||||
private void askForDozeWhitelisting() {
|
||||
if (getContext() == null) return;
|
||||
Intent i = UiUtils.getDozeWhitelistingIntent(getContext());
|
||||
startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
next.setVisibility(INVISIBLE);
|
||||
dozeButton.setVisibility(INVISIBLE);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
setupController.createAccount();
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
package org.briarproject.briar.android.login;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
class DozeView extends PowerView {
|
||||
|
||||
@Nullable
|
||||
private Runnable onButtonClickListener;
|
||||
|
||||
public DozeView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public DozeView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public DozeView(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setText(R.string.setup_doze_intro);
|
||||
setButtonText(R.string.setup_doze_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsToBeShown() {
|
||||
return needsToBeShown(getContext());
|
||||
}
|
||||
|
||||
public static boolean needsToBeShown(Context context) {
|
||||
return needsDozeWhitelisting(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHelpText() {
|
||||
return R.string.setup_doze_explanation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onButtonClick() {
|
||||
if (onButtonClickListener == null) throw new IllegalStateException();
|
||||
onButtonClickListener.run();
|
||||
}
|
||||
|
||||
public void setOnButtonClickListener(Runnable runnable) {
|
||||
onButtonClickListener = runnable;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package org.briarproject.briar.android.login;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
class HuaweiView extends PowerView {
|
||||
|
||||
private final static String PACKAGE_NAME = "com.huawei.systemmanager";
|
||||
private final static String CLASS_NAME =
|
||||
PACKAGE_NAME + ".optimize.process.ProtectActivity";
|
||||
|
||||
public HuaweiView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public HuaweiView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public HuaweiView(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setText(R.string.setup_huawei_text);
|
||||
setButtonText(R.string.setup_huawei_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsToBeShown() {
|
||||
return needsToBeShown(getContext());
|
||||
}
|
||||
|
||||
public static boolean needsToBeShown(Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(getIntent(),
|
||||
PackageManager.MATCH_DEFAULT_ONLY);
|
||||
return !resolveInfos.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@StringRes
|
||||
protected int getHelpText() {
|
||||
return R.string.setup_huawei_help;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onButtonClick() {
|
||||
getContext().startActivity(getIntent());
|
||||
setChecked(true);
|
||||
}
|
||||
|
||||
private static Intent getIntent() {
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName(PACKAGE_NAME, CLASS_NAME);
|
||||
return intent;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class PasswordFragment extends SetupFragment {
|
||||
component.inject(this);
|
||||
|
||||
// the controller is not yet available in onCreateView()
|
||||
if (!setupController.needToShowDozeFragment()) {
|
||||
if (!setupController.needsDozeWhitelisting()) {
|
||||
nextButton.setText(R.string.create_account_button);
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ public class PasswordFragment extends SetupFragment {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (!setupController.needToShowDozeFragment()) {
|
||||
if (!setupController.needsDozeWhitelisting()) {
|
||||
nextButton.setVisibility(INVISIBLE);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
package org.briarproject.briar.android.login;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.constraint.ConstraintLayout;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
abstract class PowerView extends ConstraintLayout {
|
||||
|
||||
private final TextView textView;
|
||||
private final ImageView checkImage;
|
||||
private final Button button;
|
||||
|
||||
private boolean checked = false;
|
||||
|
||||
@Nullable
|
||||
private OnCheckedChangedListener onCheckedChangedListener;
|
||||
|
||||
public PowerView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public PowerView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public PowerView(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(LAYOUT_INFLATER_SERVICE);
|
||||
View v = inflater.inflate(R.layout.power_view, this, true);
|
||||
|
||||
textView = v.findViewById(R.id.textView);
|
||||
checkImage = v.findViewById(R.id.checkImage);
|
||||
button = v.findViewById(R.id.button);
|
||||
button.setOnClickListener(view -> onButtonClick());
|
||||
ImageButton helpButton = v.findViewById(R.id.helpButton);
|
||||
helpButton.setOnClickListener(view -> onHelpButtonClick());
|
||||
|
||||
// we need to manage the checkImage state ourselves, because automatic
|
||||
// state saving is done based on the view's ID and there can be
|
||||
// multiple ImageViews with the same ID in the view hierarchy
|
||||
setSaveFromParentEnabled(true);
|
||||
|
||||
if (!isInEditMode() && !needsToBeShown()) {
|
||||
setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
SavedState ss = new SavedState(superState);
|
||||
ss.value = new boolean[] {checked};
|
||||
return ss;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState ss = (SavedState) state;
|
||||
super.onRestoreInstanceState(ss.getSuperState());
|
||||
setChecked(ss.value[0]); // also calls listener
|
||||
}
|
||||
|
||||
public abstract boolean needsToBeShown();
|
||||
|
||||
public void setChecked(boolean checked) {
|
||||
this.checked = checked;
|
||||
if (checked) {
|
||||
checkImage.setImageResource(R.drawable.ic_check_white);
|
||||
} else {
|
||||
checkImage.setImageResource(R.drawable.contact_disconnected);
|
||||
}
|
||||
if (onCheckedChangedListener != null) {
|
||||
onCheckedChangedListener.onCheckedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isChecked() {
|
||||
return getVisibility() == GONE || checked;
|
||||
}
|
||||
|
||||
public void setOnCheckedChangedListener(
|
||||
OnCheckedChangedListener onCheckedChangedListener) {
|
||||
this.onCheckedChangedListener = onCheckedChangedListener;
|
||||
}
|
||||
|
||||
@StringRes
|
||||
protected abstract int getHelpText();
|
||||
|
||||
protected void setText(@StringRes int res) {
|
||||
textView.setText(res);
|
||||
}
|
||||
|
||||
protected void setButtonText(@StringRes int res) {
|
||||
button.setText(res);
|
||||
}
|
||||
|
||||
protected abstract void onButtonClick();
|
||||
|
||||
private void onHelpButtonClick() {
|
||||
showOnboardingDialog(getContext(),
|
||||
getContext().getString(getHelpText()));
|
||||
}
|
||||
|
||||
private static class SavedState extends BaseSavedState {
|
||||
private boolean[] value = {false};
|
||||
|
||||
private SavedState(@Nullable Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
in.readBooleanArray(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeBooleanArray(value);
|
||||
}
|
||||
|
||||
static final Parcelable.Creator<SavedState> CREATOR
|
||||
= new Parcelable.Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
interface OnCheckedChangedListener {
|
||||
void onCheckedChanged();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@ public interface SetupController {
|
||||
|
||||
void setSetupActivity(SetupActivity setupActivity);
|
||||
|
||||
boolean needToShowDozeFragment();
|
||||
boolean needsDozeWhitelisting();
|
||||
|
||||
void setAuthorName(String authorName);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -40,10 +41,9 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needToShowDozeFragment() {
|
||||
public boolean needsDozeWhitelisting() {
|
||||
if (setupActivity == null) throw new IllegalStateException();
|
||||
return DozeView.needsToBeShown(setupActivity) ||
|
||||
HuaweiView.needsToBeShown(setupActivity);
|
||||
return UiUtils.needsDozeWhitelisting(setupActivity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,7 +61,7 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
||||
@Override
|
||||
public void showDozeOrCreateAccount() {
|
||||
if (setupActivity == null) throw new IllegalStateException();
|
||||
if (needToShowDozeFragment()) {
|
||||
if (needsDozeWhitelisting()) {
|
||||
setupActivity.showDozeFragment();
|
||||
} else {
|
||||
createAccount();
|
||||
|
||||
@@ -51,7 +51,6 @@ import static android.support.v4.view.GravityCompat.START;
|
||||
import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
||||
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.NO;
|
||||
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.UPDATE;
|
||||
@@ -168,7 +167,7 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
}
|
||||
|
||||
private void exitIfStartupFailed(Intent intent) {
|
||||
if (intent.getBooleanExtra(EXTRA_STARTUP_FAILED, false)) {
|
||||
if (intent.getBooleanExtra(KEY_STARTUP_FAILED, false)) {
|
||||
finish();
|
||||
LOG.info("Exiting");
|
||||
System.exit(0);
|
||||
|
||||
@@ -12,8 +12,8 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||
|
||||
public class BriarNotificationBuilder extends NotificationCompat.Builder {
|
||||
|
||||
public BriarNotificationBuilder(Context context, String channelId) {
|
||||
super(context, channelId);
|
||||
public BriarNotificationBuilder(Context context) {
|
||||
super(context);
|
||||
// Auto-cancel does not fire the delete intent, see
|
||||
// https://issuetracker.google.com/issues/36961721
|
||||
setAutoCancel(true);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M11,18h2v-2h-2v2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,6c-2.21,0 -4,1.79 -4,4h2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,2 -3,1.75 -3,5h2c0,-2.25 3,-2.5 3,-5 0,-2.21 -1.79,-4 -4,-4z"/>
|
||||
</vector>
|
||||
10
briar-android/src/main/res/drawable/navigation_accept.xml
Normal file
10
briar-android/src/main/res/drawable/navigation_accept.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:alpha="0.54"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||
</vector>
|
||||
@@ -26,7 +26,7 @@
|
||||
app:hintEnabled="false"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
app:layout_constraintTop_toBottomOf="parent">
|
||||
|
||||
<android.support.design.widget.TextInputEditText
|
||||
android:id="@+id/nickname_entry"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
@@ -17,47 +16,38 @@
|
||||
android:paddingStart="@dimen/margin_activity_horizontal"
|
||||
android:paddingTop="@dimen/margin_activity_vertical">
|
||||
|
||||
<org.briarproject.briar.android.login.DozeView
|
||||
android:id="@+id/dozeView"
|
||||
<TextView
|
||||
android:id="@+id/setup_explanation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:text="@string/setup_doze_intro"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<org.briarproject.briar.android.login.HuaweiView
|
||||
android:id="@+id/huaweiView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dozeView"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/next"
|
||||
android:id="@+id/dozeButton"
|
||||
style="@style/BriarButton.Default"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="false"
|
||||
android:text="@string/create_account_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/huaweiView"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
tools:enabled="true"/>
|
||||
android:layout_marginTop="@dimen/margin_activity_horizontal"
|
||||
android:text="@string/setup_doze_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/setup_explanation"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/next"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/dozeButton"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/next"/>
|
||||
app:layout_constraintTop_toTopOf="@+id/dozeButton"/>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:parentTag="android.support.constraint.ConstraintLayout">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/setup_huawei_text"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/checkImage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/contact_disconnected"
|
||||
android:tint="?colorControlNormal"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/button"
|
||||
tools:ignore="ContentDescription"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
style="@style/BriarButton.Default"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/helpButton"
|
||||
app:layout_constraintStart_toEndOf="@+id/checkImage"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView"
|
||||
tools:text="@string/setup_huawei_button"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/helpButton"
|
||||
style="@style/BriarButton.Default"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/help"
|
||||
android:src="@drawable/ic_help_outline_white"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/button"/>
|
||||
|
||||
</merge>
|
||||
@@ -16,7 +16,6 @@
|
||||
<color name="briar_primary_dark">@color/briar_blue_dark</color>
|
||||
|
||||
<color name="briar_accent">@color/briar_blue</color>
|
||||
<color name="control_normal_light">#757575</color>
|
||||
|
||||
<!-- text colors -->
|
||||
<color name="briar_text_link">#06b9ff</color>
|
||||
|
||||
@@ -21,11 +21,6 @@
|
||||
<string name="more_info">More Information</string>
|
||||
<string name="don_t_ask_again">Don\'t ask again</string>
|
||||
|
||||
<string name="setup_huawei_text">Please tap the button below and make sure Briar is protected in the \"Protected Apps\" screen.</string>
|
||||
<string name="setup_huawei_button">Protect Briar</string>
|
||||
<string name="setup_huawei_help">If Briar is not added to the protected apps list, it will be unable to run in the background.</string>
|
||||
<string name="warning_dozed">%s was unable to run in the background</string>
|
||||
|
||||
<!-- Login -->
|
||||
<string name="enter_password">Enter your password:</string>
|
||||
<string name="try_again">Wrong password, try again</string>
|
||||
@@ -100,8 +95,8 @@
|
||||
<string name="ellipsis">…</string>
|
||||
<string name="text_too_long">The entered text is too long</string>
|
||||
<string name="show_onboarding">Show Help Dialog</string>
|
||||
<string name="warning_dozed">%s was unable to run in the background</string>
|
||||
<string name="fix">Fix</string>
|
||||
<string name="help">Help</string>
|
||||
|
||||
<!-- Contacts and Private Conversations-->
|
||||
<string name="no_contacts">It seems that you are new here and have no contacts yet.\n\nTap the + icon at the top and follow the instructions to add some friends to your list.\n\nPlease remember: You can only add new contacts face-to-face to prevent anyone from impersonating you or reading your messages in the future.</string>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<item name="colorPrimary">@color/briar_primary</item>
|
||||
<item name="colorPrimaryDark">@color/briar_primary_dark</item>
|
||||
<item name="colorAccent">@color/briar_accent</item>
|
||||
<item name="colorControlNormal">@color/control_normal_light</item>
|
||||
<item name="android:textColorLink">@color/briar_text_link</item>
|
||||
<item name="android:windowBackground">@color/window_background</item>
|
||||
<item name="android:windowAnimationStyle">@style/ActivityAnimation</item>
|
||||
|
||||
@@ -61,7 +61,7 @@ public class PasswordFragmentTest {
|
||||
String safePass = "really.safe.password";
|
||||
|
||||
passwordFragment.setupController = setupController;
|
||||
when(setupController.needToShowDozeFragment()).thenReturn(false);
|
||||
when(setupController.needsDozeWhitelisting()).thenReturn(false);
|
||||
when(setupController.estimatePasswordStrength(safePass))
|
||||
.thenReturn(STRONG);
|
||||
|
||||
|
||||
@@ -17,15 +17,10 @@ import javax.annotation.Nullable;
|
||||
public interface BlogManager {
|
||||
|
||||
/**
|
||||
* The unique ID of the blog client.
|
||||
* Unique ID of the blog client.
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog");
|
||||
|
||||
/**
|
||||
* The current version of the blog client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Adds the given {@link Blog).}
|
||||
*/
|
||||
|
||||
@@ -5,13 +5,6 @@ import org.briarproject.briar.api.sharing.SharingManager;
|
||||
|
||||
public interface BlogSharingManager extends SharingManager<Blog> {
|
||||
|
||||
/**
|
||||
* The unique ID of the blog sharing client.
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog.sharing");
|
||||
|
||||
/**
|
||||
* The current version of the blog sharing client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
}
|
||||
|
||||
@@ -15,11 +15,6 @@ public interface FeedManager {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.feed");
|
||||
|
||||
/**
|
||||
* The current version of the RSS feed client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Adds an RSS feed as a new dedicated blog.
|
||||
*/
|
||||
|
||||
@@ -22,11 +22,6 @@ public interface ForumManager {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum");
|
||||
|
||||
/**
|
||||
* The current version of the forum client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Subscribes to a forum.
|
||||
*/
|
||||
|
||||
@@ -5,13 +5,6 @@ import org.briarproject.briar.api.sharing.SharingManager;
|
||||
|
||||
public interface ForumSharingManager extends SharingManager<Forum> {
|
||||
|
||||
/**
|
||||
* The unique ID of the forum sharing client.
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum.sharing");
|
||||
|
||||
/**
|
||||
* The current version of the forum sharing client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
}
|
||||
|
||||
@@ -86,44 +86,4 @@ public interface IntroductionConstants {
|
||||
int TASK_ACTIVATE_CONTACT = 1;
|
||||
int TASK_ABORT = 2;
|
||||
|
||||
/**
|
||||
* Label for deriving the shared secret.
|
||||
*/
|
||||
String SHARED_SECRET_LABEL =
|
||||
"org.briarproject.briar.introduction/SHARED_SECRET";
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's key binding nonce from the shared secret.
|
||||
*/
|
||||
String ALICE_NONCE_LABEL =
|
||||
"org.briarproject.briar.introduction/ALICE_NONCE";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's key binding nonce from the shared secret.
|
||||
*/
|
||||
String BOB_NONCE_LABEL =
|
||||
"org.briarproject.briar.introduction/BOB_NONCE";
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's MAC key from the shared secret.
|
||||
*/
|
||||
String ALICE_MAC_KEY_LABEL =
|
||||
"org.briarproject.briar.introduction/ALICE_MAC_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's MAC key from the shared secret.
|
||||
*/
|
||||
String BOB_MAC_KEY_LABEL =
|
||||
"org.briarproject.briar.introduction/BOB_MAC_KEY";
|
||||
|
||||
/**
|
||||
* Label for signing the introduction response.
|
||||
*/
|
||||
String SIGNING_LABEL =
|
||||
"org.briarproject.briar.introduction/RESPONSE_SIGNATURE";
|
||||
|
||||
/**
|
||||
* Label for MACing the introduction response.
|
||||
*/
|
||||
String MAC_LABEL = "org.briarproject.briar.introduction/RESPONSE_MAC";
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ public interface IntroductionManager extends ConversationClient {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.introduction");
|
||||
|
||||
/**
|
||||
* The current version of the introduction client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Sends two initial introduction messages.
|
||||
*/
|
||||
|
||||
@@ -18,11 +18,6 @@ public interface MessagingManager extends ConversationClient {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.messaging");
|
||||
|
||||
/**
|
||||
* The current version of the messaging client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Stores a local private message.
|
||||
*/
|
||||
|
||||
@@ -21,11 +21,6 @@ public interface PrivateGroupManager {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.privategroup");
|
||||
|
||||
/**
|
||||
* The current version of the private group client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Adds a new private group and joins it.
|
||||
*
|
||||
|
||||
@@ -25,11 +25,6 @@ public interface GroupInvitationManager extends ConversationClient {
|
||||
ClientId CLIENT_ID =
|
||||
new ClientId("org.briarproject.briar.privategroup.invitation");
|
||||
|
||||
/**
|
||||
* The current version of the private group invitation client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Sends an invitation to share the given private group with the given
|
||||
* contact, including an optional message.
|
||||
|
||||
@@ -13,20 +13,20 @@ dependencies {
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.8.0'
|
||||
implementation 'org.jsoup:jsoup:1.10.3'
|
||||
|
||||
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
apt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
|
||||
testImplementation project(path: ':bramble-core', configuration: 'default')
|
||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
testImplementation 'net.jodah:concurrentunit:0.4.2'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||
testImplementation "junit:junit:$junitVersion"
|
||||
testImplementation "org.jmock:jmock:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion"
|
||||
testImplementation "org.hamcrest:hamcrest-core:$hamcrestVersion"
|
||||
|
||||
testApt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
testApt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
|
||||
@@ -16,8 +16,6 @@ import javax.inject.Inject;
|
||||
|
||||
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.briar.api.blog.BlogManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -54,8 +52,8 @@ class BlogFactoryImpl implements BlogFactory {
|
||||
rssFeed
|
||||
);
|
||||
byte[] descriptor = clientHelper.toByteArray(blog);
|
||||
Group g = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
|
||||
descriptor);
|
||||
Group g = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
return new Blog(g, a, rssFeed);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -45,8 +45,6 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED;
|
||||
import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE;
|
||||
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_COMMENT_LENGTH;
|
||||
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
|
||||
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_VERSION;
|
||||
import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_COMMENT;
|
||||
import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_POST;
|
||||
import static org.briarproject.briar.api.blog.MessageType.COMMENT;
|
||||
@@ -201,8 +199,8 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
|
||||
|
||||
// Get and Validate the Wrapped Message
|
||||
Group wGroup = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
|
||||
descriptor);
|
||||
Group wGroup = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
Blog wBlog = blogFactory.parseBlog(wGroup);
|
||||
BdfList wBodyList = BdfList.of(POST.getInt(), content, signature);
|
||||
byte[] wBody = clientHelper.toByteArray(wBodyList);
|
||||
@@ -264,8 +262,8 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
MessageId parentId = new MessageId(parentIdBytes);
|
||||
|
||||
// Get and Validate the Wrapped Comment
|
||||
Group wGroup = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
|
||||
descriptor);
|
||||
Group wGroup = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
BdfList wBodyList = BdfList.of(COMMENT.getInt(), comment, pOriginalId,
|
||||
oldId, signature);
|
||||
byte[] wBody = clientHelper.toByteArray(wBodyList);
|
||||
|
||||
@@ -12,10 +12,8 @@ import org.briarproject.briar.api.client.QueueMessageFactory;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.sync.MessageId.LABEL;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||
import static org.briarproject.briar.api.client.QueueMessage.MAX_QUEUE_MESSAGE_BODY_LENGTH;
|
||||
import static org.briarproject.briar.api.client.QueueMessage.QUEUE_MESSAGE_HEADER_LENGTH;
|
||||
@@ -47,9 +45,9 @@ class QueueMessageFactoryImpl implements QueueMessageFactory {
|
||||
byte[] bodyBytes = new byte[body.length + INT_64_BYTES];
|
||||
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, bodyBytes, 0,
|
||||
body.length + INT_64_BYTES);
|
||||
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
|
||||
groupId.getBytes(), timeBytes, bodyBytes);
|
||||
MessageId id = new MessageId(hash);
|
||||
MessageId id = new MessageId(
|
||||
crypto.hash(MessageId.LABEL, groupId.getBytes(), timeBytes,
|
||||
bodyBytes));
|
||||
return new QueueMessage(id, groupId, timestamp, queuePosition, raw);
|
||||
}
|
||||
|
||||
|
||||
@@ -496,7 +496,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener,
|
||||
}
|
||||
|
||||
private Group getLocalGroup() {
|
||||
return contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION);
|
||||
return contactGroupFactory.createLocalGroup(CLIENT_ID);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import javax.inject.Inject;
|
||||
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH;
|
||||
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
|
||||
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -53,8 +52,7 @@ class ForumFactoryImpl implements ForumFactory {
|
||||
try {
|
||||
BdfList forum = BdfList.of(name, salt);
|
||||
byte[] descriptor = clientHelper.toByteArray(forum);
|
||||
Group g = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
|
||||
descriptor);
|
||||
Group g = groupFactory.createGroup(CLIENT_ID, descriptor);
|
||||
return new Forum(g, name, salt);
|
||||
} catch (FormatException e) {
|
||||
throw new AssertionError(e);
|
||||
|
||||
@@ -50,11 +50,7 @@ import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.briar.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ACCEPT;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ADDED_CONTACT_ID;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ALICE_MAC_KEY_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ALICE_NONCE_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ANSWERED;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.BOB_MAC_KEY_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.BOB_NONCE_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.EXISTS;
|
||||
@@ -64,7 +60,6 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.INTR
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_KEY;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_ID;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.NAME;
|
||||
@@ -81,9 +76,7 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.REMO
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.SHARED_SECRET_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNATURE;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNING_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.STATE;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TASK;
|
||||
@@ -96,7 +89,7 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ABORT;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ACK;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -105,6 +98,9 @@ class IntroduceeManager {
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(IntroduceeManager.class.getName());
|
||||
|
||||
static final String SIGNING_LABEL_RESPONSE =
|
||||
CLIENT_ID.getString() + "/RESPONSE";
|
||||
|
||||
private final MessageSender messageSender;
|
||||
private final DatabaseComponent db;
|
||||
private final ClientHelper clientHelper;
|
||||
@@ -292,7 +288,8 @@ class IntroduceeManager {
|
||||
|
||||
@Nullable
|
||||
private BdfDictionary performTasks(Transaction txn,
|
||||
BdfDictionary localState) throws FormatException, DbException {
|
||||
BdfDictionary localState)
|
||||
throws FormatException, DbException {
|
||||
|
||||
if (!localState.containsKey(TASK) || localState.get(TASK) == NULL_VALUE)
|
||||
return null;
|
||||
@@ -309,21 +306,22 @@ class IntroduceeManager {
|
||||
}
|
||||
|
||||
// figure out who takes which role by comparing public keys
|
||||
byte[] ourPublicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY);
|
||||
byte[] theirPublicKeyBytes = localState.getRaw(E_PUBLIC_KEY);
|
||||
int comp = Bytes.COMPARATOR.compare(new Bytes(ourPublicKeyBytes),
|
||||
new Bytes(theirPublicKeyBytes));
|
||||
byte[] publicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY);
|
||||
byte[] theirEphemeralKey = localState.getRaw(E_PUBLIC_KEY);
|
||||
int comp = Bytes.COMPARATOR.compare(new Bytes(publicKeyBytes),
|
||||
new Bytes(theirEphemeralKey));
|
||||
boolean alice = comp < 0;
|
||||
|
||||
// get our local author
|
||||
LocalAuthor author = identityManager.getLocalAuthor(txn);
|
||||
|
||||
SecretKey secretKey;
|
||||
byte[] ourPrivateKeyBytes = localState.getRaw(OUR_PRIVATE_KEY);
|
||||
byte[] privateKeyBytes = localState.getRaw(OUR_PRIVATE_KEY);
|
||||
try {
|
||||
// derive secret master key
|
||||
secretKey = deriveSecretKey(ourPublicKeyBytes,
|
||||
ourPrivateKeyBytes, alice, theirPublicKeyBytes);
|
||||
secretKey =
|
||||
deriveSecretKey(publicKeyBytes, privateKeyBytes, alice,
|
||||
theirEphemeralKey);
|
||||
// derive MAC keys and nonces, sign our nonce and calculate MAC
|
||||
deriveMacKeysAndNonces(localState, author, secretKey, alice);
|
||||
} catch (GeneralSecurityException e) {
|
||||
@@ -412,41 +410,34 @@ class IntroduceeManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
private SecretKey deriveSecretKey(byte[] ourPublicKeyBytes,
|
||||
byte[] ourPrivateKeyBytes, boolean alice,
|
||||
byte[] theirPublicKeyBytes) throws GeneralSecurityException {
|
||||
private SecretKey deriveSecretKey(byte[] publicKeyBytes,
|
||||
byte[] privateKeyBytes, boolean alice, byte[] theirPublicKey)
|
||||
throws GeneralSecurityException {
|
||||
// parse the local ephemeral key pair
|
||||
KeyParser keyParser = cryptoComponent.getAgreementKeyParser();
|
||||
PublicKey ourPublicKey;
|
||||
PrivateKey ourPrivateKey;
|
||||
PublicKey publicKey;
|
||||
PrivateKey privateKey;
|
||||
try {
|
||||
ourPublicKey = keyParser.parsePublicKey(ourPublicKeyBytes);
|
||||
ourPrivateKey = keyParser.parsePrivateKey(ourPrivateKeyBytes);
|
||||
publicKey = keyParser.parsePublicKey(publicKeyBytes);
|
||||
privateKey = keyParser.parsePrivateKey(privateKeyBytes);
|
||||
} catch (GeneralSecurityException e) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
throw new RuntimeException("Our own ephemeral key is invalid");
|
||||
}
|
||||
KeyPair ourKeyPair = new KeyPair(ourPublicKey, ourPrivateKey);
|
||||
PublicKey theirPublicKey =
|
||||
keyParser.parsePublicKey(theirPublicKeyBytes);
|
||||
KeyPair keyPair = new KeyPair(publicKey, privateKey);
|
||||
|
||||
// The shared secret is derived from the local ephemeral key pair
|
||||
// The master secret is derived from the local ephemeral key pair
|
||||
// and the remote ephemeral public key
|
||||
byte[][] inputs = {
|
||||
new byte[] {CLIENT_VERSION},
|
||||
alice ? ourPublicKeyBytes : theirPublicKeyBytes,
|
||||
alice ? theirPublicKeyBytes : ourPublicKeyBytes
|
||||
};
|
||||
return cryptoComponent.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||
theirPublicKey, ourKeyPair, inputs);
|
||||
return cryptoComponent
|
||||
.deriveMasterSecret(theirPublicKey, keyPair, alice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives nonces, signs our nonce and calculates MAC
|
||||
* <p>
|
||||
* Derives two nonces and two MAC keys from the shared secret key.
|
||||
* Derives two nonces and two mac keys from the secret master key.
|
||||
* The other introducee's nonce and MAC key are added to the localState.
|
||||
* <p>
|
||||
* Our nonce is signed with the local author's long-term private key.
|
||||
@@ -457,24 +448,21 @@ class IntroduceeManager {
|
||||
private void deriveMacKeysAndNonces(BdfDictionary localState,
|
||||
LocalAuthor author, SecretKey secretKey, boolean alice)
|
||||
throws FormatException, GeneralSecurityException {
|
||||
// Derive two nonces and two MAC keys from the shared secret key
|
||||
String ourNonceLabel = alice ? ALICE_NONCE_LABEL : BOB_NONCE_LABEL;
|
||||
String theirNonceLabel = alice ? BOB_NONCE_LABEL : ALICE_NONCE_LABEL;
|
||||
byte[] ourNonce = cryptoComponent.mac(ourNonceLabel, secretKey);
|
||||
byte[] theirNonce = cryptoComponent.mac(theirNonceLabel, secretKey);
|
||||
String ourKeyLabel = alice ? ALICE_MAC_KEY_LABEL : BOB_MAC_KEY_LABEL;
|
||||
String theirKeyLabel = alice ? BOB_MAC_KEY_LABEL : ALICE_MAC_KEY_LABEL;
|
||||
SecretKey ourMacKey = cryptoComponent.deriveKey(ourKeyLabel, secretKey);
|
||||
SecretKey theirMacKey =
|
||||
cryptoComponent.deriveKey(theirKeyLabel, secretKey);
|
||||
// Derive two nonces and a MAC key from the secret master key
|
||||
byte[] ourNonce =
|
||||
cryptoComponent.deriveSignatureNonce(secretKey, alice);
|
||||
byte[] theirNonce =
|
||||
cryptoComponent.deriveSignatureNonce(secretKey, !alice);
|
||||
SecretKey macKey = cryptoComponent.deriveMacKey(secretKey, alice);
|
||||
SecretKey theirMacKey = cryptoComponent.deriveMacKey(secretKey, !alice);
|
||||
|
||||
// Save the other nonce and MAC key for the verification
|
||||
localState.put(NONCE, theirNonce);
|
||||
localState.put(MAC_KEY, theirMacKey.getBytes());
|
||||
|
||||
// Sign our nonce with our long-term identity public key
|
||||
byte[] sig = cryptoComponent.sign(SIGNING_LABEL, ourNonce,
|
||||
author.getPrivateKey());
|
||||
byte[] sig = cryptoComponent
|
||||
.sign(SIGNING_LABEL_RESPONSE, ourNonce, author.getPrivateKey());
|
||||
|
||||
// Calculate a MAC over identity public key, ephemeral public key,
|
||||
// transport properties and timestamp.
|
||||
@@ -484,7 +472,7 @@ class IntroduceeManager {
|
||||
BdfList toMacList = BdfList.of(author.getPublicKey(),
|
||||
publicKeyBytes, tp, ourTime);
|
||||
byte[] toMac = clientHelper.toByteArray(toMacList);
|
||||
byte[] mac = cryptoComponent.mac(MAC_LABEL, ourMacKey, toMac);
|
||||
byte[] mac = cryptoComponent.mac(macKey, toMac);
|
||||
|
||||
// Add MAC and signature to localState, so it can be included in ACK
|
||||
localState.put(OUR_MAC, mac);
|
||||
@@ -498,7 +486,7 @@ class IntroduceeManager {
|
||||
byte[] key = localState.getRaw(PUBLIC_KEY);
|
||||
|
||||
// Verify the signature
|
||||
if (!cryptoComponent.verify(SIGNING_LABEL, nonce, key, sig)) {
|
||||
if (!cryptoComponent.verify(SIGNING_LABEL_RESPONSE, nonce, key, sig)) {
|
||||
LOG.warning("Invalid nonce signature in ACK");
|
||||
throw new GeneralSecurityException();
|
||||
}
|
||||
@@ -518,7 +506,7 @@ class IntroduceeManager {
|
||||
long timestamp = localState.getLong(TIME);
|
||||
BdfList toMacList = BdfList.of(pubKey, ePubKey, tp, timestamp);
|
||||
byte[] toMac = clientHelper.toByteArray(toMacList);
|
||||
byte[] calculatedMac = cryptoComponent.mac(MAC_LABEL, macKey, toMac);
|
||||
byte[] calculatedMac = cryptoComponent.mac(macKey, toMac);
|
||||
if (!Arrays.equals(mac, calculatedMac)) {
|
||||
LOG.warning("Received ACK with invalid MAC");
|
||||
throw new GeneralSecurityException();
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.briarproject.bramble.api.sync.Group;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION;
|
||||
|
||||
class IntroductionGroupFactory {
|
||||
|
||||
@@ -17,16 +16,14 @@ class IntroductionGroupFactory {
|
||||
@Inject
|
||||
IntroductionGroupFactory(ContactGroupFactory contactGroupFactory) {
|
||||
this.contactGroupFactory = contactGroupFactory;
|
||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
|
||||
CLIENT_VERSION);
|
||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID);
|
||||
}
|
||||
|
||||
Group createIntroductionGroup(Contact c) {
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, c);
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID, c);
|
||||
}
|
||||
|
||||
Group createLocalGroup() {
|
||||
public Group createLocalGroup() {
|
||||
return localGroup;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,8 +79,7 @@ class MessagingManagerImpl extends ConversationClientImpl
|
||||
|
||||
@Override
|
||||
public Group getContactGroup(Contact c) {
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, c);
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user