mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 11:19:04 +01:00
Compare commits
51 Commits
maintenanc
...
elliptic-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b38c33bf58 | ||
|
|
5bb00597ef | ||
|
|
5d528fce74 | ||
|
|
c80edc99b2 | ||
|
|
33378d9920 | ||
|
|
85a6e394b9 | ||
|
|
f2f98f28a3 | ||
|
|
d92e042971 | ||
|
|
6d6e47409f | ||
|
|
0084e51263 | ||
|
|
32e0b39771 | ||
|
|
7bb51f77ec | ||
|
|
c777a57a7d | ||
|
|
def5966767 | ||
|
|
14b18e9d42 | ||
|
|
fcff8d92f3 | ||
|
|
ea0e00f4ac | ||
|
|
f199105f6c | ||
|
|
b23c0b599b | ||
|
|
0327d4f38a | ||
|
|
4397a45519 | ||
|
|
365e159539 | ||
|
|
8171dd8bc9 | ||
|
|
c4beb60c22 | ||
|
|
4b88f0d9f1 | ||
|
|
116419f505 | ||
|
|
87b2624aa8 | ||
|
|
71fe6f3148 | ||
|
|
21df6cb809 | ||
|
|
1f0c385a5c | ||
|
|
986ea05fb2 | ||
|
|
90e395506f | ||
|
|
cf54360a93 | ||
|
|
a5d4ea4477 | ||
|
|
030b52261d | ||
|
|
a50e13c2e3 | ||
|
|
c8326103b4 | ||
|
|
0f2beee813 | ||
|
|
d2348a4e7d | ||
|
|
cc87e6fd1f | ||
|
|
1843aea2a7 | ||
|
|
9f7021acd3 | ||
|
|
ddea031cbf | ||
|
|
f0d8532f71 | ||
|
|
4883d157dc | ||
|
|
a1bec1e927 | ||
|
|
37d4d79c64 | ||
|
|
05bc3f6a71 | ||
|
|
8b3960781a | ||
|
|
f3de4f53c5 | ||
|
|
166fc2948c |
@@ -12,8 +12,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
versionCode 1612
|
versionCode 1700
|
||||||
versionName "0.16.12"
|
versionName "0.17.0"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +43,7 @@ dependencyVerification {
|
|||||||
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
||||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
|
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||||
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||||
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128-runtime.jar:e357a0f1d573c2f702a273992b1b6cb661734f66311854efb3778a888515c5b5',
|
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128-runtime.jar:e357a0f1d573c2f702a273992b1b6cb661734f66311854efb3778a888515c5b5',
|
||||||
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128.jar:47b4bec6df11a1118da3953da8b9fa1e7079d6fec857faa1a3cf912e53a6fd4e',
|
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128.jar:47b4bec6df11a1118da3953da8b9fa1e7079d6fec857faa1a3cf912e53a6fd4e',
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
-dontwarn dagger.**
|
-dontwarn dagger.**
|
||||||
-dontnote dagger.**
|
-dontnote dagger.**
|
||||||
|
|
||||||
|
-keep class net.i2p.crypto.eddsa.** { *; }
|
||||||
|
|
||||||
-dontwarn sun.misc.Unsafe
|
-dontwarn sun.misc.Unsafe
|
||||||
-dontnote com.google.common.**
|
-dontnote com.google.common.**
|
||||||
|
|
||||||
|
|||||||
@@ -12,18 +12,19 @@ public interface ContactGroupFactory {
|
|||||||
/**
|
/**
|
||||||
* Creates a group that is not shared with any contacts.
|
* Creates a group that is not shared with any contacts.
|
||||||
*/
|
*/
|
||||||
Group createLocalGroup(ClientId clientId);
|
Group createLocalGroup(ClientId clientId, int clientVersion);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a group for the given client to share with the given contact.
|
* Creates a group for the given client to share with the given contact.
|
||||||
*/
|
*/
|
||||||
Group createContactGroup(ClientId clientId, Contact contact);
|
Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
|
Contact contact);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a group for the given client to share between the given authors
|
* Creates a group for the given client to share between the given authors
|
||||||
* identified by their AuthorIds.
|
* identified by their AuthorIds.
|
||||||
*/
|
*/
|
||||||
Group createContactGroup(ClientId clientId, AuthorId authorId1,
|
Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
AuthorId authorId2);
|
AuthorId authorId1, AuthorId authorId2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,32 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface ContactExchangeTask {
|
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.
|
* Exchanges contact information with a remote peer.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package org.briarproject.bramble.api.crypto;
|
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.GeneralSecurityException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
@@ -20,156 +17,98 @@ public interface CryptoComponent {
|
|||||||
|
|
||||||
KeyParser getSignatureKeyParser();
|
KeyParser getSignatureKeyParser();
|
||||||
|
|
||||||
|
KeyPair generateEdKeyPair();
|
||||||
|
|
||||||
|
KeyParser getEdKeyParser();
|
||||||
|
|
||||||
KeyParser getMessageKeyParser();
|
KeyParser getMessageKeyParser();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a stream header key from the given master secret.
|
* Derives another secret key from the given secret key.
|
||||||
* @param alice whether the key is for use by Alice or Bob.
|
|
||||||
*/
|
|
||||||
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
|
* @param label a namespaced label indicating the purpose of the derived
|
||||||
* @return the commitment to the provided public key.
|
* key, to prevent it from being repurposed or colliding with a key derived
|
||||||
|
* for another purpose
|
||||||
*/
|
*/
|
||||||
byte[] deriveKeyCommitment(byte[] publicKey);
|
SecretKey deriveKey(String label, SecretKey k, byte[]... inputs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a common shared secret from two public keys and one of the
|
* Derives a common shared secret from two public keys and one of the
|
||||||
* corresponding private keys.
|
* corresponding private keys.
|
||||||
* <p/>
|
|
||||||
* Part of BQP.
|
|
||||||
*
|
*
|
||||||
* @param theirPublicKey the ephemeral public key of the remote party
|
* @param label a namespaced label indicating the purpose of this shared
|
||||||
* @param ourKeyPair our ephemeral keypair
|
* secret, to prevent it from being repurposed or colliding with a shared
|
||||||
* @param alice true if ourKeyPair belongs to Alice
|
* secret derived for another purpose
|
||||||
|
* @param theirPublicKey the public key of the remote party
|
||||||
|
* @param ourKeyPair the key pair of the local party
|
||||||
* @return the shared secret
|
* @return the shared secret
|
||||||
* @throws GeneralSecurityException
|
|
||||||
*/
|
*/
|
||||||
SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
|
SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||||
boolean alice) throws GeneralSecurityException;
|
KeyPair ourKeyPair, byte[]... inputs)
|
||||||
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives the content of a confirmation record.
|
* Signs the given byte[] with the given ECDSA private key.
|
||||||
* <p/>
|
|
||||||
* Part of BQP.
|
|
||||||
*
|
*
|
||||||
* @param sharedSecret the common shared secret
|
* @param label a namespaced label indicating the purpose of this
|
||||||
* @param theirPayload the commit payload from the remote party
|
* signature, to prevent it from being repurposed or colliding with a
|
||||||
* @param ourPayload the commit payload we sent
|
* signature created for another purpose
|
||||||
* @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
|
|
||||||
* @throws GeneralSecurityException
|
|
||||||
*/
|
|
||||||
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 PrivateKey.
|
|
||||||
*
|
|
||||||
* @param label A label specific to this signature
|
|
||||||
* to ensure that the signature cannot be repurposed
|
|
||||||
*/
|
*/
|
||||||
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies that the given signature is valid for the signedData
|
* Signs the given byte[] with the given Ed25519 private key.
|
||||||
* and the given publicKey.
|
|
||||||
*
|
*
|
||||||
* @param label A label that was specific to this signature
|
* @param label A label specific to this signature
|
||||||
* to ensure that the signature cannot be repurposed
|
* to ensure that the signature cannot be repurposed
|
||||||
|
*/
|
||||||
|
byte[] signEd(String label, byte[] toSign, byte[] privateKey)
|
||||||
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
* @return true if the signature was valid, false otherwise.
|
* @return true if the signature was valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean verify(String label, byte[] signedData, byte[] publicKey,
|
boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||||
byte[] signature) throws GeneralSecurityException;
|
byte[] signature) throws GeneralSecurityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the given signature is valid for the signed data
|
||||||
|
* and the given Ed25519 public key.
|
||||||
|
*
|
||||||
|
* @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 verifyEd(String label, byte[] signedData, byte[] publicKey,
|
||||||
|
byte[] signature) throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the hash of the given inputs. The inputs are unambiguously
|
* Returns the hash of the given inputs. The inputs are unambiguously
|
||||||
* combined by prefixing each input with its length.
|
* combined by prefixing each input with its length.
|
||||||
*
|
*
|
||||||
* @param label A label specific to this hash to ensure that hashes
|
* @param label a namespaced label indicating the purpose of this hash, to
|
||||||
* calculated for distinct purposes don't collide.
|
* prevent it from being repurposed or colliding with a hash created for
|
||||||
|
* another purpose
|
||||||
*/
|
*/
|
||||||
byte[] hash(String label, byte[]... inputs);
|
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
|
* Returns a message authentication code with the given key over the
|
||||||
* given inputs. The inputs are unambiguously combined by prefixing each
|
* given inputs. The inputs are unambiguously combined by prefixing each
|
||||||
* input with its length.
|
* 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(SecretKey macKey, byte[]... inputs);
|
byte[] mac(String label, SecretKey macKey, byte[]... inputs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypts and authenticates the given plaintext so it can be written to
|
* Encrypts and authenticates the given plaintext so it can be written to
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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.
|
* 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) {
|
public AuthorId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ public interface KeyAgreementConstants {
|
|||||||
/**
|
/**
|
||||||
* The current version of the BQP protocol.
|
* The current version of the BQP protocol.
|
||||||
*/
|
*/
|
||||||
byte PROTOCOL_VERSION = 2;
|
byte PROTOCOL_VERSION = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the record header in bytes.
|
* The length of the record header in bytes.
|
||||||
@@ -22,7 +22,10 @@ public interface KeyAgreementConstants {
|
|||||||
*/
|
*/
|
||||||
int COMMIT_LENGTH = 16;
|
int COMMIT_LENGTH = 16;
|
||||||
|
|
||||||
long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds
|
/**
|
||||||
|
* The connection timeout in milliseconds.
|
||||||
|
*/
|
||||||
|
long CONNECTION_TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The transport identifier for Bluetooth.
|
* The transport identifier for Bluetooth.
|
||||||
@@ -33,4 +36,16 @@ public interface KeyAgreementConstants {
|
|||||||
* The transport identifier for LAN.
|
* The transport identifier for LAN.
|
||||||
*/
|
*/
|
||||||
int TRANSPORT_ID_LAN = 1;
|
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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
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,6 +17,11 @@ public interface TransportPropertyManager {
|
|||||||
*/
|
*/
|
||||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties");
|
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
|
* Stores the given properties received while adding a contact - they will
|
||||||
* be superseded by any properties synced from the contact.
|
* be superseded by any properties synced from the contact.
|
||||||
|
|||||||
@@ -36,4 +36,8 @@ public class ClientId implements Comparable<ClientId> {
|
|||||||
return id.hashCode();
|
return id.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
public interface GroupFactory {
|
public interface GroupFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a group with the given client ID and descriptor.
|
* Creates a group with the given client ID, client version and descriptor.
|
||||||
*/
|
*/
|
||||||
Group createGroup(ClientId c, byte[] descriptor);
|
Group createGroup(ClientId c, int clientVersion, byte[] descriptor);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class GroupId extends UniqueId {
|
|||||||
/**
|
/**
|
||||||
* Label for hashing groups to calculate their identifiers.
|
* 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) {
|
public GroupId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class MessageId extends UniqueId {
|
|||||||
/**
|
/**
|
||||||
* Label for hashing messages to calculate their identifiers.
|
* 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) {
|
public MessageId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public interface TransportConstants {
|
|||||||
/**
|
/**
|
||||||
* The current version of the transport protocol.
|
* The current version of the transport protocol.
|
||||||
*/
|
*/
|
||||||
int PROTOCOL_VERSION = 3;
|
int PROTOCOL_VERSION = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the pseudo-random tag in bytes.
|
* The length of the pseudo-random tag in bytes.
|
||||||
@@ -80,4 +80,32 @@ public interface TransportConstants {
|
|||||||
* The size of the reordering window.
|
* The size of the reordering window.
|
||||||
*/
|
*/
|
||||||
int REORDERING_WINDOW_SIZE = 32;
|
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,18 +9,21 @@ apply plugin: 'witness'
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-api', configuration: 'default')
|
implementation project(path: ':bramble-api', configuration: 'default')
|
||||||
implementation 'com.madgag.spongycastle:core:1.58.0.0'
|
implementation 'com.madgag.spongycastle:core:1.58.0.0'
|
||||||
implementation 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
|
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||||
implementation 'org.bitlet:weupnp:0.1.4'
|
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:2.0.2'
|
||||||
|
|
||||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
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 'junit:junit:4.12'
|
||||||
testImplementation "org.jmock:jmock:2.8.2"
|
testImplementation "org.jmock:jmock:2.8.2"
|
||||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||||
|
testImplementation "org.whispersystems:curve25519-java:0.4.1"
|
||||||
|
|
||||||
testApt 'com.google.dagger:dagger-compiler:2.0.2'
|
testApt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
}
|
}
|
||||||
@@ -37,18 +40,21 @@ dependencyVerification {
|
|||||||
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||||
|
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||||
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
'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-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||||
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
'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-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-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-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||||
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||||
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||||
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
'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,23 +32,25 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createLocalGroup(ClientId clientId) {
|
public Group createLocalGroup(ClientId clientId, int clientVersion) {
|
||||||
return groupFactory.createGroup(clientId, LOCAL_GROUP_DESCRIPTOR);
|
return groupFactory.createGroup(clientId, clientVersion,
|
||||||
|
LOCAL_GROUP_DESCRIPTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createContactGroup(ClientId clientId, Contact contact) {
|
public Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
|
Contact contact) {
|
||||||
AuthorId local = contact.getLocalAuthorId();
|
AuthorId local = contact.getLocalAuthorId();
|
||||||
AuthorId remote = contact.getAuthor().getId();
|
AuthorId remote = contact.getAuthor().getId();
|
||||||
byte[] descriptor = createGroupDescriptor(local, remote);
|
byte[] descriptor = createGroupDescriptor(local, remote);
|
||||||
return groupFactory.createGroup(clientId, descriptor);
|
return groupFactory.createGroup(clientId, clientVersion, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createContactGroup(ClientId clientId, AuthorId authorId1,
|
public Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
AuthorId authorId2) {
|
AuthorId authorId1, AuthorId authorId2) {
|
||||||
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
|
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
|
||||||
return groupFactory.createGroup(clientId, descriptor);
|
return groupFactory.createGroup(clientId, clientVersion, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
|
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
|
||||||
|
|||||||
@@ -141,8 +141,10 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Derive the header keys for the transport streams
|
// Derive the header keys for the transport streams
|
||||||
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
|
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL,
|
||||||
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
|
masterSecret, new byte[] {PROTOCOL_VERSION});
|
||||||
|
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret,
|
||||||
|
new byte[] {PROTOCOL_VERSION});
|
||||||
|
|
||||||
// Create the readers
|
// Create the readers
|
||||||
InputStream streamReader =
|
InputStream streamReader =
|
||||||
@@ -156,8 +158,10 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
||||||
|
|
||||||
// Derive the nonces to be signed
|
// Derive the nonces to be signed
|
||||||
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
|
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
|
||||||
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
|
new byte[] {PROTOCOL_VERSION});
|
||||||
|
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
|
||||||
|
new byte[] {PROTOCOL_VERSION});
|
||||||
|
|
||||||
// Exchange pseudonyms, signed nonces, and timestamps
|
// Exchange pseudonyms, signed nonces, and timestamps
|
||||||
long localTimestamp = clock.currentTimeMillis();
|
long localTimestamp = clock.currentTimeMillis();
|
||||||
@@ -196,8 +200,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Add the contact
|
// Add the contact
|
||||||
ContactId contactId = addContact(remoteAuthor, masterSecret,
|
ContactId contactId = addContact(remoteAuthor, timestamp,
|
||||||
timestamp, alice, remoteProperties);
|
remoteProperties);
|
||||||
// Reuse the connection as a transport connection
|
// Reuse the connection as a transport connection
|
||||||
connectionManager.manageOutgoingConnection(contactId, transportId,
|
connectionManager.manageOutgoingConnection(contactId, transportId,
|
||||||
conn);
|
conn);
|
||||||
@@ -294,15 +298,15 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
return remote;
|
return remote;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContactId addContact(Author remoteAuthor, SecretKey master,
|
private ContactId addContact(Author remoteAuthor, long timestamp,
|
||||||
long timestamp, boolean alice,
|
|
||||||
Map<TransportId, TransportProperties> remoteProperties)
|
Map<TransportId, TransportProperties> remoteProperties)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
ContactId contactId;
|
ContactId contactId;
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
contactId = contactManager.addContact(txn, remoteAuthor,
|
contactId = contactManager.addContact(txn, remoteAuthor,
|
||||||
localAuthor.getId(), master, timestamp, alice, true, true);
|
localAuthor.getId(), masterSecret, timestamp, alice,
|
||||||
|
true, true);
|
||||||
transportPropertyManager.addRemoteProperties(txn, contactId,
|
transportPropertyManager.addRemoteProperties(txn, contactId,
|
||||||
remoteProperties);
|
remoteProperties);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -312,8 +316,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
return contactId;
|
return contactId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(DuplexTransportConnection conn,
|
private void tryToClose(DuplexTransportConnection conn, boolean exception) {
|
||||||
boolean exception) {
|
|
||||||
try {
|
try {
|
||||||
LOG.info("Closing connection");
|
LOG.info("Closing connection");
|
||||||
conn.getReader().dispose(exception, true);
|
conn.getReader().dispose(exception, true);
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||||
|
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||||
|
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
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.ByteUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
@@ -26,7 +26,6 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
|||||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
@@ -40,14 +39,8 @@ import java.util.logging.Logger;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static org.briarproject.bramble.api.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.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_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 {
|
class CryptoComponentImpl implements CryptoComponent {
|
||||||
|
|
||||||
@@ -56,49 +49,19 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
private static final int AGREEMENT_KEY_PAIR_BITS = 256;
|
private static final int AGREEMENT_KEY_PAIR_BITS = 256;
|
||||||
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
||||||
|
private static final int ED_KEY_PAIR_BITS = 256;
|
||||||
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
||||||
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
||||||
private static final int PBKDF_TARGET_MILLIS = 500;
|
private static final int PBKDF_TARGET_MILLIS = 500;
|
||||||
private static final int PBKDF_SAMPLES = 30;
|
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 SecureRandom secureRandom;
|
||||||
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
||||||
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
||||||
private final KeyParser agreementKeyParser, signatureKeyParser;
|
private final KeyParser agreementKeyParser, signatureKeyParser;
|
||||||
private final MessageEncrypter messageEncrypter;
|
private final MessageEncrypter messageEncrypter;
|
||||||
|
private final KeyPairGenerator edKeyPairGenerator;
|
||||||
|
private final KeyParser edKeyParser;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
||||||
@@ -132,6 +95,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
signatureKeyParser = new Sec1KeyParser(PARAMETERS,
|
signatureKeyParser = new Sec1KeyParser(PARAMETERS,
|
||||||
SIGNATURE_KEY_PAIR_BITS);
|
SIGNATURE_KEY_PAIR_BITS);
|
||||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
messageEncrypter = new MessageEncrypter(secureRandom);
|
||||||
|
edKeyPairGenerator = new KeyPairGenerator();
|
||||||
|
edKeyPairGenerator.initialize(ED_KEY_PAIR_BITS, secureRandom);
|
||||||
|
edKeyParser = new EdKeyParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
@@ -190,6 +156,21 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyPair generateEdKeyPair() {
|
||||||
|
java.security.KeyPair keyPair = edKeyPairGenerator.generateKeyPair();
|
||||||
|
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
|
||||||
|
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
|
||||||
|
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
|
||||||
|
PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed());
|
||||||
|
return new KeyPair(publicKey, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyParser getEdKeyParser() {
|
||||||
|
return edKeyParser;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyPair generateAgreementKeyPair() {
|
public KeyPair generateAgreementKeyPair() {
|
||||||
AsymmetricCipherKeyPair keyPair =
|
AsymmetricCipherKeyPair keyPair =
|
||||||
@@ -238,197 +219,63 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey deriveHeaderKey(SecretKey master,
|
public SecretKey deriveKey(String label, SecretKey k, byte[]... inputs) {
|
||||||
boolean alice) {
|
byte[] mac = mac(label, k, inputs);
|
||||||
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
|
if (mac.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||||
|
return new SecretKey(mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey deriveMacKey(SecretKey master, boolean alice) {
|
public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||||
return new SecretKey(macKdf(master, alice ? A_MAC : B_MAC));
|
KeyPair ourKeyPair, byte[]... inputs)
|
||||||
}
|
throws GeneralSecurityException {
|
||||||
|
|
||||||
@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();
|
PrivateKey ourPriv = ourKeyPair.getPrivate();
|
||||||
PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
|
byte[][] hashInputs = new byte[inputs.length + 1][];
|
||||||
byte[] raw = performRawKeyAgreement(ourPriv, theirPub);
|
hashInputs[0] = performRawKeyAgreement(ourPriv, theirPublicKey);
|
||||||
byte[] alicePub, bobPub;
|
System.arraycopy(inputs, 0, hashInputs, 1, inputs.length);
|
||||||
if (alice) {
|
byte[] hash = hash(label, hashInputs);
|
||||||
alicePub = ourKeyPair.getPublic().getEncoded();
|
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||||
bobPub = theirPublicKey;
|
return new SecretKey(hash);
|
||||||
} 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
|
@Override
|
||||||
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
Signature signature = new SignatureImpl(secureRandom);
|
return sign(new SignatureImpl(secureRandom), signatureKeyParser, label,
|
||||||
KeyParser keyParser = getSignatureKeyParser();
|
toSign, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] signEd(String label, byte[] toSign, byte[] privateKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
return sign(new EdSignature(), edKeyParser, label, toSign, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] sign(Signature sig, KeyParser keyParser, String label,
|
||||||
|
byte[] toSign, byte[] privateKey) throws GeneralSecurityException {
|
||||||
PrivateKey key = keyParser.parsePrivateKey(privateKey);
|
PrivateKey key = keyParser.parsePrivateKey(privateKey);
|
||||||
signature.initSign(key);
|
sig.initSign(key);
|
||||||
updateSignature(signature, label, toSign);
|
updateSignature(sig, label, toSign);
|
||||||
return signature.sign();
|
return sig.sign();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(String label, byte[] signedData, byte[] publicKey,
|
public boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||||
byte[] signature) throws GeneralSecurityException {
|
byte[] signature) throws GeneralSecurityException {
|
||||||
Signature sig = new SignatureImpl(secureRandom);
|
return verify(new SignatureImpl(secureRandom), signatureKeyParser,
|
||||||
KeyParser keyParser = getSignatureKeyParser();
|
label, signedData, publicKey, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verifyEd(String label, byte[] signedData, byte[] publicKey,
|
||||||
|
byte[] signature) throws GeneralSecurityException {
|
||||||
|
return verify(new EdSignature(), edKeyParser, label, signedData,
|
||||||
|
publicKey, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verify(Signature sig, KeyParser keyParser, String label,
|
||||||
|
byte[] signedData, byte[] publicKey, byte[] signature)
|
||||||
|
throws GeneralSecurityException {
|
||||||
PublicKey key = keyParser.parsePublicKey(publicKey);
|
PublicKey key = keyParser.parsePublicKey(publicKey);
|
||||||
sig.initVerify(key);
|
sig.initVerify(key);
|
||||||
updateSignature(sig, label, signedData);
|
updateSignature(sig, label, signedData);
|
||||||
@@ -436,7 +283,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSignature(Signature signature, String label,
|
private void updateSignature(Signature signature, String label,
|
||||||
byte[] toSign) {
|
byte[] toSign) throws GeneralSecurityException {
|
||||||
byte[] labelBytes = StringUtils.toUtf8(label);
|
byte[] labelBytes = StringUtils.toUtf8(label);
|
||||||
byte[] length = new byte[INT_32_BYTES];
|
byte[] length = new byte[INT_32_BYTES];
|
||||||
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
||||||
@@ -466,14 +313,13 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getHashLength() {
|
public byte[] mac(String label, SecretKey macKey, byte[]... inputs) {
|
||||||
return HASH_SIZE;
|
byte[] labelBytes = StringUtils.toUtf8(label);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] mac(SecretKey macKey, byte[]... inputs) {
|
|
||||||
Digest mac = new Blake2sDigest(macKey.getBytes());
|
Digest mac = new Blake2sDigest(macKey.getBytes());
|
||||||
byte[] length = new byte[INT_32_BYTES];
|
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) {
|
for (byte[] input : inputs) {
|
||||||
ByteUtils.writeUint32(input.length, length, 0);
|
ByteUtils.writeUint32(input.length, length, 0);
|
||||||
mac.update(length, 0, length.length);
|
mac.update(length, 0, length.length);
|
||||||
@@ -565,30 +411,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return AsciiArmour.wrap(b, lineLength);
|
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
|
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
|
||||||
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
||||||
byte[] utf8 = StringUtils.toUtf8(password);
|
byte[] utf8 = StringUtils.toUtf8(password);
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.TimeLoggingExecutor;
|
import org.briarproject.bramble.TimeLoggingExecutor;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
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.PasswordStrengthEstimator;
|
||||||
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
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.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
@@ -74,6 +76,12 @@ public class CryptoModule {
|
|||||||
return new PasswordStrengthEstimatorImpl();
|
return new PasswordStrengthEstimatorImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
TransportCrypto provideTransportCrypto(
|
||||||
|
TransportCryptoImpl transportCrypto) {
|
||||||
|
return transportCrypto;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
StreamDecrypterFactory provideStreamDecrypterFactory(
|
StreamDecrypterFactory provideStreamDecrypterFactory(
|
||||||
Provider<AuthenticatedCipher> cipherProvider) {
|
Provider<AuthenticatedCipher> cipherProvider) {
|
||||||
@@ -81,9 +89,17 @@ public class CryptoModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
StreamEncrypterFactory provideStreamEncrypterFactory(CryptoComponent crypto,
|
StreamEncrypterFactory provideStreamEncrypterFactory(
|
||||||
|
CryptoComponent crypto, TransportCrypto transportCrypto,
|
||||||
Provider<AuthenticatedCipher> cipherProvider) {
|
Provider<AuthenticatedCipher> cipherProvider) {
|
||||||
return new StreamEncrypterFactoryImpl(crypto, cipherProvider);
|
return new StreamEncrypterFactoryImpl(crypto, transportCrypto,
|
||||||
|
cipherProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
KeyAgreementCrypto provideKeyAgreementCrypto(
|
||||||
|
KeyAgreementCryptoImpl keyAgreementCrypto) {
|
||||||
|
return keyAgreementCrypto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class EdKeyParser implements KeyParser {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey parsePublicKey(byte[] encodedKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||||
|
return new EdPublicKey(encodedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||||
|
return new EdPrivateKey(encodedKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class EdPrivateKey extends Bytes implements PrivateKey {
|
||||||
|
|
||||||
|
EdPrivateKey(byte[] bytes) {
|
||||||
|
super(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getEncoded() {
|
||||||
|
return getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class EdPublicKey extends Bytes implements PublicKey {
|
||||||
|
|
||||||
|
EdPublicKey(byte[] bytes) {
|
||||||
|
super(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getEncoded() {
|
||||||
|
return getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||||
|
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||||
|
import net.i2p.crypto.eddsa.EdDSASecurityProvider;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Provider;
|
||||||
|
|
||||||
|
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class EdSignature implements Signature {
|
||||||
|
|
||||||
|
private static final Provider PROVIDER = new EdDSASecurityProvider();
|
||||||
|
|
||||||
|
private static final EdDSANamedCurveSpec CURVE_SPEC =
|
||||||
|
EdDSANamedCurveTable.getByName("Ed25519");
|
||||||
|
|
||||||
|
private final java.security.Signature signature;
|
||||||
|
|
||||||
|
EdSignature() {
|
||||||
|
try {
|
||||||
|
signature = java.security.Signature
|
||||||
|
.getInstance(SIGNATURE_ALGORITHM, PROVIDER);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initSign(PrivateKey k) throws GeneralSecurityException {
|
||||||
|
if (!(k instanceof EdPrivateKey))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
EdDSAPrivateKey privateKey = new EdDSAPrivateKey(
|
||||||
|
new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC));
|
||||||
|
signature.initSign(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initVerify(PublicKey k) throws GeneralSecurityException {
|
||||||
|
if (!(k instanceof EdPublicKey))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
EdDSAPublicKey publicKey = new EdDSAPublicKey(
|
||||||
|
new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC));
|
||||||
|
signature.initVerify(publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte b) throws GeneralSecurityException {
|
||||||
|
signature.update(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] b) throws GeneralSecurityException {
|
||||||
|
signature.update(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] b, int off, int len)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
signature.update(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] sign() throws GeneralSecurityException {
|
||||||
|
return signature.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(byte[] sig) throws GeneralSecurityException {
|
||||||
|
return signature.verify(sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,25 +22,25 @@ interface Signature {
|
|||||||
/**
|
/**
|
||||||
* @see {@link java.security.Signature#update(byte)}
|
* @see {@link java.security.Signature#update(byte)}
|
||||||
*/
|
*/
|
||||||
void update(byte b);
|
void update(byte b) throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see {@link java.security.Signature#update(byte[])}
|
* @see {@link java.security.Signature#update(byte[])}
|
||||||
*/
|
*/
|
||||||
void update(byte[] b);
|
void update(byte[] b) throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see {@link java.security.Signature#update(byte[], int, int)}
|
* @see {@link java.security.Signature#update(byte[], int, int)}
|
||||||
*/
|
*/
|
||||||
void update(byte[] b, int off, int len);
|
void update(byte[] b, int off, int len) throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see {@link java.security.Signature#sign()}
|
* @see {@link java.security.Signature#sign()}
|
||||||
*/
|
*/
|
||||||
byte[] sign();
|
byte[] sign() throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see {@link java.security.Signature#verify(byte[])}
|
* @see {@link java.security.Signature#verify(byte[])}
|
||||||
*/
|
*/
|
||||||
boolean verify(byte[] signature);
|
boolean verify(byte[] signature) throws GeneralSecurityException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
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.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
|
|
||||||
@@ -22,12 +23,15 @@ import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENG
|
|||||||
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
|
private final TransportCrypto transportCrypto;
|
||||||
private final Provider<AuthenticatedCipher> cipherProvider;
|
private final Provider<AuthenticatedCipher> cipherProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
StreamEncrypterFactoryImpl(CryptoComponent crypto,
|
StreamEncrypterFactoryImpl(CryptoComponent crypto,
|
||||||
|
TransportCrypto transportCrypto,
|
||||||
Provider<AuthenticatedCipher> cipherProvider) {
|
Provider<AuthenticatedCipher> cipherProvider) {
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
|
this.transportCrypto = transportCrypto;
|
||||||
this.cipherProvider = cipherProvider;
|
this.cipherProvider = cipherProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +41,8 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
long streamNumber = ctx.getStreamNumber();
|
long streamNumber = ctx.getStreamNumber();
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
|
transportCrypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION,
|
||||||
|
streamNumber);
|
||||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
||||||
SecretKey frameKey = crypto.generateSecretKey();
|
SecretKey frameKey = crypto.generateSecretKey();
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
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,8 +90,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
private final ReentrantReadWriteLock lock =
|
private final ReentrantReadWriteLock lock =
|
||||||
new ReentrantReadWriteLock(true);
|
new ReentrantReadWriteLock(true);
|
||||||
|
|
||||||
private volatile int shutdownHandle = -1;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus,
|
DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus,
|
||||||
ShutdownManager shutdown) {
|
ShutdownManager shutdown) {
|
||||||
@@ -103,22 +101,20 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean open() throws DbException {
|
public boolean open() throws DbException {
|
||||||
Runnable shutdownHook = () -> {
|
boolean reopened = db.open();
|
||||||
|
shutdown.addShutdownHook(() -> {
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
boolean reopened = db.open();
|
|
||||||
shutdownHandle = shutdown.addShutdownHook(shutdownHook);
|
|
||||||
return reopened;
|
return reopened;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws DbException {
|
public void close() throws DbException {
|
||||||
if (closed.getAndSet(true)) return;
|
if (closed.getAndSet(true)) return;
|
||||||
shutdown.removeShutdownHook(shutdownHandle);
|
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,21 +22,23 @@ import javax.inject.Inject;
|
|||||||
class H2Database extends JdbcDatabase {
|
class H2Database extends JdbcDatabase {
|
||||||
|
|
||||||
private static final String HASH_TYPE = "BINARY(32)";
|
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 BINARY_TYPE = "BINARY";
|
||||||
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
||||||
private static final String SECRET_TYPE = "BINARY(32)";
|
private static final String STRING_TYPE = "VARCHAR";
|
||||||
|
|
||||||
private final DatabaseConfig config;
|
private final DatabaseConfig config;
|
||||||
private final String url;
|
private final String url;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
H2Database(DatabaseConfig config, Clock clock) {
|
H2Database(DatabaseConfig config, Clock clock) {
|
||||||
super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE, clock);
|
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||||
|
clock);
|
||||||
this.config = config;
|
this.config = config;
|
||||||
File dir = config.getDatabaseDirectory();
|
File dir = config.getDatabaseDirectory();
|
||||||
String path = new File(dir, "db").getAbsolutePath();
|
String path = new File(dir, "db").getAbsolutePath();
|
||||||
url = "jdbc:h2:split:" + path + ";CIPHER=AES;MULTI_THREADED=1"
|
url = "jdbc:h2:split:" + path + ";CIPHER=AES;MULTI_THREADED=1"
|
||||||
+ ";WRITE_DELAY=0;DB_CLOSE_ON_EXIT=false";
|
+ ";WRITE_DELAY=0";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
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
|
@NotNullByDefault
|
||||||
abstract class JdbcDatabase implements Database<Connection> {
|
abstract class JdbcDatabase implements Database<Connection> {
|
||||||
|
|
||||||
private static final int SCHEMA_VERSION = 30;
|
private static final int SCHEMA_VERSION = 31;
|
||||||
private static final int MIN_SCHEMA_VERSION = 30;
|
private static final int MIN_SCHEMA_VERSION = 31;
|
||||||
|
|
||||||
private static final String CREATE_SETTINGS =
|
private static final String CREATE_SETTINGS =
|
||||||
"CREATE TABLE settings"
|
"CREATE TABLE settings"
|
||||||
+ " (namespace VARCHAR NOT NULL,"
|
+ " (namespace _STRING NOT NULL,"
|
||||||
+ " key VARCHAR NOT NULL,"
|
+ " settingKey _STRING NOT NULL,"
|
||||||
+ " value VARCHAR NOT NULL,"
|
+ " value _STRING NOT NULL,"
|
||||||
+ " PRIMARY KEY (namespace, key))";
|
+ " PRIMARY KEY (namespace, settingKey))";
|
||||||
|
|
||||||
private static final String CREATE_LOCAL_AUTHORS =
|
private static final String CREATE_LOCAL_AUTHORS =
|
||||||
"CREATE TABLE localAuthors"
|
"CREATE TABLE localAuthors"
|
||||||
+ " (authorId HASH NOT NULL,"
|
+ " (authorId _HASH NOT NULL,"
|
||||||
+ " name VARCHAR NOT NULL,"
|
+ " name _STRING NOT NULL,"
|
||||||
+ " publicKey BINARY NOT NULL,"
|
+ " publicKey _BINARY NOT NULL,"
|
||||||
+ " privateKey BINARY NOT NULL,"
|
+ " privateKey _BINARY NOT NULL,"
|
||||||
+ " created BIGINT NOT NULL,"
|
+ " created BIGINT NOT NULL,"
|
||||||
+ " PRIMARY KEY (authorId))";
|
+ " PRIMARY KEY (authorId))";
|
||||||
|
|
||||||
private static final String CREATE_CONTACTS =
|
private static final String CREATE_CONTACTS =
|
||||||
"CREATE TABLE contacts"
|
"CREATE TABLE contacts"
|
||||||
+ " (contactId COUNTER,"
|
+ " (contactId _COUNTER,"
|
||||||
+ " authorId HASH NOT NULL,"
|
+ " authorId _HASH NOT NULL,"
|
||||||
+ " name VARCHAR NOT NULL,"
|
+ " name _STRING NOT NULL,"
|
||||||
+ " publicKey BINARY NOT NULL,"
|
+ " publicKey _BINARY NOT NULL,"
|
||||||
+ " localAuthorId HASH NOT NULL,"
|
+ " localAuthorId _HASH NOT NULL,"
|
||||||
+ " verified BOOLEAN NOT NULL,"
|
+ " verified BOOLEAN NOT NULL,"
|
||||||
+ " active BOOLEAN NOT NULL,"
|
+ " active BOOLEAN NOT NULL,"
|
||||||
+ " PRIMARY KEY (contactId),"
|
+ " PRIMARY KEY (contactId),"
|
||||||
@@ -103,17 +103,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_GROUPS =
|
private static final String CREATE_GROUPS =
|
||||||
"CREATE TABLE groups"
|
"CREATE TABLE groups"
|
||||||
+ " (groupId HASH NOT NULL,"
|
+ " (groupId _HASH NOT NULL,"
|
||||||
+ " clientId VARCHAR NOT NULL,"
|
+ " clientId _STRING NOT NULL,"
|
||||||
+ " descriptor BINARY NOT NULL,"
|
+ " descriptor _BINARY NOT NULL,"
|
||||||
+ " PRIMARY KEY (groupId))";
|
+ " PRIMARY KEY (groupId))";
|
||||||
|
|
||||||
private static final String CREATE_GROUP_METADATA =
|
private static final String CREATE_GROUP_METADATA =
|
||||||
"CREATE TABLE groupMetadata"
|
"CREATE TABLE groupMetadata"
|
||||||
+ " (groupId HASH NOT NULL,"
|
+ " (groupId _HASH NOT NULL,"
|
||||||
+ " key VARCHAR NOT NULL,"
|
+ " metaKey _STRING NOT NULL,"
|
||||||
+ " value BINARY NOT NULL,"
|
+ " value _BINARY NOT NULL,"
|
||||||
+ " PRIMARY KEY (groupId, key),"
|
+ " PRIMARY KEY (groupId, metaKey),"
|
||||||
+ " FOREIGN KEY (groupId)"
|
+ " FOREIGN KEY (groupId)"
|
||||||
+ " REFERENCES groups (groupId)"
|
+ " REFERENCES groups (groupId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " ON DELETE CASCADE)";
|
||||||
@@ -121,7 +121,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private static final String CREATE_GROUP_VISIBILITIES =
|
private static final String CREATE_GROUP_VISIBILITIES =
|
||||||
"CREATE TABLE groupVisibilities"
|
"CREATE TABLE groupVisibilities"
|
||||||
+ " (contactId INT NOT NULL,"
|
+ " (contactId INT NOT NULL,"
|
||||||
+ " groupId HASH NOT NULL,"
|
+ " groupId _HASH NOT NULL,"
|
||||||
+ " shared BOOLEAN NOT NULL,"
|
+ " shared BOOLEAN NOT NULL,"
|
||||||
+ " PRIMARY KEY (contactId, groupId),"
|
+ " PRIMARY KEY (contactId, groupId),"
|
||||||
+ " FOREIGN KEY (contactId)"
|
+ " FOREIGN KEY (contactId)"
|
||||||
@@ -133,8 +133,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_MESSAGES =
|
private static final String CREATE_MESSAGES =
|
||||||
"CREATE TABLE messages"
|
"CREATE TABLE messages"
|
||||||
+ " (messageId HASH NOT NULL,"
|
+ " (messageId _HASH NOT NULL,"
|
||||||
+ " groupId HASH NOT NULL,"
|
+ " groupId _HASH NOT NULL,"
|
||||||
+ " timestamp BIGINT NOT NULL,"
|
+ " timestamp BIGINT NOT NULL,"
|
||||||
+ " state INT NOT NULL,"
|
+ " state INT NOT NULL,"
|
||||||
+ " shared BOOLEAN NOT NULL,"
|
+ " shared BOOLEAN NOT NULL,"
|
||||||
@@ -147,19 +147,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_MESSAGE_METADATA =
|
private static final String CREATE_MESSAGE_METADATA =
|
||||||
"CREATE TABLE messageMetadata"
|
"CREATE TABLE messageMetadata"
|
||||||
+ " (messageId HASH NOT NULL,"
|
+ " (messageId _HASH NOT NULL,"
|
||||||
+ " key VARCHAR NOT NULL,"
|
+ " metaKey _STRING NOT NULL,"
|
||||||
+ " value BINARY NOT NULL,"
|
+ " value _BINARY NOT NULL,"
|
||||||
+ " PRIMARY KEY (messageId, key),"
|
+ " PRIMARY KEY (messageId, metaKey),"
|
||||||
+ " FOREIGN KEY (messageId)"
|
+ " FOREIGN KEY (messageId)"
|
||||||
+ " REFERENCES messages (messageId)"
|
+ " REFERENCES messages (messageId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " ON DELETE CASCADE)";
|
||||||
|
|
||||||
private static final String CREATE_MESSAGE_DEPENDENCIES =
|
private static final String CREATE_MESSAGE_DEPENDENCIES =
|
||||||
"CREATE TABLE messageDependencies"
|
"CREATE TABLE messageDependencies"
|
||||||
+ " (groupId HASH NOT NULL,"
|
+ " (groupId _HASH NOT NULL,"
|
||||||
+ " messageId HASH NOT NULL,"
|
+ " messageId _HASH NOT NULL,"
|
||||||
+ " dependencyId HASH NOT NULL," // Not a foreign key
|
+ " dependencyId _HASH NOT NULL," // Not a foreign key
|
||||||
+ " FOREIGN KEY (groupId)"
|
+ " FOREIGN KEY (groupId)"
|
||||||
+ " REFERENCES groups (groupId)"
|
+ " REFERENCES groups (groupId)"
|
||||||
+ " ON DELETE CASCADE,"
|
+ " ON DELETE CASCADE,"
|
||||||
@@ -169,7 +169,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_OFFERS =
|
private static final String CREATE_OFFERS =
|
||||||
"CREATE TABLE offers"
|
"CREATE TABLE offers"
|
||||||
+ " (messageId HASH NOT NULL," // Not a foreign key
|
+ " (messageId _HASH NOT NULL," // Not a foreign key
|
||||||
+ " contactId INT NOT NULL,"
|
+ " contactId INT NOT NULL,"
|
||||||
+ " PRIMARY KEY (messageId, contactId),"
|
+ " PRIMARY KEY (messageId, contactId),"
|
||||||
+ " FOREIGN KEY (contactId)"
|
+ " FOREIGN KEY (contactId)"
|
||||||
@@ -178,7 +178,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_STATUSES =
|
private static final String CREATE_STATUSES =
|
||||||
"CREATE TABLE statuses"
|
"CREATE TABLE statuses"
|
||||||
+ " (messageId HASH NOT NULL,"
|
+ " (messageId _HASH NOT NULL,"
|
||||||
+ " contactId INT NOT NULL,"
|
+ " contactId INT NOT NULL,"
|
||||||
+ " ack BOOLEAN NOT NULL,"
|
+ " ack BOOLEAN NOT NULL,"
|
||||||
+ " seen BOOLEAN NOT NULL,"
|
+ " seen BOOLEAN NOT NULL,"
|
||||||
@@ -195,20 +195,20 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_TRANSPORTS =
|
private static final String CREATE_TRANSPORTS =
|
||||||
"CREATE TABLE transports"
|
"CREATE TABLE transports"
|
||||||
+ " (transportId VARCHAR NOT NULL,"
|
+ " (transportId _STRING NOT NULL,"
|
||||||
+ " maxLatency INT NOT NULL,"
|
+ " maxLatency INT NOT NULL,"
|
||||||
+ " PRIMARY KEY (transportId))";
|
+ " PRIMARY KEY (transportId))";
|
||||||
|
|
||||||
private static final String CREATE_INCOMING_KEYS =
|
private static final String CREATE_INCOMING_KEYS =
|
||||||
"CREATE TABLE incomingKeys"
|
"CREATE TABLE incomingKeys"
|
||||||
+ " (contactId INT NOT NULL,"
|
+ " (contactId INT NOT NULL,"
|
||||||
+ " transportId VARCHAR NOT NULL,"
|
+ " transportId _STRING NOT NULL,"
|
||||||
+ " period BIGINT NOT NULL,"
|
+ " rotationPeriod BIGINT NOT NULL,"
|
||||||
+ " tagKey SECRET NOT NULL,"
|
+ " tagKey _SECRET NOT NULL,"
|
||||||
+ " headerKey SECRET NOT NULL,"
|
+ " headerKey _SECRET NOT NULL,"
|
||||||
+ " base BIGINT NOT NULL,"
|
+ " base BIGINT NOT NULL,"
|
||||||
+ " bitmap BINARY NOT NULL,"
|
+ " bitmap _BINARY NOT NULL,"
|
||||||
+ " PRIMARY KEY (contactId, transportId, period),"
|
+ " PRIMARY KEY (contactId, transportId, rotationPeriod),"
|
||||||
+ " FOREIGN KEY (contactId)"
|
+ " FOREIGN KEY (contactId)"
|
||||||
+ " REFERENCES contacts (contactId)"
|
+ " REFERENCES contacts (contactId)"
|
||||||
+ " ON DELETE CASCADE,"
|
+ " ON DELETE CASCADE,"
|
||||||
@@ -219,10 +219,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private static final String CREATE_OUTGOING_KEYS =
|
private static final String CREATE_OUTGOING_KEYS =
|
||||||
"CREATE TABLE outgoingKeys"
|
"CREATE TABLE outgoingKeys"
|
||||||
+ " (contactId INT NOT NULL,"
|
+ " (contactId INT NOT NULL,"
|
||||||
+ " transportId VARCHAR NOT NULL,"
|
+ " transportId _STRING NOT NULL,"
|
||||||
+ " period BIGINT NOT NULL,"
|
+ " rotationPeriod BIGINT NOT NULL,"
|
||||||
+ " tagKey SECRET NOT NULL,"
|
+ " tagKey _SECRET NOT NULL,"
|
||||||
+ " headerKey SECRET NOT NULL,"
|
+ " headerKey _SECRET NOT NULL,"
|
||||||
+ " stream BIGINT NOT NULL,"
|
+ " stream BIGINT NOT NULL,"
|
||||||
+ " PRIMARY KEY (contactId, transportId),"
|
+ " PRIMARY KEY (contactId, transportId),"
|
||||||
+ " FOREIGN KEY (contactId)"
|
+ " FOREIGN KEY (contactId)"
|
||||||
@@ -260,7 +260,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
Logger.getLogger(JdbcDatabase.class.getName());
|
Logger.getLogger(JdbcDatabase.class.getName());
|
||||||
|
|
||||||
// Different database libraries use different names for certain types
|
// Different database libraries use different names for certain types
|
||||||
private final String hashType, binaryType, counterType, secretType;
|
private final String hashType, secretType, binaryType;
|
||||||
|
private final String counterType, stringType;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
|
|
||||||
// Locking: connectionsLock
|
// Locking: connectionsLock
|
||||||
@@ -275,12 +276,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private final Lock connectionsLock = new ReentrantLock();
|
private final Lock connectionsLock = new ReentrantLock();
|
||||||
private final Condition connectionsChanged = connectionsLock.newCondition();
|
private final Condition connectionsChanged = connectionsLock.newCondition();
|
||||||
|
|
||||||
JdbcDatabase(String hashType, String binaryType, String counterType,
|
JdbcDatabase(String hashType, String secretType, String binaryType,
|
||||||
String secretType, Clock clock) {
|
String counterType, String stringType, Clock clock) {
|
||||||
this.hashType = hashType;
|
this.hashType = hashType;
|
||||||
|
this.secretType = secretType;
|
||||||
this.binaryType = binaryType;
|
this.binaryType = binaryType;
|
||||||
this.counterType = counterType;
|
this.counterType = counterType;
|
||||||
this.secretType = secretType;
|
this.stringType = stringType;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,16 +385,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String insertTypeNames(String s) {
|
private String insertTypeNames(String s) {
|
||||||
s = s.replaceAll("HASH", hashType);
|
s = s.replaceAll("_HASH", hashType);
|
||||||
s = s.replaceAll("BINARY", binaryType);
|
s = s.replaceAll("_SECRET", secretType);
|
||||||
s = s.replaceAll("COUNTER", counterType);
|
s = s.replaceAll("_BINARY", binaryType);
|
||||||
s = s.replaceAll("SECRET", secretType);
|
s = s.replaceAll("_COUNTER", counterType);
|
||||||
|
s = s.replaceAll("_STRING", stringType);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection startTransaction() throws DbException {
|
public Connection startTransaction() throws DbException {
|
||||||
Connection txn = null;
|
Connection txn;
|
||||||
connectionsLock.lock();
|
connectionsLock.lock();
|
||||||
try {
|
try {
|
||||||
if (closed) throw new DbClosedException();
|
if (closed) throw new DbClosedException();
|
||||||
@@ -500,7 +503,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
try {
|
try {
|
||||||
// Create a contact row
|
// Create a contact row
|
||||||
String sql = "INSERT INTO contacts"
|
String sql = "INSERT INTO contacts"
|
||||||
+ " (authorId, name, publicKey, localAuthorId, verified, active)"
|
+ " (authorId, name, publicKey, localAuthorId,"
|
||||||
|
+ " verified, active)"
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, remote.getId().getBytes());
|
ps.setBytes(1, remote.getId().getBytes());
|
||||||
@@ -719,7 +723,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
try {
|
try {
|
||||||
// Store the incoming keys
|
// Store the incoming keys
|
||||||
String sql = "INSERT INTO incomingKeys (contactId, transportId,"
|
String sql = "INSERT INTO incomingKeys (contactId, transportId,"
|
||||||
+ " period, tagKey, headerKey, base, bitmap)"
|
+ " rotationPeriod, tagKey, headerKey, base, bitmap)"
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
@@ -754,8 +758,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (rows != 1) throw new DbStateException();
|
if (rows != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
// Store the outgoing keys
|
// Store the outgoing keys
|
||||||
sql = "INSERT INTO outgoingKeys (contactId, transportId, period,"
|
sql = "INSERT INTO outgoingKeys (contactId, transportId,"
|
||||||
+ " tagKey, headerKey, stream)"
|
+ " rotationPeriod, tagKey, headerKey, stream)"
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
@@ -1335,7 +1339,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " JOIN messageMetadata AS md"
|
+ " JOIN messageMetadata AS md"
|
||||||
+ " ON m.messageId = md.messageId"
|
+ " ON m.messageId = md.messageId"
|
||||||
+ " WHERE state = ? AND groupId = ?"
|
+ " WHERE state = ? AND groupId = ?"
|
||||||
+ " AND key = ? AND value = ?";
|
+ " AND metaKey = ? AND value = ?";
|
||||||
for (Entry<String, byte[]> e : query.entrySet()) {
|
for (Entry<String, byte[]> e : query.entrySet()) {
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, DELIVERED.getValue());
|
ps.setInt(1, DELIVERED.getValue());
|
||||||
@@ -1367,7 +1371,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT m.messageId, key, value"
|
String sql = "SELECT m.messageId, metaKey, value"
|
||||||
+ " FROM messages AS m"
|
+ " FROM messages AS m"
|
||||||
+ " JOIN messageMetadata AS md"
|
+ " JOIN messageMetadata AS md"
|
||||||
+ " ON m.messageId = md.messageId"
|
+ " ON m.messageId = md.messageId"
|
||||||
@@ -1417,7 +1421,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT key, value FROM groupMetadata"
|
String sql = "SELECT metaKey, value FROM groupMetadata"
|
||||||
+ " WHERE groupId = ?";
|
+ " WHERE groupId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, g.getBytes());
|
ps.setBytes(1, g.getBytes());
|
||||||
@@ -1440,7 +1444,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT key, value FROM messageMetadata AS md"
|
String sql = "SELECT metaKey, value FROM messageMetadata AS md"
|
||||||
+ " JOIN messages AS m"
|
+ " JOIN messages AS m"
|
||||||
+ " ON m.messageId = md.messageId"
|
+ " ON m.messageId = md.messageId"
|
||||||
+ " WHERE m.state = ? AND md.messageId = ?";
|
+ " WHERE m.state = ? AND md.messageId = ?";
|
||||||
@@ -1466,7 +1470,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT key, value FROM messageMetadata AS md"
|
String sql = "SELECT metaKey, value FROM messageMetadata AS md"
|
||||||
+ " JOIN messages AS m"
|
+ " JOIN messages AS m"
|
||||||
+ " ON m.messageId = md.messageId"
|
+ " ON m.messageId = md.messageId"
|
||||||
+ " WHERE (m.state = ? OR m.state = ?)"
|
+ " WHERE (m.state = ? OR m.state = ?)"
|
||||||
@@ -1904,7 +1908,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT key, value FROM settings WHERE namespace = ?";
|
String sql = "SELECT settingKey, value FROM settings"
|
||||||
|
+ " WHERE namespace = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setString(1, namespace);
|
ps.setString(1, namespace);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
@@ -1927,10 +1932,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
// Retrieve the incoming keys
|
// Retrieve the incoming keys
|
||||||
String sql = "SELECT period, tagKey, headerKey, base, bitmap"
|
String sql = "SELECT rotationPeriod, tagKey, headerKey,"
|
||||||
|
+ " base, bitmap"
|
||||||
+ " FROM incomingKeys"
|
+ " FROM incomingKeys"
|
||||||
+ " WHERE transportId = ?"
|
+ " WHERE transportId = ?"
|
||||||
+ " ORDER BY contactId, period";
|
+ " ORDER BY contactId, rotationPeriod";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setString(1, t.getString());
|
ps.setString(1, t.getString());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
@@ -1947,10 +1953,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
// Retrieve the outgoing keys in the same order
|
// Retrieve the outgoing keys in the same order
|
||||||
sql = "SELECT contactId, period, tagKey, headerKey, stream"
|
sql = "SELECT contactId, rotationPeriod, tagKey, headerKey, stream"
|
||||||
+ " FROM outgoingKeys"
|
+ " FROM outgoingKeys"
|
||||||
+ " WHERE transportId = ?"
|
+ " WHERE transportId = ?"
|
||||||
+ " ORDER BY contactId, period";
|
+ " ORDER BY contactId, rotationPeriod";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setString(1, t.getString());
|
ps.setString(1, t.getString());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
@@ -1987,7 +1993,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
String sql = "UPDATE outgoingKeys SET stream = stream + 1"
|
String sql = "UPDATE outgoingKeys SET stream = stream + 1"
|
||||||
+ " WHERE contactId = ? AND transportId = ? AND period = ?";
|
+ " WHERE contactId = ? AND transportId = ?"
|
||||||
|
+ " AND rotationPeriod = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setString(2, t.getString());
|
ps.setString(2, t.getString());
|
||||||
@@ -2081,7 +2088,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
// Delete any keys that are being removed
|
// Delete any keys that are being removed
|
||||||
if (!removed.isEmpty()) {
|
if (!removed.isEmpty()) {
|
||||||
String sql = "DELETE FROM " + tableName
|
String sql = "DELETE FROM " + tableName
|
||||||
+ " WHERE " + columnName + " = ? AND key = ?";
|
+ " WHERE " + columnName + " = ? AND metaKey = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, id);
|
ps.setBytes(1, id);
|
||||||
for (String key : removed) {
|
for (String key : removed) {
|
||||||
@@ -2100,7 +2107,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (retained.isEmpty()) return;
|
if (retained.isEmpty()) return;
|
||||||
// Update any keys that already exist
|
// Update any keys that already exist
|
||||||
String sql = "UPDATE " + tableName + " SET value = ?"
|
String sql = "UPDATE " + tableName + " SET value = ?"
|
||||||
+ " WHERE " + columnName + " = ? AND key = ?";
|
+ " WHERE " + columnName + " = ? AND metaKey = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(2, id);
|
ps.setBytes(2, id);
|
||||||
for (Entry<String, byte[]> e : retained.entrySet()) {
|
for (Entry<String, byte[]> e : retained.entrySet()) {
|
||||||
@@ -2117,7 +2124,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
// Insert any keys that don't already exist
|
// Insert any keys that don't already exist
|
||||||
sql = "INSERT INTO " + tableName
|
sql = "INSERT INTO " + tableName
|
||||||
+ " (" + columnName + ", key, value)"
|
+ " (" + columnName + ", metaKey, value)"
|
||||||
+ " VALUES (?, ?, ?)";
|
+ " VALUES (?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, id);
|
ps.setBytes(1, id);
|
||||||
@@ -2149,7 +2156,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
try {
|
try {
|
||||||
// Update any settings that already exist
|
// Update any settings that already exist
|
||||||
String sql = "UPDATE settings SET value = ?"
|
String sql = "UPDATE settings SET value = ?"
|
||||||
+ " WHERE namespace = ? AND key = ?";
|
+ " WHERE namespace = ? AND settingKey = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
for (Entry<String, String> e : s.entrySet()) {
|
for (Entry<String, String> e : s.entrySet()) {
|
||||||
ps.setString(1, e.getValue());
|
ps.setString(1, e.getValue());
|
||||||
@@ -2164,7 +2171,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (rows > 1) throw new DbStateException();
|
if (rows > 1) throw new DbStateException();
|
||||||
}
|
}
|
||||||
// Insert any settings that don't already exist
|
// Insert any settings that don't already exist
|
||||||
sql = "INSERT INTO settings (namespace, key, value)"
|
sql = "INSERT INTO settings (namespace, settingKey, value)"
|
||||||
+ " VALUES (?, ?, ?)";
|
+ " VALUES (?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
int updateIndex = 0, inserted = 0;
|
int updateIndex = 0, inserted = 0;
|
||||||
@@ -2528,7 +2535,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
String sql = "UPDATE incomingKeys SET base = ?, bitmap = ?"
|
String sql = "UPDATE incomingKeys SET base = ?, bitmap = ?"
|
||||||
+ " WHERE contactId = ? AND transportId = ? AND period = ?";
|
+ " WHERE contactId = ? AND transportId = ?"
|
||||||
|
+ " AND rotationPeriod = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setLong(1, base);
|
ps.setLong(1, base);
|
||||||
ps.setBytes(2, bitmap);
|
ps.setBytes(2, bitmap);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
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.KeyPair;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||||
@@ -46,7 +46,7 @@ class KeyAgreementConnector {
|
|||||||
|
|
||||||
private final Callbacks callbacks;
|
private final Callbacks callbacks;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final CryptoComponent crypto;
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
private final CompletionService<KeyAgreementConnection> connect;
|
private final CompletionService<KeyAgreementConnection> connect;
|
||||||
|
|
||||||
@@ -58,11 +58,11 @@ class KeyAgreementConnector {
|
|||||||
private volatile boolean alice = false;
|
private volatile boolean alice = false;
|
||||||
|
|
||||||
KeyAgreementConnector(Callbacks callbacks, Clock clock,
|
KeyAgreementConnector(Callbacks callbacks, Clock clock,
|
||||||
CryptoComponent crypto, PluginManager pluginManager,
|
KeyAgreementCrypto keyAgreementCrypto, PluginManager pluginManager,
|
||||||
Executor ioExecutor) {
|
Executor ioExecutor) {
|
||||||
this.callbacks = callbacks;
|
this.callbacks = callbacks;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.crypto = crypto;
|
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
connect = new ExecutorCompletionService<>(ioExecutor);
|
connect = new ExecutorCompletionService<>(ioExecutor);
|
||||||
}
|
}
|
||||||
@@ -70,8 +70,8 @@ class KeyAgreementConnector {
|
|||||||
public Payload listen(KeyPair localKeyPair) {
|
public Payload listen(KeyPair localKeyPair) {
|
||||||
LOG.info("Starting BQP listeners");
|
LOG.info("Starting BQP listeners");
|
||||||
// Derive commitment
|
// Derive commitment
|
||||||
byte[] commitment = crypto.deriveKeyCommitment(
|
byte[] commitment = keyAgreementCrypto.deriveKeyCommitment(
|
||||||
localKeyPair.getPublic().getEncoded());
|
localKeyPair.getPublic());
|
||||||
// Start all listeners and collect their descriptors
|
// Start all listeners and collect their descriptors
|
||||||
List<TransportDescriptor> descriptors = new ArrayList<>();
|
List<TransportDescriptor> descriptors = new ArrayList<>();
|
||||||
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {
|
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
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.BdfReaderFactory;
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||||
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.keyagreement.PayloadEncoder;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
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.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
@@ -22,13 +13,9 @@ import dagger.Provides;
|
|||||||
public class KeyAgreementModule {
|
public class KeyAgreementModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
KeyAgreementTask provideKeyAgreementTask(
|
||||||
KeyAgreementTaskFactory provideKeyAgreementTaskFactory(Clock clock,
|
KeyAgreementTaskImpl keyAgreementTask) {
|
||||||
CryptoComponent crypto, EventBus eventBus,
|
return keyAgreementTask;
|
||||||
@IoExecutor Executor ioExecutor, PayloadEncoder payloadEncoder,
|
|
||||||
PluginManager pluginManager) {
|
|
||||||
return new KeyAgreementTaskFactoryImpl(clock, crypto, eventBus,
|
|
||||||
ioExecutor, payloadEncoder, pluginManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
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.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.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
@@ -11,6 +14,10 @@ import java.io.IOException;
|
|||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Arrays;
|
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.
|
* Implementation of the BQP protocol.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -57,6 +64,7 @@ class KeyAgreementProtocol {
|
|||||||
|
|
||||||
private final Callbacks callbacks;
|
private final Callbacks callbacks;
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
private final PayloadEncoder payloadEncoder;
|
private final PayloadEncoder payloadEncoder;
|
||||||
private final KeyAgreementTransport transport;
|
private final KeyAgreementTransport transport;
|
||||||
private final Payload theirPayload, ourPayload;
|
private final Payload theirPayload, ourPayload;
|
||||||
@@ -64,11 +72,13 @@ class KeyAgreementProtocol {
|
|||||||
private final boolean alice;
|
private final boolean alice;
|
||||||
|
|
||||||
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
|
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
|
||||||
|
KeyAgreementCrypto keyAgreementCrypto,
|
||||||
PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
|
PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
|
||||||
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
|
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
|
||||||
boolean alice) {
|
boolean alice) {
|
||||||
this.callbacks = callbacks;
|
this.callbacks = callbacks;
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
|
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||||
this.payloadEncoder = payloadEncoder;
|
this.payloadEncoder = payloadEncoder;
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
this.theirPayload = theirPayload;
|
this.theirPayload = theirPayload;
|
||||||
@@ -86,7 +96,7 @@ class KeyAgreementProtocol {
|
|||||||
*/
|
*/
|
||||||
SecretKey perform() throws AbortException, IOException {
|
SecretKey perform() throws AbortException, IOException {
|
||||||
try {
|
try {
|
||||||
byte[] theirPublicKey;
|
PublicKey theirPublicKey;
|
||||||
if (alice) {
|
if (alice) {
|
||||||
sendKey();
|
sendKey();
|
||||||
// Alice waits here until Bob obtains her payload.
|
// Alice waits here until Bob obtains her payload.
|
||||||
@@ -104,7 +114,7 @@ class KeyAgreementProtocol {
|
|||||||
receiveConfirm(s, theirPublicKey);
|
receiveConfirm(s, theirPublicKey);
|
||||||
sendConfirm(s, theirPublicKey);
|
sendConfirm(s, theirPublicKey);
|
||||||
}
|
}
|
||||||
return crypto.deriveMasterSecret(s);
|
return crypto.deriveKey(MASTER_SECRET_LABEL, s);
|
||||||
} catch (AbortException e) {
|
} catch (AbortException e) {
|
||||||
sendAbort(e.getCause() != null);
|
sendAbort(e.getCause() != null);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -115,27 +125,41 @@ class KeyAgreementProtocol {
|
|||||||
transport.sendKey(ourKeyPair.getPublic().getEncoded());
|
transport.sendKey(ourKeyPair.getPublic().getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] receiveKey() throws AbortException {
|
private PublicKey receiveKey() throws AbortException {
|
||||||
byte[] publicKey = transport.receiveKey();
|
byte[] publicKeyBytes = transport.receiveKey();
|
||||||
callbacks.initialRecordReceived();
|
callbacks.initialRecordReceived();
|
||||||
byte[] expected = crypto.deriveKeyCommitment(publicKey);
|
KeyParser keyParser = crypto.getAgreementKeyParser();
|
||||||
if (!Arrays.equals(expected, theirPayload.getCommitment()))
|
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) {
|
||||||
throw new AbortException();
|
throw new AbortException();
|
||||||
return publicKey;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecretKey deriveSharedSecret(byte[] theirPublicKey)
|
private SecretKey deriveSharedSecret(PublicKey theirPublicKey)
|
||||||
throws AbortException {
|
throws AbortException {
|
||||||
try {
|
try {
|
||||||
return crypto.deriveSharedSecret(theirPublicKey, ourKeyPair, alice);
|
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);
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new AbortException(e);
|
throw new AbortException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendConfirm(SecretKey s, byte[] theirPublicKey)
|
private void sendConfirm(SecretKey s, PublicKey theirPublicKey)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
byte[] confirm = crypto.deriveConfirmationRecord(s,
|
byte[] confirm = keyAgreementCrypto.deriveConfirmationRecord(s,
|
||||||
payloadEncoder.encode(theirPayload),
|
payloadEncoder.encode(theirPayload),
|
||||||
payloadEncoder.encode(ourPayload),
|
payloadEncoder.encode(ourPayload),
|
||||||
theirPublicKey, ourKeyPair,
|
theirPublicKey, ourKeyPair,
|
||||||
@@ -143,10 +167,10 @@ class KeyAgreementProtocol {
|
|||||||
transport.sendConfirm(confirm);
|
transport.sendConfirm(confirm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receiveConfirm(SecretKey s, byte[] theirPublicKey)
|
private void receiveConfirm(SecretKey s, PublicKey theirPublicKey)
|
||||||
throws AbortException {
|
throws AbortException {
|
||||||
byte[] confirm = transport.receiveConfirm();
|
byte[] confirm = transport.receiveConfirm();
|
||||||
byte[] expected = crypto.deriveConfirmationRecord(s,
|
byte[] expected = keyAgreementCrypto.deriveConfirmationRecord(s,
|
||||||
payloadEncoder.encode(theirPayload),
|
payloadEncoder.encode(theirPayload),
|
||||||
payloadEncoder.encode(ourPayload),
|
payloadEncoder.encode(ourPayload),
|
||||||
theirPublicKey, ourKeyPair,
|
theirPublicKey, ourKeyPair,
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
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,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
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.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
@@ -14,6 +15,7 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent
|
|||||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
|
||||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
|
||||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
|
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.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
@@ -23,6 +25,8 @@ import java.io.IOException;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -35,6 +39,7 @@ class KeyAgreementTaskImpl extends Thread implements
|
|||||||
Logger.getLogger(KeyAgreementTaskImpl.class.getName());
|
Logger.getLogger(KeyAgreementTaskImpl.class.getName());
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final PayloadEncoder payloadEncoder;
|
private final PayloadEncoder payloadEncoder;
|
||||||
private final KeyPair localKeyPair;
|
private final KeyPair localKeyPair;
|
||||||
@@ -43,14 +48,17 @@ class KeyAgreementTaskImpl extends Thread implements
|
|||||||
private Payload localPayload;
|
private Payload localPayload;
|
||||||
private Payload remotePayload;
|
private Payload remotePayload;
|
||||||
|
|
||||||
|
@Inject
|
||||||
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
|
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
|
||||||
EventBus eventBus, PayloadEncoder payloadEncoder,
|
KeyAgreementCrypto keyAgreementCrypto, EventBus eventBus,
|
||||||
PluginManager pluginManager, Executor ioExecutor) {
|
PayloadEncoder payloadEncoder, PluginManager pluginManager,
|
||||||
|
@IoExecutor Executor ioExecutor) {
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
|
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.payloadEncoder = payloadEncoder;
|
this.payloadEncoder = payloadEncoder;
|
||||||
localKeyPair = crypto.generateAgreementKeyPair();
|
localKeyPair = crypto.generateAgreementKeyPair();
|
||||||
connector = new KeyAgreementConnector(this, clock, crypto,
|
connector = new KeyAgreementConnector(this, clock, keyAgreementCrypto,
|
||||||
pluginManager, ioExecutor);
|
pluginManager, ioExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,8 +108,8 @@ class KeyAgreementTaskImpl extends Thread implements
|
|||||||
// Run BQP protocol over the connection
|
// Run BQP protocol over the connection
|
||||||
LOG.info("Starting BQP protocol");
|
LOG.info("Starting BQP protocol");
|
||||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
|
||||||
payloadEncoder, transport, remotePayload, localPayload,
|
keyAgreementCrypto, payloadEncoder, transport, remotePayload,
|
||||||
localKeyPair, alice);
|
localPayload, localKeyPair, alice);
|
||||||
try {
|
try {
|
||||||
SecretKey master = protocol.perform();
|
SecretKey master = protocol.perform();
|
||||||
KeyAgreementResult result =
|
KeyAgreementResult result =
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
this.metadataParser = metadataParser;
|
this.metadataParser = metadataParser;
|
||||||
this.contactGroupFactory = contactGroupFactory;
|
this.contactGroupFactory = contactGroupFactory;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID);
|
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -129,8 +130,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
public Map<TransportId, TransportProperties> getLocalProperties()
|
public Map<TransportId, TransportProperties> getLocalProperties()
|
||||||
throws DbException {
|
throws DbException {
|
||||||
Map<TransportId, TransportProperties> local;
|
Map<TransportId, TransportProperties> local;
|
||||||
// TODO: Transaction can be read-only when code is simplified
|
Transaction txn = db.startTransaction(true);
|
||||||
Transaction txn = db.startTransaction(false);
|
|
||||||
try {
|
try {
|
||||||
local = getLocalProperties(txn);
|
local = getLocalProperties(txn);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -165,8 +165,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
throws DbException {
|
throws DbException {
|
||||||
try {
|
try {
|
||||||
TransportProperties p = null;
|
TransportProperties p = null;
|
||||||
// TODO: Transaction can be read-only when code is simplified
|
Transaction txn = db.startTransaction(true);
|
||||||
Transaction txn = db.startTransaction(false);
|
|
||||||
try {
|
try {
|
||||||
// Find the latest local update
|
// Find the latest local update
|
||||||
LatestUpdate latest = findLatest(txn, localGroup.getId(), t,
|
LatestUpdate latest = findLatest(txn, localGroup.getId(), t,
|
||||||
@@ -192,8 +191,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
public Map<ContactId, TransportProperties> getRemoteProperties(
|
public Map<ContactId, TransportProperties> getRemoteProperties(
|
||||||
TransportId t) throws DbException {
|
TransportId t) throws DbException {
|
||||||
Map<ContactId, TransportProperties> remote = new HashMap<>();
|
Map<ContactId, TransportProperties> remote = new HashMap<>();
|
||||||
// TODO: Transaction can be read-only when code is simplified
|
Transaction txn = db.startTransaction(true);
|
||||||
Transaction txn = db.startTransaction(false);
|
|
||||||
try {
|
try {
|
||||||
for (Contact c : db.getContacts(txn))
|
for (Contact c : db.getContacts(txn))
|
||||||
remote.put(c.getId(), getRemoteProperties(txn, c, t));
|
remote.put(c.getId(), getRemoteProperties(txn, c, t));
|
||||||
@@ -227,8 +225,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
public TransportProperties getRemoteProperties(ContactId c, TransportId t)
|
public TransportProperties getRemoteProperties(ContactId c, TransportId t)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
TransportProperties p;
|
TransportProperties p;
|
||||||
// TODO: Transaction can be read-only when code is simplified
|
Transaction txn = db.startTransaction(true);
|
||||||
Transaction txn = db.startTransaction(false);
|
|
||||||
try {
|
try {
|
||||||
p = getRemoteProperties(txn, db.getContact(txn, c), t);
|
p = getRemoteProperties(txn, db.getContact(txn, c), t);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -291,7 +288,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Group getContactGroup(Contact c) {
|
private Group getContactGroup(Contact c) {
|
||||||
return contactGroupFactory.createContactGroup(CLIENT_ID, c);
|
return contactGroupFactory.createContactGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeMessage(Transaction txn, GroupId g, TransportId t,
|
private void storeMessage(Transaction txn, GroupId g, TransportId t,
|
||||||
@@ -318,7 +316,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
|
|
||||||
private Map<TransportId, LatestUpdate> findLatestLocal(Transaction txn)
|
private Map<TransportId, LatestUpdate> findLatestLocal(Transaction txn)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
// TODO: This can be simplified before 1.0
|
|
||||||
Map<TransportId, LatestUpdate> latestUpdates = new HashMap<>();
|
Map<TransportId, LatestUpdate> latestUpdates = new HashMap<>();
|
||||||
Map<MessageId, BdfDictionary> metadata = clientHelper
|
Map<MessageId, BdfDictionary> metadata = clientHelper
|
||||||
.getMessageMetadataAsDictionary(txn, localGroup.getId());
|
.getMessageMetadataAsDictionary(txn, localGroup.getId());
|
||||||
@@ -326,17 +323,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
BdfDictionary meta = e.getValue();
|
BdfDictionary meta = e.getValue();
|
||||||
TransportId t = new TransportId(meta.getString("transportId"));
|
TransportId t = new TransportId(meta.getString("transportId"));
|
||||||
long version = meta.getLong("version");
|
long version = meta.getLong("version");
|
||||||
LatestUpdate latest = latestUpdates.get(t);
|
latestUpdates.put(t, new LatestUpdate(e.getKey(), version));
|
||||||
if (latest == null) {
|
|
||||||
latestUpdates.put(t, new LatestUpdate(e.getKey(), version));
|
|
||||||
} else if (version > latest.version) {
|
|
||||||
// This update is newer - delete the previous one
|
|
||||||
db.removeMessage(txn, latest.messageId);
|
|
||||||
latestUpdates.put(t, new LatestUpdate(e.getKey(), version));
|
|
||||||
} else {
|
|
||||||
// We've already found a newer update - delete this one
|
|
||||||
db.removeMessage(txn, e.getKey());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return latestUpdates;
|
return latestUpdates;
|
||||||
}
|
}
|
||||||
@@ -344,38 +331,16 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
@Nullable
|
@Nullable
|
||||||
private LatestUpdate findLatest(Transaction txn, GroupId g, TransportId t,
|
private LatestUpdate findLatest(Transaction txn, GroupId g, TransportId t,
|
||||||
boolean local) throws DbException, FormatException {
|
boolean local) throws DbException, FormatException {
|
||||||
// TODO: This can be simplified before 1.0
|
|
||||||
LatestUpdate latest = null;
|
|
||||||
Map<MessageId, BdfDictionary> metadata =
|
Map<MessageId, BdfDictionary> metadata =
|
||||||
clientHelper.getMessageMetadataAsDictionary(txn, g);
|
clientHelper.getMessageMetadataAsDictionary(txn, g);
|
||||||
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
|
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
|
||||||
BdfDictionary meta = e.getValue();
|
BdfDictionary meta = e.getValue();
|
||||||
if (meta.getString("transportId").equals(t.getString())
|
if (meta.getString("transportId").equals(t.getString())
|
||||||
&& meta.getBoolean("local") == local) {
|
&& meta.getBoolean("local") == local) {
|
||||||
long version = meta.getLong("version");
|
return new LatestUpdate(e.getKey(), meta.getLong("version"));
|
||||||
if (latest == null) {
|
|
||||||
latest = new LatestUpdate(e.getKey(), version);
|
|
||||||
} else if (version > latest.version) {
|
|
||||||
// This update is newer - delete the previous one
|
|
||||||
if (local) {
|
|
||||||
db.removeMessage(txn, latest.messageId);
|
|
||||||
} else {
|
|
||||||
db.deleteMessage(txn, latest.messageId);
|
|
||||||
db.deleteMessageMetadata(txn, latest.messageId);
|
|
||||||
}
|
|
||||||
latest = new LatestUpdate(e.getKey(), version);
|
|
||||||
} else {
|
|
||||||
// We've already found a newer update - delete this one
|
|
||||||
if (local) {
|
|
||||||
db.removeMessage(txn, e.getKey());
|
|
||||||
} else {
|
|
||||||
db.deleteMessage(txn, e.getKey());
|
|
||||||
db.deleteMessageMetadata(txn, e.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return latest;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransportProperties parseProperties(BdfList message)
|
private TransportProperties parseProperties(BdfList message)
|
||||||
|
|||||||
@@ -6,11 +6,16 @@ import org.briarproject.bramble.api.sync.ClientId;
|
|||||||
import org.briarproject.bramble.api.sync.Group;
|
import org.briarproject.bramble.api.sync.Group;
|
||||||
import org.briarproject.bramble.api.sync.GroupFactory;
|
import org.briarproject.bramble.api.sync.GroupFactory;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
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
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class GroupFactoryImpl implements GroupFactory {
|
class GroupFactoryImpl implements GroupFactory {
|
||||||
@@ -23,9 +28,12 @@ class GroupFactoryImpl implements GroupFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createGroup(ClientId c, byte[] descriptor) {
|
public Group createGroup(ClientId c, int clientVersion, byte[] descriptor) {
|
||||||
byte[] hash = crypto.hash(GroupId.LABEL,
|
byte[] clientVersionBytes = new byte[INT_32_BYTES];
|
||||||
StringUtils.toUtf8(c.getString()), descriptor);
|
ByteUtils.writeUint32(clientVersion, clientVersionBytes, 0);
|
||||||
|
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
|
||||||
|
StringUtils.toUtf8(c.getString()), clientVersionBytes,
|
||||||
|
descriptor);
|
||||||
return new Group(new GroupId(hash), c, descriptor);
|
return new Group(new GroupId(hash), c, descriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ import org.briarproject.bramble.util.ByteUtils;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
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.MAX_MESSAGE_BODY_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -32,9 +34,9 @@ class MessageFactoryImpl implements MessageFactory {
|
|||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
byte[] timeBytes = new byte[ByteUtils.INT_64_BYTES];
|
byte[] timeBytes = new byte[ByteUtils.INT_64_BYTES];
|
||||||
ByteUtils.writeUint64(timestamp, timeBytes, 0);
|
ByteUtils.writeUint64(timestamp, timeBytes, 0);
|
||||||
byte[] idHash =
|
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
|
||||||
crypto.hash(MessageId.LABEL, g.getBytes(), timeBytes, body);
|
g.getBytes(), timeBytes, body);
|
||||||
MessageId id = new MessageId(idHash);
|
MessageId id = new MessageId(hash);
|
||||||
byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length];
|
byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length];
|
||||||
System.arraycopy(g.getBytes(), 0, raw, 0, UniqueId.LENGTH);
|
System.arraycopy(g.getBytes(), 0, raw, 0, UniqueId.LENGTH);
|
||||||
ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);
|
ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
@@ -20,17 +20,18 @@ class TransportKeyManagerFactoryImpl implements
|
|||||||
TransportKeyManagerFactory {
|
TransportKeyManagerFactory {
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final CryptoComponent crypto;
|
private final TransportCrypto transportCrypto;
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final ScheduledExecutorService scheduler;
|
private final ScheduledExecutorService scheduler;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TransportKeyManagerFactoryImpl(DatabaseComponent db, CryptoComponent crypto,
|
TransportKeyManagerFactoryImpl(DatabaseComponent db,
|
||||||
|
TransportCrypto transportCrypto,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
@Scheduler ScheduledExecutorService scheduler, Clock clock) {
|
@Scheduler ScheduledExecutorService scheduler, Clock clock) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.crypto = crypto;
|
this.transportCrypto = transportCrypto;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
@@ -39,8 +40,8 @@ class TransportKeyManagerFactoryImpl implements
|
|||||||
@Override
|
@Override
|
||||||
public TransportKeyManager createTransportKeyManager(
|
public TransportKeyManager createTransportKeyManager(
|
||||||
TransportId transportId, long maxLatency) {
|
TransportId transportId, long maxLatency) {
|
||||||
return new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler,
|
return new TransportKeyManagerImpl(db, transportCrypto, dbExecutor,
|
||||||
clock, transportId, maxLatency);
|
scheduler, clock, transportId, maxLatency);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package org.briarproject.bramble.transport;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
@@ -41,7 +41,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
Logger.getLogger(TransportKeyManagerImpl.class.getName());
|
Logger.getLogger(TransportKeyManagerImpl.class.getName());
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final CryptoComponent crypto;
|
private final TransportCrypto transportCrypto;
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final ScheduledExecutorService scheduler;
|
private final ScheduledExecutorService scheduler;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
@@ -54,11 +54,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
private final Map<ContactId, MutableOutgoingKeys> outContexts;
|
private final Map<ContactId, MutableOutgoingKeys> outContexts;
|
||||||
private final Map<ContactId, MutableTransportKeys> keys;
|
private final Map<ContactId, MutableTransportKeys> keys;
|
||||||
|
|
||||||
TransportKeyManagerImpl(DatabaseComponent db, CryptoComponent crypto,
|
TransportKeyManagerImpl(DatabaseComponent db,
|
||||||
Executor dbExecutor, @Scheduler ScheduledExecutorService scheduler,
|
TransportCrypto transportCrypto, Executor dbExecutor,
|
||||||
Clock clock, TransportId transportId, long maxLatency) {
|
@Scheduler ScheduledExecutorService scheduler, Clock clock,
|
||||||
|
TransportId transportId, long maxLatency) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.crypto = crypto;
|
this.transportCrypto = transportCrypto;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
@@ -99,7 +100,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
|
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
|
||||||
ContactId c = e.getKey();
|
ContactId c = e.getKey();
|
||||||
TransportKeys k = e.getValue();
|
TransportKeys k = e.getValue();
|
||||||
TransportKeys k1 = crypto.rotateTransportKeys(k, rotationPeriod);
|
TransportKeys k1 =
|
||||||
|
transportCrypto.rotateTransportKeys(k, rotationPeriod);
|
||||||
if (k1.getRotationPeriod() > k.getRotationPeriod())
|
if (k1.getRotationPeriod() > k.getRotationPeriod())
|
||||||
rotationResult.rotated.put(c, k1);
|
rotationResult.rotated.put(c, k1);
|
||||||
rotationResult.current.put(c, k1);
|
rotationResult.current.put(c, k1);
|
||||||
@@ -127,7 +129,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
||||||
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
|
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||||
streamNumber);
|
streamNumber);
|
||||||
inContexts.put(new Bytes(tag), tagCtx);
|
inContexts.put(new Bytes(tag), tagCtx);
|
||||||
}
|
}
|
||||||
@@ -162,11 +164,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
// Work out what rotation period the timestamp belongs to
|
// Work out what rotation period the timestamp belongs to
|
||||||
long rotationPeriod = timestamp / rotationPeriodLength;
|
long rotationPeriod = timestamp / rotationPeriodLength;
|
||||||
// Derive the transport keys
|
// Derive the transport keys
|
||||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||||
rotationPeriod, alice);
|
master, rotationPeriod, alice);
|
||||||
// Rotate the keys to the current rotation period if necessary
|
// Rotate the keys to the current rotation period if necessary
|
||||||
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
|
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
|
||||||
k = crypto.rotateTransportKeys(k, rotationPeriod);
|
k = transportCrypto.rotateTransportKeys(k, rotationPeriod);
|
||||||
// Initialise mutable state for the contact
|
// Initialise mutable state for the contact
|
||||||
addKeys(c, new MutableTransportKeys(k));
|
addKeys(c, new MutableTransportKeys(k));
|
||||||
// Write the keys back to the DB
|
// Write the keys back to the DB
|
||||||
@@ -234,8 +236,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
// Add tags for any stream numbers added to the window
|
// Add tags for any stream numbers added to the window
|
||||||
for (long streamNumber : change.getAdded()) {
|
for (long streamNumber : change.getAdded()) {
|
||||||
byte[] addTag = new byte[TAG_LENGTH];
|
byte[] addTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
|
||||||
streamNumber);
|
PROTOCOL_VERSION, streamNumber);
|
||||||
inContexts.put(new Bytes(addTag), new TagContext(
|
inContexts.put(new Bytes(addTag), new TagContext(
|
||||||
tagCtx.contactId, inKeys, streamNumber));
|
tagCtx.contactId, inKeys, streamNumber));
|
||||||
}
|
}
|
||||||
@@ -243,7 +245,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
for (long streamNumber : change.getRemoved()) {
|
for (long streamNumber : change.getRemoved()) {
|
||||||
if (streamNumber == tagCtx.streamNumber) continue;
|
if (streamNumber == tagCtx.streamNumber) continue;
|
||||||
byte[] removeTag = new byte[TAG_LENGTH];
|
byte[] removeTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(removeTag, inKeys.getTagKey(),
|
transportCrypto.encodeTag(removeTag, inKeys.getTagKey(),
|
||||||
PROTOCOL_VERSION, streamNumber);
|
PROTOCOL_VERSION, streamNumber);
|
||||||
inContexts.remove(new Bytes(removeTag));
|
inContexts.remove(new Bytes(removeTag));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
public class EcdsaSignatureTest extends SignatureTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyPair generateKeyPair() {
|
||||||
|
return crypto.generateSignatureKeyPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
return crypto.sign(label, toSign, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||||
|
byte[] signature) throws GeneralSecurityException {
|
||||||
|
return crypto.verify(label, signedData, publicKey, signature);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
public class EdSignatureTest extends SignatureTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyPair generateKeyPair() {
|
||||||
|
return crypto.generateEdKeyPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
return crypto.signEd(label, toSign, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||||
|
byte[] signature) throws GeneralSecurityException {
|
||||||
|
return crypto.verifyEd(label, signedData, publicKey, signature);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
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.sec.SECNamedCurves;
|
||||||
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||||
import org.spongycastle.asn1.x9.X9ECParameters;
|
import org.spongycastle.asn1.x9.X9ECParameters;
|
||||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
|
import org.spongycastle.crypto.BasicAgreement;
|
||||||
import org.spongycastle.crypto.Digest;
|
import org.spongycastle.crypto.Digest;
|
||||||
|
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
|
||||||
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||||
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
||||||
import org.spongycastle.crypto.params.ECDomainParameters;
|
import org.spongycastle.crypto.params.ECDomainParameters;
|
||||||
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
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.params.ParametersWithRandom;
|
||||||
import org.spongycastle.crypto.signers.DSADigestSigner;
|
import org.spongycastle.crypto.signers.DSADigestSigner;
|
||||||
import org.spongycastle.crypto.signers.DSAKCalculator;
|
import org.spongycastle.crypto.signers.DSAKCalculator;
|
||||||
@@ -19,14 +22,23 @@ import org.spongycastle.crypto.signers.HMacDSAKCalculator;
|
|||||||
import org.spongycastle.math.ec.ECCurve;
|
import org.spongycastle.math.ec.ECCurve;
|
||||||
import org.spongycastle.math.ec.ECPoint;
|
import org.spongycastle.math.ec.ECPoint;
|
||||||
import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
|
import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
|
||||||
|
import org.whispersystems.curve25519.Curve25519;
|
||||||
|
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.Provider;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.security.Signature;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
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
|
// Not a JUnit test
|
||||||
public class EllipticCurvePerformanceTest {
|
public class EllipticCurvePerformanceTest {
|
||||||
|
|
||||||
@@ -37,8 +49,9 @@ public class EllipticCurvePerformanceTest {
|
|||||||
"secp256k1", "secp256r1", "secp384r1", "secp521r1");
|
"secp256k1", "secp256r1", "secp384r1", "secp521r1");
|
||||||
private static final List<String> BRAINPOOL_NAMES = Arrays.asList(
|
private static final List<String> BRAINPOOL_NAMES = Arrays.asList(
|
||||||
"brainpoolp256r1", "brainpoolp384r1", "brainpoolp512r1");
|
"brainpoolp256r1", "brainpoolp384r1", "brainpoolp512r1");
|
||||||
|
private static final Provider ED_PROVIDER = new EdDSASecurityProvider();
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) throws GeneralSecurityException {
|
||||||
for (String name : SEC_NAMES) {
|
for (String name : SEC_NAMES) {
|
||||||
ECDomainParameters params =
|
ECDomainParameters params =
|
||||||
convertParams(SECNamedCurves.getByName(name));
|
convertParams(SECNamedCurves.getByName(name));
|
||||||
@@ -51,43 +64,32 @@ public class EllipticCurvePerformanceTest {
|
|||||||
runTest(name + " default", params);
|
runTest(name + " default", params);
|
||||||
runTest(name + " constant", constantTime(params));
|
runTest(name + " constant", constantTime(params));
|
||||||
}
|
}
|
||||||
runTest("ours", EllipticCurveConstants.PARAMETERS);
|
runTest("ours", PARAMETERS);
|
||||||
|
runCurve25519Test();
|
||||||
|
runEd25519Test();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void runTest(String name, ECDomainParameters params) {
|
private static void runTest(String name, ECDomainParameters params) {
|
||||||
// Generate two key pairs using the given parameters
|
// Generate two key pairs using the given parameters
|
||||||
ECKeyGenerationParameters generatorParams =
|
|
||||||
new ECKeyGenerationParameters(params, random);
|
|
||||||
ECKeyPairGenerator generator = new ECKeyPairGenerator();
|
ECKeyPairGenerator generator = new ECKeyPairGenerator();
|
||||||
generator.init(generatorParams);
|
generator.init(new ECKeyGenerationParameters(params, random));
|
||||||
AsymmetricCipherKeyPair keyPair1 = generator.generateKeyPair();
|
AsymmetricCipherKeyPair keyPair1 = generator.generateKeyPair();
|
||||||
ECPublicKeyParameters public1 =
|
|
||||||
(ECPublicKeyParameters) keyPair1.getPublic();
|
|
||||||
ECPrivateKeyParameters private1 =
|
|
||||||
(ECPrivateKeyParameters) keyPair1.getPrivate();
|
|
||||||
AsymmetricCipherKeyPair keyPair2 = generator.generateKeyPair();
|
AsymmetricCipherKeyPair keyPair2 = generator.generateKeyPair();
|
||||||
ECPublicKeyParameters public2 =
|
// Time some ECDH and ECDHC key agreements
|
||||||
(ECPublicKeyParameters) keyPair2.getPublic();
|
long agreementMedian = runAgreementTest(keyPair1, keyPair2, false);
|
||||||
// Time some ECDH key agreements
|
long agreementWithCofactorMedian =
|
||||||
List<Long> samples = new ArrayList<>();
|
runAgreementTest(keyPair1, keyPair2, true);
|
||||||
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
|
// Time some signatures
|
||||||
|
List<Long> samples = new ArrayList<>();
|
||||||
List<byte[]> signatures = new ArrayList<>();
|
List<byte[]> signatures = new ArrayList<>();
|
||||||
samples.clear();
|
|
||||||
for (int i = 0; i < SAMPLES; i++) {
|
for (int i = 0; i < SAMPLES; i++) {
|
||||||
Digest digest = new Blake2sDigest();
|
Digest digest = new Blake2sDigest();
|
||||||
DSAKCalculator calculator = new HMacDSAKCalculator(digest);
|
DSAKCalculator calculator = new HMacDSAKCalculator(digest);
|
||||||
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
||||||
calculator), digest);
|
calculator), digest);
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
signer.init(true, new ParametersWithRandom(private1, random));
|
signer.init(true,
|
||||||
|
new ParametersWithRandom(keyPair1.getPrivate(), random));
|
||||||
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||||
signatures.add(signer.generateSignature());
|
signatures.add(signer.generateSignature());
|
||||||
samples.add(System.nanoTime() - start);
|
samples.add(System.nanoTime() - start);
|
||||||
@@ -101,17 +103,83 @@ public class EllipticCurvePerformanceTest {
|
|||||||
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
||||||
calculator), digest);
|
calculator), digest);
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
signer.init(false, public1);
|
signer.init(false, keyPair1.getPublic());
|
||||||
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||||
if (!signer.verifySignature(signatures.get(i)))
|
if (!signer.verifySignature(signatures.get(i)))
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
samples.add(System.nanoTime() - start);
|
samples.add(System.nanoTime() - start);
|
||||||
}
|
}
|
||||||
long verificationMedian = median(samples);
|
long verificationMedian = median(samples);
|
||||||
System.out.println(name + ": "
|
System.out.println(String.format("%s: %,d %,d %,d %,d", name,
|
||||||
+ agreementMedian + " "
|
agreementMedian, agreementWithCofactorMedian,
|
||||||
+ signatureMedian + " "
|
signatureMedian, verificationMedian));
|
||||||
+ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long median(List<Long> list) {
|
private static long median(List<Long> list) {
|
||||||
|
|||||||
@@ -3,40 +3,32 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
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.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.junit.Test;
|
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;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
public class KeyAgreementTest extends BrambleTestCase {
|
public class KeyAgreementTest extends BrambleTestCase {
|
||||||
|
|
||||||
@Test
|
|
||||||
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();
|
|
||||||
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
|
@Test
|
||||||
public void testDeriveSharedSecret() throws Exception {
|
public void testDeriveSharedSecret() throws Exception {
|
||||||
SecureRandomProvider
|
CryptoComponent crypto =
|
||||||
secureRandomProvider = new TestSecureRandomProvider();
|
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] aPub = aPair.getPublic().getEncoded();
|
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] bPub = bPair.getPublic().getEncoded();
|
Random random = new Random();
|
||||||
SecretKey aShared = crypto.deriveSharedSecret(bPub, aPair, true);
|
byte[][] inputs = new byte[random.nextInt(10) + 1][];
|
||||||
SecretKey bShared = crypto.deriveSharedSecret(aPub, bPair, false);
|
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);
|
||||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
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.Bytes;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -16,35 +16,34 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class KeyDerivationTest extends BrambleTestCase {
|
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 TransportId transportId = new TransportId("id");
|
||||||
private final CryptoComponent crypto;
|
private final SecretKey master = getSecretKey();
|
||||||
private final SecretKey master;
|
|
||||||
|
|
||||||
public KeyDerivationTest() {
|
|
||||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
|
||||||
master = TestUtils.getSecretKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreDistinct() {
|
public void testKeysAreDistinct() {
|
||||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
assertAllDifferent(k);
|
assertAllDifferent(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCurrentKeysMatchCurrentKeysOfContact() {
|
public void testCurrentKeysMatchCurrentKeysOfContact() {
|
||||||
// Start in rotation period 123
|
// Start in rotation period 123
|
||||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, false);
|
master, 123, false);
|
||||||
// Alice's incoming keys should equal Bob's outgoing keys
|
// Alice's incoming keys should equal Bob's outgoing keys
|
||||||
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
||||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||||
@@ -56,8 +55,8 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
|
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
|
||||||
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
|
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
|
||||||
// Rotate into the future
|
// Rotate into the future
|
||||||
kA = crypto.rotateTransportKeys(kA, 456);
|
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||||
kB = crypto.rotateTransportKeys(kB, 456);
|
kB = transportCrypto.rotateTransportKeys(kB, 456);
|
||||||
// Alice's incoming keys should equal Bob's outgoing keys
|
// Alice's incoming keys should equal Bob's outgoing keys
|
||||||
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
||||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||||
@@ -73,22 +72,23 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testPreviousKeysMatchPreviousKeysOfContact() {
|
public void testPreviousKeysMatchPreviousKeysOfContact() {
|
||||||
// Start in rotation period 123
|
// Start in rotation period 123
|
||||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, false);
|
master, 123, false);
|
||||||
// Compare Alice's previous keys in period 456 with Bob's current keys
|
// Compare Alice's previous keys in period 456 with Bob's current keys
|
||||||
// in period 455
|
// in period 455
|
||||||
kA = crypto.rotateTransportKeys(kA, 456);
|
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||||
kB = crypto.rotateTransportKeys(kB, 455);
|
kB = transportCrypto.rotateTransportKeys(kB, 455);
|
||||||
// Alice's previous incoming keys should equal Bob's outgoing keys
|
// Alice's previous incoming keys should equal Bob's outgoing keys
|
||||||
assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(),
|
||||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||||
assertArrayEquals(kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
|
assertArrayEquals(
|
||||||
|
kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
|
||||||
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
|
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
|
||||||
// Compare Alice's current keys in period 456 with Bob's previous keys
|
// Compare Alice's current keys in period 456 with Bob's previous keys
|
||||||
// in period 457
|
// in period 457
|
||||||
kB = crypto.rotateTransportKeys(kB, 457);
|
kB = transportCrypto.rotateTransportKeys(kB, 457);
|
||||||
// Alice's outgoing keys should equal Bob's previous incoming keys
|
// Alice's outgoing keys should equal Bob's previous incoming keys
|
||||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
||||||
kB.getPreviousIncomingKeys().getTagKey().getBytes());
|
kB.getPreviousIncomingKeys().getTagKey().getBytes());
|
||||||
@@ -99,14 +99,14 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testNextKeysMatchNextKeysOfContact() {
|
public void testNextKeysMatchNextKeysOfContact() {
|
||||||
// Start in rotation period 123
|
// Start in rotation period 123
|
||||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, false);
|
master, 123, false);
|
||||||
// Compare Alice's current keys in period 456 with Bob's next keys in
|
// Compare Alice's current keys in period 456 with Bob's next keys in
|
||||||
// period 455
|
// period 455
|
||||||
kA = crypto.rotateTransportKeys(kA, 456);
|
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||||
kB = crypto.rotateTransportKeys(kB, 455);
|
kB = transportCrypto.rotateTransportKeys(kB, 455);
|
||||||
// Alice's outgoing keys should equal Bob's next incoming keys
|
// Alice's outgoing keys should equal Bob's next incoming keys
|
||||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
||||||
kB.getNextIncomingKeys().getTagKey().getBytes());
|
kB.getNextIncomingKeys().getTagKey().getBytes());
|
||||||
@@ -114,7 +114,7 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
kB.getNextIncomingKeys().getHeaderKey().getBytes());
|
kB.getNextIncomingKeys().getHeaderKey().getBytes());
|
||||||
// Compare Alice's next keys in period 456 with Bob's current keys
|
// Compare Alice's next keys in period 456 with Bob's current keys
|
||||||
// in period 457
|
// in period 457
|
||||||
kB = crypto.rotateTransportKeys(kB, 457);
|
kB = transportCrypto.rotateTransportKeys(kB, 457);
|
||||||
// Alice's next incoming keys should equal Bob's outgoing keys
|
// Alice's next incoming keys should equal Bob's outgoing keys
|
||||||
assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(),
|
assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(),
|
||||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||||
@@ -124,12 +124,12 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMasterKeyAffectsOutput() {
|
public void testMasterKeyAffectsOutput() {
|
||||||
SecretKey master1 = TestUtils.getSecretKey();
|
SecretKey master1 = getSecretKey();
|
||||||
assertFalse(Arrays.equals(master.getBytes(), master1.getBytes()));
|
assertFalse(Arrays.equals(master.getBytes(), master1.getBytes()));
|
||||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
TransportKeys k1 = crypto.deriveTransportKeys(transportId, master1,
|
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master1, 123, true);
|
||||||
assertAllDifferent(k, k1);
|
assertAllDifferent(k, k1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,10 +137,10 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
public void testTransportIdAffectsOutput() {
|
public void testTransportIdAffectsOutput() {
|
||||||
TransportId transportId1 = new TransportId("id1");
|
TransportId transportId1 = new TransportId("id1");
|
||||||
assertFalse(transportId.getString().equals(transportId1.getString()));
|
assertFalse(transportId.getString().equals(transportId1.getString()));
|
||||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||||
123, true);
|
master, 123, true);
|
||||||
TransportKeys k1 = crypto.deriveTransportKeys(transportId1, master,
|
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
|
||||||
123, true);
|
master, 123, true);
|
||||||
assertAllDifferent(k, k1);
|
assertAllDifferent(k, k1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,42 +4,49 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Arrays;
|
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.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
public class MacTest extends BrambleTestCase {
|
public class MacTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto =
|
||||||
|
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
|
|
||||||
private final SecretKey k = TestUtils.getSecretKey();
|
private final SecretKey key1 = getSecretKey(), key2 = getSecretKey();
|
||||||
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
private final String label1 = getRandomString(123);
|
||||||
private final byte[] inputBytes1 = TestUtils.getRandomBytes(234);
|
private final String label2 = getRandomString(123);
|
||||||
private final byte[] inputBytes2 = new byte[0];
|
private final byte[] input1 = getRandomBytes(123);
|
||||||
|
private final byte[] input2 = getRandomBytes(234);
|
||||||
public MacTest() {
|
private final byte[] input3 = new byte[0];
|
||||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIdenticalKeysAndInputsProduceIdenticalMacs() {
|
public void testIdenticalKeysAndInputsProduceIdenticalMacs() {
|
||||||
// Calculate the MAC twice - the results should be identical
|
// Calculate the MAC twice - the results should be identical
|
||||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||||
byte[] mac1 = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
byte[] mac1 = crypto.mac(label1, key1, input1, input2, input3);
|
||||||
assertArrayEquals(mac, mac1);
|
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
|
@Test
|
||||||
public void testDifferentKeysProduceDifferentMacs() {
|
public void testDifferentKeysProduceDifferentMacs() {
|
||||||
// Generate second random key
|
|
||||||
SecretKey k1 = TestUtils.getSecretKey();
|
|
||||||
// Calculate the MAC with each key - the results should be different
|
// Calculate the MAC with each key - the results should be different
|
||||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||||
byte[] mac1 = crypto.mac(k1, inputBytes, inputBytes1, inputBytes2);
|
byte[] mac1 = crypto.mac(label1, key2, input1, input2, input3);
|
||||||
assertFalse(Arrays.equals(mac, mac1));
|
assertFalse(Arrays.equals(mac, mac1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +54,8 @@ public class MacTest extends BrambleTestCase {
|
|||||||
public void testDifferentInputsProduceDifferentMacs() {
|
public void testDifferentInputsProduceDifferentMacs() {
|
||||||
// Calculate the MAC with the inputs in different orders - the results
|
// Calculate the MAC with the inputs in different orders - the results
|
||||||
// should be different
|
// should be different
|
||||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||||
byte[] mac1 = crypto.mac(k, inputBytes2, inputBytes1, inputBytes);
|
byte[] mac1 = crypto.mac(label1, key1, input3, input2, input1);
|
||||||
assertFalse(Arrays.equals(mac, mac1));
|
assertFalse(Arrays.equals(mac, mac1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,23 +8,32 @@ import org.briarproject.bramble.test.TestUtils;
|
|||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class SignatureTest extends BrambleTestCase {
|
public abstract class SignatureTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
protected final CryptoComponent crypto;
|
||||||
|
|
||||||
private final byte[] publicKey, privateKey;
|
private final byte[] publicKey, privateKey;
|
||||||
private final String label = StringUtils.getRandomString(42);
|
private final String label = StringUtils.getRandomString(42);
|
||||||
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
||||||
|
|
||||||
public SignatureTest() {
|
protected abstract KeyPair generateKeyPair();
|
||||||
|
|
||||||
|
protected abstract byte[] sign(String label, byte[] toSign,
|
||||||
|
byte[] privateKey) throws GeneralSecurityException;
|
||||||
|
|
||||||
|
protected abstract boolean verify(String label, byte[] signedData,
|
||||||
|
byte[] publicKey, byte[] signature) throws GeneralSecurityException;
|
||||||
|
|
||||||
|
SignatureTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
KeyPair k = crypto.generateSignatureKeyPair();
|
KeyPair k = generateKeyPair();
|
||||||
publicKey = k.getPublic().getEncoded();
|
publicKey = k.getPublic().getEncoded();
|
||||||
privateKey = k.getPrivate().getEncoded();
|
privateKey = k.getPrivate().getEncoded();
|
||||||
}
|
}
|
||||||
@@ -33,19 +42,19 @@ public class SignatureTest extends BrambleTestCase {
|
|||||||
public void testIdenticalKeysAndInputsProduceIdenticalSignatures()
|
public void testIdenticalKeysAndInputsProduceIdenticalSignatures()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// Calculate the Signature twice - the results should be identical
|
// Calculate the Signature twice - the results should be identical
|
||||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||||
byte[] sig2 = crypto.sign(label, inputBytes, privateKey);
|
byte[] sig2 = sign(label, inputBytes, privateKey);
|
||||||
assertArrayEquals(sig1, sig2);
|
assertArrayEquals(sig1, sig2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDifferentKeysProduceDifferentSignatures() throws Exception {
|
public void testDifferentKeysProduceDifferentSignatures() throws Exception {
|
||||||
// Generate second private key
|
// Generate second private key
|
||||||
KeyPair k2 = crypto.generateSignatureKeyPair();
|
KeyPair k2 = generateKeyPair();
|
||||||
byte[] privateKey2 = k2.getPrivate().getEncoded();
|
byte[] privateKey2 = k2.getPrivate().getEncoded();
|
||||||
// Calculate the signature with each key
|
// Calculate the signature with each key
|
||||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||||
byte[] sig2 = crypto.sign(label, inputBytes, privateKey2);
|
byte[] sig2 = sign(label, inputBytes, privateKey2);
|
||||||
assertFalse(Arrays.equals(sig1, sig2));
|
assertFalse(Arrays.equals(sig1, sig2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,8 +65,8 @@ public class SignatureTest extends BrambleTestCase {
|
|||||||
byte[] inputBytes2 = TestUtils.getRandomBytes(123);
|
byte[] inputBytes2 = TestUtils.getRandomBytes(123);
|
||||||
// Calculate the signature with different inputs
|
// Calculate the signature with different inputs
|
||||||
// the results should be different
|
// the results should be different
|
||||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||||
byte[] sig2 = crypto.sign(label, inputBytes2, privateKey);
|
byte[] sig2 = sign(label, inputBytes2, privateKey);
|
||||||
assertFalse(Arrays.equals(sig1, sig2));
|
assertFalse(Arrays.equals(sig1, sig2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,25 +77,25 @@ public class SignatureTest extends BrambleTestCase {
|
|||||||
String label2 = StringUtils.getRandomString(42);
|
String label2 = StringUtils.getRandomString(42);
|
||||||
// Calculate the signature with different inputs
|
// Calculate the signature with different inputs
|
||||||
// the results should be different
|
// the results should be different
|
||||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||||
byte[] sig2 = crypto.sign(label2, inputBytes, privateKey);
|
byte[] sig2 = sign(label2, inputBytes, privateKey);
|
||||||
assertFalse(Arrays.equals(sig1, sig2));
|
assertFalse(Arrays.equals(sig1, sig2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignatureVerification() throws Exception {
|
public void testSignatureVerification() throws Exception {
|
||||||
byte[] sig = crypto.sign(label, inputBytes, privateKey);
|
byte[] sig = sign(label, inputBytes, privateKey);
|
||||||
assertTrue(crypto.verify(label, inputBytes, publicKey, sig));
|
assertTrue(verify(label, inputBytes, publicKey, sig));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDifferentKeyFailsVerification() throws Exception {
|
public void testDifferentKeyFailsVerification() throws Exception {
|
||||||
// Generate second private key
|
// Generate second private key
|
||||||
KeyPair k2 = crypto.generateSignatureKeyPair();
|
KeyPair k2 = generateKeyPair();
|
||||||
byte[] privateKey2 = k2.getPrivate().getEncoded();
|
byte[] privateKey2 = k2.getPrivate().getEncoded();
|
||||||
// calculate the signature with different key, should fail to verify
|
// calculate the signature with different key, should fail to verify
|
||||||
byte[] sig = crypto.sign(label, inputBytes, privateKey2);
|
byte[] sig = sign(label, inputBytes, privateKey2);
|
||||||
assertFalse(crypto.verify(label, inputBytes, publicKey, sig));
|
assertFalse(verify(label, inputBytes, publicKey, sig));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -94,8 +103,8 @@ public class SignatureTest extends BrambleTestCase {
|
|||||||
// Generate a second input
|
// Generate a second input
|
||||||
byte[] inputBytes2 = TestUtils.getRandomBytes(123);
|
byte[] inputBytes2 = TestUtils.getRandomBytes(123);
|
||||||
// calculate the signature with different input, should fail to verify
|
// calculate the signature with different input, should fail to verify
|
||||||
byte[] sig = crypto.sign(label, inputBytes, privateKey);
|
byte[] sig = sign(label, inputBytes, privateKey);
|
||||||
assertFalse(crypto.verify(label, inputBytes2, publicKey, sig));
|
assertFalse(verify(label, inputBytes2, publicKey, sig));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -103,8 +112,8 @@ public class SignatureTest extends BrambleTestCase {
|
|||||||
// Generate a second label
|
// Generate a second label
|
||||||
String label2 = StringUtils.getRandomString(42);
|
String label2 = StringUtils.getRandomString(42);
|
||||||
// calculate the signature with different label, should fail to verify
|
// calculate the signature with different label, should fail to verify
|
||||||
byte[] sig = crypto.sign(label, inputBytes, privateKey);
|
byte[] sig = sign(label, inputBytes, privateKey);
|
||||||
assertFalse(crypto.verify(label2, inputBytes, publicKey, sig));
|
assertFalse(verify(label2, inputBytes, publicKey, sig));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.Bytes;
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -14,25 +13,25 @@ import java.util.Set;
|
|||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
|
|
||||||
public class TagEncodingTest extends BrambleTestCase {
|
public class TagEncodingTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||||
private final SecretKey tagKey;
|
|
||||||
|
private final TransportCrypto transportCrypto =
|
||||||
|
new TransportCryptoImpl(crypto);
|
||||||
|
private final SecretKey tagKey = getSecretKey();
|
||||||
private final long streamNumber = 1234567890;
|
private final long streamNumber = 1234567890;
|
||||||
|
|
||||||
public TagEncodingTest() {
|
|
||||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
|
||||||
tagKey = TestUtils.getSecretKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeyAffectsTag() throws Exception {
|
public void testKeyAffectsTag() throws Exception {
|
||||||
Set<Bytes> set = new HashSet<>();
|
Set<Bytes> set = new HashSet<>();
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
SecretKey tagKey = TestUtils.getSecretKey();
|
SecretKey tagKey = getSecretKey();
|
||||||
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber);
|
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION,
|
||||||
|
streamNumber);
|
||||||
assertTrue(set.add(new Bytes(tag)));
|
assertTrue(set.add(new Bytes(tag)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +41,8 @@ public class TagEncodingTest extends BrambleTestCase {
|
|||||||
Set<Bytes> set = new HashSet<>();
|
Set<Bytes> set = new HashSet<>();
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber);
|
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i,
|
||||||
|
streamNumber);
|
||||||
assertTrue(set.add(new Bytes(tag)));
|
assertTrue(set.add(new Bytes(tag)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,8 @@ public class TagEncodingTest extends BrambleTestCase {
|
|||||||
Set<Bytes> set = new HashSet<>();
|
Set<Bytes> set = new HashSet<>();
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i);
|
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION,
|
||||||
|
streamNumber + i);
|
||||||
assertTrue(set.add(new Bytes(tag)));
|
assertTrue(set.add(new Bytes(tag)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,380 @@
|
|||||||
|
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,345 +1,46 @@
|
|||||||
package org.briarproject.bramble.db;
|
package org.briarproject.bramble.db;
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
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.File;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.util.Properties;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static java.sql.Types.BINARY;
|
public class BasicH2Test extends BasicDatabaseTest {
|
||||||
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;
|
|
||||||
|
|
||||||
public class BasicH2Test extends BrambleTestCase {
|
private final SecretKey key = TestUtils.getSecretKey();
|
||||||
|
|
||||||
private static final String CREATE_TABLE =
|
@Override
|
||||||
"CREATE TABLE foo (uniqueId BINARY(32), name VARCHAR NOT NULL)";
|
protected String getBinaryType() {
|
||||||
private static final int BATCH_SIZE = 100;
|
return "BINARY(32)";
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Override
|
||||||
public void testInsertUpdateAndDelete() throws Exception {
|
protected String getDriverName() {
|
||||||
// Create the table
|
return "org.h2.Driver";
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Override
|
||||||
public void testBatchInsertUpdateAndDelete() throws Exception {
|
protected Connection openConnection(File db, boolean encrypt)
|
||||||
// Create the table
|
throws SQLException {
|
||||||
createTable(connection);
|
String url = "jdbc:h2:split:" + db.getAbsolutePath();
|
||||||
// Generate some IDs and two sets of names
|
Properties props = new Properties();
|
||||||
byte[][] ids = new byte[BATCH_SIZE][];
|
props.setProperty("user", "user");
|
||||||
String[] oldNames = new String[BATCH_SIZE];
|
if (encrypt) {
|
||||||
String[] newNames = new String[BATCH_SIZE];
|
url += ";CIPHER=AES";
|
||||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
String hex = StringUtils.toHexString(key.getBytes());
|
||||||
ids[i] = TestUtils.getRandomId();
|
props.setProperty("password", hex + " password");
|
||||||
oldNames[i] = StringUtils.getRandomString(50);
|
|
||||||
newNames[i] = StringUtils.getRandomString(50);
|
|
||||||
}
|
}
|
||||||
// Insert the IDs and old names into the table as a batch
|
return DriverManager.getConnection(url, props);
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Override
|
||||||
public void testSortOrder() throws Exception {
|
protected void shutdownDatabase(File db, boolean encrypt)
|
||||||
byte[] first = new byte[] {
|
throws SQLException {
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
// The DB is closed automatically when the connection is closed
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
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,11 +47,10 @@ import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
|||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.Mockery;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -74,7 +73,13 @@ import static org.junit.Assert.assertFalse;
|
|||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class DatabaseComponentImplTest extends BrambleTestCase {
|
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);
|
||||||
|
|
||||||
private final Object txn = new Object();
|
private final Object txn = new Object();
|
||||||
private final ClientId clientId;
|
private final ClientId clientId;
|
||||||
@@ -125,13 +130,8 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void testSimpleCalls() throws Exception {
|
public void testSimpleCalls() throws Exception {
|
||||||
int shutdownHandle = 12345;
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
// open()
|
// open()
|
||||||
oneOf(database).open();
|
oneOf(database).open();
|
||||||
@@ -194,7 +194,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
// endTransaction()
|
// endTransaction()
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
// close()
|
// close()
|
||||||
oneOf(shutdown).removeShutdownHook(shutdownHandle);
|
|
||||||
oneOf(database).close();
|
oneOf(database).close();
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
@@ -221,18 +220,11 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
db.close();
|
db.close();
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocalMessagesAreNotStoredUnlessGroupExists()
|
public void testLocalMessagesAreNotStoredUnlessGroupExists()
|
||||||
throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -252,17 +244,10 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddLocalMessage() throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -294,18 +279,11 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVariousMethodsThrowExceptionIfContactIsMissing()
|
public void testVariousMethodsThrowExceptionIfContactIsMissing()
|
||||||
throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the contact is in the DB (which it's not)
|
// Check whether the contact is in the DB (which it's not)
|
||||||
exactly(18).of(database).startTransaction();
|
exactly(18).of(database).startTransaction();
|
||||||
@@ -500,18 +478,11 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVariousMethodsThrowExceptionIfLocalAuthorIsMissing()
|
public void testVariousMethodsThrowExceptionIfLocalAuthorIsMissing()
|
||||||
throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the pseudonym is in the DB (which it's not)
|
// Check whether the pseudonym is in the DB (which it's not)
|
||||||
exactly(3).of(database).startTransaction();
|
exactly(3).of(database).startTransaction();
|
||||||
@@ -552,18 +523,11 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVariousMethodsThrowExceptionIfGroupIsMissing()
|
public void testVariousMethodsThrowExceptionIfGroupIsMissing()
|
||||||
throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the group is in the DB (which it's not)
|
// Check whether the group is in the DB (which it's not)
|
||||||
exactly(8).of(database).startTransaction();
|
exactly(8).of(database).startTransaction();
|
||||||
@@ -657,18 +621,11 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVariousMethodsThrowExceptionIfMessageIsMissing()
|
public void testVariousMethodsThrowExceptionIfMessageIsMissing()
|
||||||
throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the message is in the DB (which it's not)
|
// Check whether the message is in the DB (which it's not)
|
||||||
exactly(11).of(database).startTransaction();
|
exactly(11).of(database).startTransaction();
|
||||||
@@ -792,18 +749,11 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVariousMethodsThrowExceptionIfTransportIsMissing()
|
public void testVariousMethodsThrowExceptionIfTransportIsMissing()
|
||||||
throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
// startTransaction()
|
// startTransaction()
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -890,19 +840,12 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenerateAck() throws Exception {
|
public void testGenerateAck() throws Exception {
|
||||||
Collection<MessageId> messagesToAck = Arrays.asList(messageId,
|
Collection<MessageId> messagesToAck = Arrays.asList(messageId,
|
||||||
messageId1);
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -925,8 +868,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -934,11 +875,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
byte[] raw1 = new byte[size];
|
byte[] raw1 = new byte[size];
|
||||||
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
||||||
Collection<byte[]> messages = Arrays.asList(raw, raw1);
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -969,19 +905,12 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenerateOffer() throws Exception {
|
public void testGenerateOffer() throws Exception {
|
||||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||||
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1007,19 +936,12 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenerateRequest() throws Exception {
|
public void testGenerateRequest() throws Exception {
|
||||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||||
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1042,8 +964,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1051,11 +971,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
byte[] raw1 = new byte[size];
|
byte[] raw1 = new byte[size];
|
||||||
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
||||||
Collection<byte[]> messages = Arrays.asList(raw, raw1);
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1087,17 +1002,10 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReceiveAck() throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1120,17 +1028,10 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReceiveMessage() throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1175,17 +1076,10 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReceiveDuplicateMessage() throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1212,17 +1106,10 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReceiveMessageWithoutVisibleGroup() throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1242,8 +1129,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1251,11 +1136,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||||
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
|
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
|
||||||
MessageId messageId3 = 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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1296,17 +1176,10 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReceiveRequest() throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1330,17 +1203,10 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testChangingVisibilityCallsListeners() throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1370,18 +1236,11 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotChangingVisibilityDoesNotCallListeners()
|
public void testNotChangingVisibilityDoesNotCallListeners()
|
||||||
throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1403,8 +1262,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1412,11 +1269,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
TransportKeys transportKeys = createTransportKeys();
|
TransportKeys transportKeys = createTransportKeys();
|
||||||
Map<ContactId, TransportKeys> keys = Collections.singletonMap(
|
Map<ContactId, TransportKeys> keys = Collections.singletonMap(
|
||||||
contactId, transportKeys);
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
// startTransaction()
|
// startTransaction()
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -1446,8 +1298,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransportKeys createTransportKeys() {
|
private TransportKeys createTransportKeys() {
|
||||||
@@ -1480,11 +1330,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
Settings merged = new Settings();
|
Settings merged = new Settings();
|
||||||
merged.put("foo", "bar");
|
merged.put("foo", "bar");
|
||||||
merged.put("baz", "qux");
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
// startTransaction()
|
// startTransaction()
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -1514,8 +1359,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalStateException.class)
|
@Test(expected = IllegalStateException.class)
|
||||||
@@ -1545,12 +1388,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
private void testCannotStartTransactionDuringTransaction(
|
private void testCannotStartTransactionDuringTransaction(
|
||||||
boolean firstTxnReadOnly, boolean secondTxnReadOnly)
|
boolean firstTxnReadOnly, boolean secondTxnReadOnly)
|
||||||
throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1560,22 +1397,12 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
shutdown);
|
shutdown);
|
||||||
|
|
||||||
assertNotNull(db.startTransaction(firstTxnReadOnly));
|
assertNotNull(db.startTransaction(firstTxnReadOnly));
|
||||||
try {
|
db.startTransaction(secondTxnReadOnly);
|
||||||
db.startTransaction(secondTxnReadOnly);
|
fail();
|
||||||
fail();
|
|
||||||
} finally {
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCannotAddLocalIdentityAsContact() throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1599,18 +1426,10 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCannotAddDuplicateContact() throws Exception {
|
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() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -1636,18 +1455,12 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void testMessageDependencies() throws Exception {
|
public void testMessageDependencies() throws Exception {
|
||||||
int shutdownHandle = 12345;
|
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());
|
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// open()
|
// open()
|
||||||
@@ -1693,7 +1506,6 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
// endTransaction()
|
// endTransaction()
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
// close()
|
// close()
|
||||||
oneOf(shutdown).removeShutdownHook(shutdownHandle);
|
|
||||||
oneOf(database).close();
|
oneOf(database).close();
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
@@ -1714,7 +1526,5 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
|
|||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
db.close();
|
db.close();
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class TransactionIsolationTest extends BrambleTestCase {
|
public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||||
|
|
||||||
private static final String DROP_TABLE = "DROP TABLE foo IF EXISTS";
|
private static final String DROP_TABLE = "DROP TABLE foo IF EXISTS";
|
||||||
private static final String CREATE_TABLE = "CREATE TABLE foo"
|
private static final String CREATE_TABLE = "CREATE TABLE foo"
|
||||||
@@ -34,9 +34,9 @@ public class TransactionIsolationTest extends BrambleTestCase {
|
|||||||
|
|
||||||
private final File testDir = TestUtils.getTestDirectory();
|
private final File testDir = TestUtils.getTestDirectory();
|
||||||
private final File db = new File(testDir, "db");
|
private final File db = new File(testDir, "db");
|
||||||
private final String withMvcc = "jdbc:h2:" + db.getAbsolutePath()
|
private final String withMvcc = "jdbc:h2:split:" + db.getAbsolutePath()
|
||||||
+ ";MV_STORE=TRUE;MVCC=TRUE";
|
+ ";MV_STORE=TRUE;MVCC=TRUE";
|
||||||
private final String withoutMvcc = "jdbc:h2:" + db.getAbsolutePath()
|
private final String withoutMvcc = "jdbc:h2:split:" + db.getAbsolutePath()
|
||||||
+ ";MV_STORE=FALSE;MVCC=FALSE;LOCK_MODE=1";
|
+ ";MV_STORE=FALSE;MVCC=FALSE;LOCK_MODE=1";
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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
@@ -1,13 +1,14 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
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.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.auto.Mock;
|
import org.jmock.auto.Mock;
|
||||||
import org.jmock.integration.junit4.JUnitRuleMockery;
|
import org.jmock.integration.junit4.JUnitRuleMockery;
|
||||||
@@ -16,6 +17,11 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_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.equalTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
@@ -28,34 +34,33 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
setImposteriser(ClassImposteriser.INSTANCE);
|
setImposteriser(ClassImposteriser.INSTANCE);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
private static final byte[] ALICE_PUBKEY = TestUtils.getRandomBytes(32);
|
private final PublicKey alicePubKey =
|
||||||
private static final byte[] ALICE_COMMIT =
|
context.mock(PublicKey.class, "alice");
|
||||||
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
private final byte[] alicePubKeyBytes = getRandomBytes(32);
|
||||||
private static final byte[] ALICE_PAYLOAD =
|
private final byte[] aliceCommit = getRandomBytes(COMMIT_LENGTH);
|
||||||
TestUtils.getRandomBytes(COMMIT_LENGTH + 8);
|
private final byte[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8);
|
||||||
|
private final byte[] aliceConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||||
|
|
||||||
private static final byte[] BOB_PUBKEY = TestUtils.getRandomBytes(32);
|
private final PublicKey bobPubKey = context.mock(PublicKey.class, "bob");
|
||||||
private static final byte[] BOB_COMMIT =
|
private final byte[] bobPubKeyBytes = getRandomBytes(32);
|
||||||
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
private final byte[] bobCommit = getRandomBytes(COMMIT_LENGTH);
|
||||||
private static final byte[] BOB_PAYLOAD =
|
private final byte[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19);
|
||||||
TestUtils.getRandomBytes(COMMIT_LENGTH + 19);
|
private final byte[] bobConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||||
|
|
||||||
private static final byte[] ALICE_CONFIRM =
|
private final PublicKey badPubKey = context.mock(PublicKey.class, "bad");
|
||||||
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
private final byte[] badPubKeyBytes = getRandomBytes(32);
|
||||||
private static final byte[] BOB_CONFIRM =
|
private final byte[] badCommit = getRandomBytes(COMMIT_LENGTH);
|
||||||
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
private final byte[] badConfirm = 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
|
@Mock
|
||||||
KeyAgreementProtocol.Callbacks callbacks;
|
KeyAgreementProtocol.Callbacks callbacks;
|
||||||
@Mock
|
@Mock
|
||||||
CryptoComponent crypto;
|
CryptoComponent crypto;
|
||||||
@Mock
|
@Mock
|
||||||
|
KeyAgreementCrypto keyAgreementCrypto;
|
||||||
|
@Mock
|
||||||
|
KeyParser keyParser;
|
||||||
|
@Mock
|
||||||
PayloadEncoder payloadEncoder;
|
PayloadEncoder payloadEncoder;
|
||||||
@Mock
|
@Mock
|
||||||
KeyAgreementTransport transport;
|
KeyAgreementTransport transport;
|
||||||
@@ -65,60 +70,72 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testAliceProtocol() throws Exception {
|
public void testAliceProtocol() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
Payload theirPayload = new Payload(bobCommit, null);
|
||||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
Payload ourPayload = new Payload(aliceCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
SecretKey masterSecret = TestUtils.getSecretKey();
|
SecretKey masterSecret = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
theirPayload, ourPayload, ourKeyPair, true);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(payloadEncoder).encode(ourPayload);
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
will(returnValue(ALICE_PAYLOAD));
|
will(returnValue(alicePayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(BOB_PAYLOAD));
|
will(returnValue(bobPayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(ALICE_PUBKEY));
|
will(returnValue(alicePubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
allowing(alicePubKey).getEncoded();
|
||||||
|
will(returnValue(alicePubKeyBytes));
|
||||||
|
allowing(bobPubKey).getEncoded();
|
||||||
|
will(returnValue(bobPubKeyBytes));
|
||||||
|
|
||||||
// Alice sends her public key
|
// Alice sends her public key
|
||||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||||
|
|
||||||
// Alice receives Bob's public key
|
// Alice receives Bob's public key
|
||||||
oneOf(callbacks).connectionWaiting();
|
oneOf(callbacks).connectionWaiting();
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(BOB_PUBKEY));
|
will(returnValue(bobPubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
|
||||||
|
will(returnValue(bobPubKey));
|
||||||
|
|
||||||
// Alice verifies Bob's public key
|
// Alice verifies Bob's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(bobPubKey);
|
||||||
will(returnValue(BOB_COMMIT));
|
will(returnValue(bobCommit));
|
||||||
|
|
||||||
// Alice computes shared secret
|
// Alice computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
||||||
|
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||||
|
alicePubKeyBytes, bobPubKeyBytes);
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Alice sends her confirmation record
|
// Alice sends her confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
|
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||||
will(returnValue(ALICE_CONFIRM));
|
true, true);
|
||||||
oneOf(transport).sendConfirm(ALICE_CONFIRM);
|
will(returnValue(aliceConfirm));
|
||||||
|
oneOf(transport).sendConfirm(aliceConfirm);
|
||||||
|
|
||||||
// Alice receives Bob's confirmation record
|
// Alice receives Bob's confirmation record
|
||||||
oneOf(transport).receiveConfirm();
|
oneOf(transport).receiveConfirm();
|
||||||
will(returnValue(BOB_CONFIRM));
|
will(returnValue(bobConfirm));
|
||||||
|
|
||||||
// Alice verifies Bob's confirmation record
|
// Alice verifies Bob's confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
|
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||||
will(returnValue(BOB_CONFIRM));
|
true, false);
|
||||||
|
will(returnValue(bobConfirm));
|
||||||
|
|
||||||
// Alice computes master secret
|
// Alice computes master secret
|
||||||
oneOf(crypto).deriveMasterSecret(sharedSecret);
|
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||||
will(returnValue(masterSecret));
|
will(returnValue(masterSecret));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
@@ -129,59 +146,71 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testBobProtocol() throws Exception {
|
public void testBobProtocol() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
Payload theirPayload = new Payload(aliceCommit, null);
|
||||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
Payload ourPayload = new Payload(bobCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
SecretKey masterSecret = TestUtils.getSecretKey();
|
SecretKey masterSecret = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
theirPayload, ourPayload, ourKeyPair, false);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(payloadEncoder).encode(ourPayload);
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
will(returnValue(BOB_PAYLOAD));
|
will(returnValue(bobPayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(ALICE_PAYLOAD));
|
will(returnValue(alicePayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(BOB_PUBKEY));
|
will(returnValue(bobPubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
allowing(alicePubKey).getEncoded();
|
||||||
|
will(returnValue(alicePubKeyBytes));
|
||||||
|
allowing(bobPubKey).getEncoded();
|
||||||
|
will(returnValue(bobPubKeyBytes));
|
||||||
|
|
||||||
// Bob receives Alice's public key
|
// Bob receives Alice's public key
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(ALICE_PUBKEY));
|
will(returnValue(alicePubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
|
||||||
|
will(returnValue(alicePubKey));
|
||||||
|
|
||||||
// Bob verifies Alice's public key
|
// Bob verifies Alice's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(alicePubKey);
|
||||||
will(returnValue(ALICE_COMMIT));
|
will(returnValue(aliceCommit));
|
||||||
|
|
||||||
// Bob sends his public key
|
// Bob sends his public key
|
||||||
oneOf(transport).sendKey(BOB_PUBKEY);
|
oneOf(transport).sendKey(bobPubKeyBytes);
|
||||||
|
|
||||||
// Bob computes shared secret
|
// Bob computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
||||||
|
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||||
|
alicePubKeyBytes, bobPubKeyBytes);
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Bob receives Alices's confirmation record
|
// Bob receives Alices's confirmation record
|
||||||
oneOf(transport).receiveConfirm();
|
oneOf(transport).receiveConfirm();
|
||||||
will(returnValue(ALICE_CONFIRM));
|
will(returnValue(aliceConfirm));
|
||||||
|
|
||||||
// Bob verifies Alice's confirmation record
|
// Bob verifies Alice's confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
|
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||||
will(returnValue(ALICE_CONFIRM));
|
false, true);
|
||||||
|
will(returnValue(aliceConfirm));
|
||||||
|
|
||||||
// Bob sends his confirmation record
|
// Bob sends his confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
|
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||||
will(returnValue(BOB_CONFIRM));
|
false, false);
|
||||||
oneOf(transport).sendConfirm(BOB_CONFIRM);
|
will(returnValue(bobConfirm));
|
||||||
|
oneOf(transport).sendConfirm(bobConfirm);
|
||||||
|
|
||||||
// Bob computes master secret
|
// Bob computes master secret
|
||||||
oneOf(crypto).deriveMasterSecret(sharedSecret);
|
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||||
will(returnValue(masterSecret));
|
will(returnValue(masterSecret));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
@@ -192,38 +221,44 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testAliceProtocolAbortOnBadKey() throws Exception {
|
public void testAliceProtocolAbortOnBadKey() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
Payload theirPayload = new Payload(bobCommit, null);
|
||||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
Payload ourPayload = new Payload(aliceCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
theirPayload, ourPayload, ourKeyPair, true);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(ALICE_PUBKEY));
|
will(returnValue(alicePubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
|
||||||
// Alice sends her public key
|
// Alice sends her public key
|
||||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||||
|
|
||||||
// Alice receives a bad public key
|
// Alice receives a bad public key
|
||||||
oneOf(callbacks).connectionWaiting();
|
oneOf(callbacks).connectionWaiting();
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(BAD_PUBKEY));
|
will(returnValue(badPubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
|
||||||
|
will(returnValue(badPubKey));
|
||||||
|
|
||||||
// Alice verifies Bob's public key
|
// Alice verifies Bob's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(badPubKey);
|
||||||
will(returnValue(BAD_COMMIT));
|
will(returnValue(badCommit));
|
||||||
|
|
||||||
// Alice aborts
|
// Alice aborts
|
||||||
oneOf(transport).sendAbort(false);
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
// Alice never computes shared secret
|
// Alice never computes shared secret
|
||||||
never(crypto).deriveSharedSecret(BAD_PUBKEY, ourKeyPair, true);
|
never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey,
|
||||||
|
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||||
|
alicePubKeyBytes, bobPubKeyBytes);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
@@ -233,34 +268,38 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testBobProtocolAbortOnBadKey() throws Exception {
|
public void testBobProtocolAbortOnBadKey() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
Payload theirPayload = new Payload(aliceCommit, null);
|
||||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
Payload ourPayload = new Payload(bobCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
theirPayload, ourPayload, ourKeyPair, false);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(BOB_PUBKEY));
|
will(returnValue(bobPubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
|
||||||
// Bob receives a bad public key
|
// Bob receives a bad public key
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(BAD_PUBKEY));
|
will(returnValue(badPubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
|
||||||
|
will(returnValue(badPubKey));
|
||||||
|
|
||||||
// Bob verifies Alice's public key
|
// Bob verifies Alice's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(badPubKey);
|
||||||
will(returnValue(BAD_COMMIT));
|
will(returnValue(badCommit));
|
||||||
|
|
||||||
// Bob aborts
|
// Bob aborts
|
||||||
oneOf(transport).sendAbort(false);
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
// Bob never sends his public key
|
// Bob never sends his public key
|
||||||
never(transport).sendKey(BOB_PUBKEY);
|
never(transport).sendKey(bobPubKeyBytes);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
@@ -270,62 +309,72 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
|
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
Payload theirPayload = new Payload(bobCommit, null);
|
||||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
Payload ourPayload = new Payload(aliceCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
theirPayload, ourPayload, ourKeyPair, true);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(payloadEncoder).encode(ourPayload);
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
will(returnValue(ALICE_PAYLOAD));
|
will(returnValue(alicePayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(BOB_PAYLOAD));
|
will(returnValue(bobPayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(ALICE_PUBKEY));
|
will(returnValue(alicePubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
allowing(bobPubKey).getEncoded();
|
||||||
|
will(returnValue(bobPubKeyBytes));
|
||||||
|
|
||||||
// Alice sends her public key
|
// Alice sends her public key
|
||||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||||
|
|
||||||
// Alice receives Bob's public key
|
// Alice receives Bob's public key
|
||||||
oneOf(callbacks).connectionWaiting();
|
oneOf(callbacks).connectionWaiting();
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(BOB_PUBKEY));
|
will(returnValue(bobPubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
|
||||||
|
will(returnValue(bobPubKey));
|
||||||
|
|
||||||
// Alice verifies Bob's public key
|
// Alice verifies Bob's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(bobPubKey);
|
||||||
will(returnValue(BOB_COMMIT));
|
will(returnValue(bobCommit));
|
||||||
|
|
||||||
// Alice computes shared secret
|
// Alice computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
||||||
|
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||||
|
alicePubKeyBytes, bobPubKeyBytes);
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Alice sends her confirmation record
|
// Alice sends her confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
|
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||||
will(returnValue(ALICE_CONFIRM));
|
true, true);
|
||||||
oneOf(transport).sendConfirm(ALICE_CONFIRM);
|
will(returnValue(aliceConfirm));
|
||||||
|
oneOf(transport).sendConfirm(aliceConfirm);
|
||||||
|
|
||||||
// Alice receives a bad confirmation record
|
// Alice receives a bad confirmation record
|
||||||
oneOf(transport).receiveConfirm();
|
oneOf(transport).receiveConfirm();
|
||||||
will(returnValue(BAD_CONFIRM));
|
will(returnValue(badConfirm));
|
||||||
|
|
||||||
// Alice verifies Bob's confirmation record
|
// Alice verifies Bob's confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
|
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||||
will(returnValue(BOB_CONFIRM));
|
true, false);
|
||||||
|
will(returnValue(bobConfirm));
|
||||||
|
|
||||||
// Alice aborts
|
// Alice aborts
|
||||||
oneOf(transport).sendAbort(false);
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
// Alice never computes master secret
|
// Alice never computes master secret
|
||||||
never(crypto).deriveMasterSecret(sharedSecret);
|
never(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
@@ -335,56 +384,66 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
public void testBobProtocolAbortOnBadConfirm() throws Exception {
|
public void testBobProtocolAbortOnBadConfirm() throws Exception {
|
||||||
// set up
|
// set up
|
||||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
Payload theirPayload = new Payload(aliceCommit, null);
|
||||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
Payload ourPayload = new Payload(bobCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol =
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
theirPayload, ourPayload, ourKeyPair, false);
|
||||||
|
|
||||||
// expectations
|
// expectations
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Helpers
|
// Helpers
|
||||||
allowing(payloadEncoder).encode(ourPayload);
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
will(returnValue(BOB_PAYLOAD));
|
will(returnValue(bobPayload));
|
||||||
allowing(payloadEncoder).encode(theirPayload);
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
will(returnValue(ALICE_PAYLOAD));
|
will(returnValue(alicePayload));
|
||||||
allowing(ourPubKey).getEncoded();
|
allowing(ourPubKey).getEncoded();
|
||||||
will(returnValue(BOB_PUBKEY));
|
will(returnValue(bobPubKeyBytes));
|
||||||
|
allowing(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
allowing(alicePubKey).getEncoded();
|
||||||
|
will(returnValue(alicePubKeyBytes));
|
||||||
|
|
||||||
// Bob receives Alice's public key
|
// Bob receives Alice's public key
|
||||||
oneOf(transport).receiveKey();
|
oneOf(transport).receiveKey();
|
||||||
will(returnValue(ALICE_PUBKEY));
|
will(returnValue(alicePubKeyBytes));
|
||||||
oneOf(callbacks).initialRecordReceived();
|
oneOf(callbacks).initialRecordReceived();
|
||||||
|
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
|
||||||
|
will(returnValue(alicePubKey));
|
||||||
|
|
||||||
// Bob verifies Alice's public key
|
// Bob verifies Alice's public key
|
||||||
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
|
oneOf(keyAgreementCrypto).deriveKeyCommitment(alicePubKey);
|
||||||
will(returnValue(ALICE_COMMIT));
|
will(returnValue(aliceCommit));
|
||||||
|
|
||||||
// Bob sends his public key
|
// Bob sends his public key
|
||||||
oneOf(transport).sendKey(BOB_PUBKEY);
|
oneOf(transport).sendKey(bobPubKeyBytes);
|
||||||
|
|
||||||
// Bob computes shared secret
|
// Bob computes shared secret
|
||||||
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
|
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
||||||
|
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||||
|
alicePubKeyBytes, bobPubKeyBytes);
|
||||||
will(returnValue(sharedSecret));
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
// Bob receives a bad confirmation record
|
// Bob receives a bad confirmation record
|
||||||
oneOf(transport).receiveConfirm();
|
oneOf(transport).receiveConfirm();
|
||||||
will(returnValue(BAD_CONFIRM));
|
will(returnValue(badConfirm));
|
||||||
|
|
||||||
// Bob verifies Alice's confirmation record
|
// Bob verifies Alice's confirmation record
|
||||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
|
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||||
will(returnValue(ALICE_CONFIRM));
|
false, true);
|
||||||
|
will(returnValue(aliceConfirm));
|
||||||
|
|
||||||
// Bob aborts
|
// Bob aborts
|
||||||
oneOf(transport).sendAbort(false);
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
// Bob never sends his confirmation record
|
// Bob never sends his confirmation record
|
||||||
never(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
never(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
|
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||||
|
false, false);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
|
|||||||
@@ -150,17 +150,14 @@ public class LanTcpPluginTest extends BrambleTestCase {
|
|||||||
int port = ss.getLocalPort();
|
int port = ss.getLocalPort();
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
AtomicBoolean error = new AtomicBoolean(false);
|
AtomicBoolean error = new AtomicBoolean(false);
|
||||||
new Thread() {
|
new Thread(() -> {
|
||||||
@Override
|
try {
|
||||||
public void run() {
|
ss.accept();
|
||||||
try {
|
latch.countDown();
|
||||||
ss.accept();
|
} catch (IOException e) {
|
||||||
latch.countDown();
|
error.set(true);
|
||||||
} catch (IOException e) {
|
|
||||||
error.set(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.start();
|
}).start();
|
||||||
// Tell the plugin about the port
|
// Tell the plugin about the port
|
||||||
TransportProperties p = new TransportProperties();
|
TransportProperties p = new TransportProperties();
|
||||||
p.put("ipPorts", addrString + ":" + port);
|
p.put("ipPorts", addrString + ":" + port);
|
||||||
@@ -243,17 +240,14 @@ public class LanTcpPluginTest extends BrambleTestCase {
|
|||||||
ss.bind(new InetSocketAddress(addrString, 0), 10);
|
ss.bind(new InetSocketAddress(addrString, 0), 10);
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
AtomicBoolean error = new AtomicBoolean(false);
|
AtomicBoolean error = new AtomicBoolean(false);
|
||||||
new Thread() {
|
new Thread(() -> {
|
||||||
@Override
|
try {
|
||||||
public void run() {
|
ss.accept();
|
||||||
try {
|
latch.countDown();
|
||||||
ss.accept();
|
} catch (IOException e) {
|
||||||
latch.countDown();
|
error.set(true);
|
||||||
} catch (IOException e) {
|
|
||||||
error.set(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.start();
|
}).start();
|
||||||
// Tell the plugin about the port
|
// Tell the plugin about the port
|
||||||
BdfList descriptor = new BdfList();
|
BdfList descriptor = new BdfList();
|
||||||
descriptor.add(TRANSPORT_ID_LAN);
|
descriptor.add(TRANSPORT_ID_LAN);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ 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_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_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_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.Group.Visibility.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||||
@@ -78,7 +79,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
private TransportPropertyManagerImpl createInstance() {
|
private TransportPropertyManagerImpl createInstance() {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID);
|
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION);
|
||||||
will(returnValue(localGroup));
|
will(returnValue(localGroup));
|
||||||
}});
|
}});
|
||||||
return new TransportPropertyManagerImpl(db, clientHelper,
|
return new TransportPropertyManagerImpl(db, clientHelper,
|
||||||
@@ -98,12 +100,14 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(db).getContacts(txn);
|
oneOf(db).getContacts(txn);
|
||||||
will(returnValue(contacts));
|
will(returnValue(contacts));
|
||||||
// The first contact's group has already been set up
|
// The first contact's group has already been set up
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact1);
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION, contact1);
|
||||||
will(returnValue(contactGroup1));
|
will(returnValue(contactGroup1));
|
||||||
oneOf(db).containsGroup(txn, contactGroup1.getId());
|
oneOf(db).containsGroup(txn, contactGroup1.getId());
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
// The second contact's group hasn't been set up
|
// The second contact's group hasn't been set up
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact2);
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION, contact2);
|
||||||
will(returnValue(contactGroup2));
|
will(returnValue(contactGroup2));
|
||||||
oneOf(db).containsGroup(txn, contactGroup2.getId());
|
oneOf(db).containsGroup(txn, contactGroup2.getId());
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
@@ -130,7 +134,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Create the group and share it with the contact
|
// Create the group and share it with the contact
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION, contact);
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
oneOf(db).containsGroup(txn, contactGroup.getId());
|
oneOf(db).containsGroup(txn, contactGroup.getId());
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
@@ -156,7 +161,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
Group contactGroup = getGroup();
|
Group contactGroup = getGroup();
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION, contact);
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
oneOf(db).removeGroup(txn, contactGroup);
|
oneOf(db).removeGroup(txn, contactGroup);
|
||||||
}});
|
}});
|
||||||
@@ -215,6 +221,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
long timestamp = 123456789;
|
long timestamp = 123456789;
|
||||||
Message message = getMessage(contactGroupId, timestamp);
|
Message message = getMessage(contactGroupId, timestamp);
|
||||||
Metadata meta = new Metadata();
|
Metadata meta = new Metadata();
|
||||||
|
// Version 4 is being delivered
|
||||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||||
new BdfEntry("transportId", "foo"),
|
new BdfEntry("transportId", "foo"),
|
||||||
new BdfEntry("version", 4),
|
new BdfEntry("version", 4),
|
||||||
@@ -222,19 +229,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
);
|
);
|
||||||
Map<MessageId, BdfDictionary> messageMetadata =
|
Map<MessageId, BdfDictionary> messageMetadata =
|
||||||
new LinkedHashMap<>();
|
new LinkedHashMap<>();
|
||||||
// Old remote updates for the same transport should be deleted
|
// An older remote update for the same transport should be deleted
|
||||||
MessageId fooVersion2 = new MessageId(getRandomId());
|
|
||||||
messageMetadata.put(fooVersion2, BdfDictionary.of(
|
|
||||||
new BdfEntry("transportId", "foo"),
|
|
||||||
new BdfEntry("version", 2),
|
|
||||||
new BdfEntry("local", false)
|
|
||||||
));
|
|
||||||
MessageId fooVersion1 = new MessageId(getRandomId());
|
|
||||||
messageMetadata.put(fooVersion1, BdfDictionary.of(
|
|
||||||
new BdfEntry("transportId", "foo"),
|
|
||||||
new BdfEntry("version", 1),
|
|
||||||
new BdfEntry("local", false)
|
|
||||||
));
|
|
||||||
MessageId fooVersion3 = new MessageId(getRandomId());
|
MessageId fooVersion3 = new MessageId(getRandomId());
|
||||||
messageMetadata.put(fooVersion3, BdfDictionary.of(
|
messageMetadata.put(fooVersion3, BdfDictionary.of(
|
||||||
new BdfEntry("transportId", "foo"),
|
new BdfEntry("transportId", "foo"),
|
||||||
@@ -248,11 +243,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroupId);
|
contactGroupId);
|
||||||
will(returnValue(messageMetadata));
|
will(returnValue(messageMetadata));
|
||||||
// Versions 1-3 should be deleted
|
// The previous update (version 3) should be deleted
|
||||||
oneOf(db).deleteMessage(txn, fooVersion1);
|
|
||||||
oneOf(db).deleteMessageMetadata(txn, fooVersion1);
|
|
||||||
oneOf(db).deleteMessage(txn, fooVersion2);
|
|
||||||
oneOf(db).deleteMessageMetadata(txn, fooVersion2);
|
|
||||||
oneOf(db).deleteMessage(txn, fooVersion3);
|
oneOf(db).deleteMessage(txn, fooVersion3);
|
||||||
oneOf(db).deleteMessageMetadata(txn, fooVersion3);
|
oneOf(db).deleteMessageMetadata(txn, fooVersion3);
|
||||||
}});
|
}});
|
||||||
@@ -268,6 +259,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
long timestamp = 123456789;
|
long timestamp = 123456789;
|
||||||
Message message = getMessage(contactGroupId, timestamp);
|
Message message = getMessage(contactGroupId, timestamp);
|
||||||
Metadata meta = new Metadata();
|
Metadata meta = new Metadata();
|
||||||
|
// Version 3 is being delivered
|
||||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||||
new BdfEntry("transportId", "foo"),
|
new BdfEntry("transportId", "foo"),
|
||||||
new BdfEntry("version", 3),
|
new BdfEntry("version", 3),
|
||||||
@@ -275,19 +267,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
);
|
);
|
||||||
Map<MessageId, BdfDictionary> messageMetadata =
|
Map<MessageId, BdfDictionary> messageMetadata =
|
||||||
new LinkedHashMap<>();
|
new LinkedHashMap<>();
|
||||||
// Old remote updates for the same transport should be deleted
|
|
||||||
MessageId fooVersion2 = new MessageId(getRandomId());
|
|
||||||
messageMetadata.put(fooVersion2, BdfDictionary.of(
|
|
||||||
new BdfEntry("transportId", "foo"),
|
|
||||||
new BdfEntry("version", 2),
|
|
||||||
new BdfEntry("local", false)
|
|
||||||
));
|
|
||||||
MessageId fooVersion1 = new MessageId(getRandomId());
|
|
||||||
messageMetadata.put(fooVersion1, BdfDictionary.of(
|
|
||||||
new BdfEntry("transportId", "foo"),
|
|
||||||
new BdfEntry("version", 1),
|
|
||||||
new BdfEntry("local", false)
|
|
||||||
));
|
|
||||||
// A newer remote update for the same transport should not be deleted
|
// A newer remote update for the same transport should not be deleted
|
||||||
MessageId fooVersion4 = new MessageId(getRandomId());
|
MessageId fooVersion4 = new MessageId(getRandomId());
|
||||||
messageMetadata.put(fooVersion4, BdfDictionary.of(
|
messageMetadata.put(fooVersion4, BdfDictionary.of(
|
||||||
@@ -302,11 +281,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroupId);
|
contactGroupId);
|
||||||
will(returnValue(messageMetadata));
|
will(returnValue(messageMetadata));
|
||||||
// Versions 1 and 2 should be deleted, version 4 should not
|
|
||||||
oneOf(db).deleteMessage(txn, fooVersion1);
|
|
||||||
oneOf(db).deleteMessageMetadata(txn, fooVersion1);
|
|
||||||
oneOf(db).deleteMessage(txn, fooVersion2);
|
|
||||||
oneOf(db).deleteMessageMetadata(txn, fooVersion2);
|
|
||||||
// The update being delivered (version 3) should be deleted
|
// The update being delivered (version 3) should be deleted
|
||||||
oneOf(db).deleteMessage(txn, message.getId());
|
oneOf(db).deleteMessage(txn, message.getId());
|
||||||
oneOf(db).deleteMessageMetadata(txn, message.getId());
|
oneOf(db).deleteMessageMetadata(txn, message.getId());
|
||||||
@@ -329,7 +303,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(db).getContact(txn, contact.getId());
|
oneOf(db).getContact(txn, contact.getId());
|
||||||
will(returnValue(contact));
|
will(returnValue(contact));
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION, contact);
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
}});
|
}});
|
||||||
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
|
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
|
||||||
@@ -343,7 +318,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReturnsLatestLocalProperties() throws Exception {
|
public void testReturnsLatestLocalProperties() throws Exception {
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, true);
|
||||||
|
|
||||||
expectGetLocalProperties(txn);
|
expectGetLocalProperties(txn);
|
||||||
|
|
||||||
@@ -357,7 +332,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testReturnsEmptyPropertiesIfNoLocalPropertiesAreFound()
|
public void testReturnsEmptyPropertiesIfNoLocalPropertiesAreFound()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, true);
|
||||||
Map<MessageId, BdfDictionary> messageMetadata =
|
Map<MessageId, BdfDictionary> messageMetadata =
|
||||||
new LinkedHashMap<>();
|
new LinkedHashMap<>();
|
||||||
// A local update for another transport should be ignored
|
// A local update for another transport should be ignored
|
||||||
@@ -369,7 +344,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
));
|
));
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(db).startTransaction(false);
|
oneOf(db).startTransaction(true);
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
localGroup.getId());
|
localGroup.getId());
|
||||||
@@ -384,7 +359,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReturnsLocalProperties() throws Exception {
|
public void testReturnsLocalProperties() throws Exception {
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, true);
|
||||||
Map<MessageId, BdfDictionary> messageMetadata =
|
Map<MessageId, BdfDictionary> messageMetadata =
|
||||||
new LinkedHashMap<>();
|
new LinkedHashMap<>();
|
||||||
// A local update for another transport should be ignored
|
// A local update for another transport should be ignored
|
||||||
@@ -404,7 +379,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict);
|
BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(db).startTransaction(false);
|
oneOf(db).startTransaction(true);
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
localGroup.getId());
|
localGroup.getId());
|
||||||
@@ -423,7 +398,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testReturnsRemotePropertiesOrEmptyProperties()
|
public void testReturnsRemotePropertiesOrEmptyProperties()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, true);
|
||||||
Contact contact1 = getContact(false);
|
Contact contact1 = getContact(false);
|
||||||
Contact contact2 = getContact(true);
|
Contact contact2 = getContact(true);
|
||||||
Contact contact3 = getContact(true);
|
Contact contact3 = getContact(true);
|
||||||
@@ -457,19 +432,21 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict);
|
BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(db).startTransaction(false);
|
oneOf(db).startTransaction(true);
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
oneOf(db).getContacts(txn);
|
oneOf(db).getContacts(txn);
|
||||||
will(returnValue(contacts));
|
will(returnValue(contacts));
|
||||||
// First contact: skipped because not active
|
// First contact: skipped because not active
|
||||||
// Second contact: no updates
|
// Second contact: no updates
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact2);
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION, contact2);
|
||||||
will(returnValue(contactGroup2));
|
will(returnValue(contactGroup2));
|
||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroup2.getId());
|
contactGroup2.getId());
|
||||||
will(returnValue(Collections.emptyMap()));
|
will(returnValue(Collections.emptyMap()));
|
||||||
// Third contact: returns an update
|
// Third contact: returns an update
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact3);
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION, contact3);
|
||||||
will(returnValue(contactGroup3));
|
will(returnValue(contactGroup3));
|
||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroup3.getId());
|
contactGroup3.getId());
|
||||||
@@ -539,7 +516,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
// Store the new properties in each contact's group, version 1
|
// Store the new properties in each contact's group, version 1
|
||||||
oneOf(db).getContacts(txn);
|
oneOf(db).getContacts(txn);
|
||||||
will(returnValue(Collections.singletonList(contact)));
|
will(returnValue(Collections.singletonList(contact)));
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION, contact);
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroup.getId());
|
contactGroup.getId());
|
||||||
@@ -591,7 +569,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
// Store the merged properties in each contact's group, version 2
|
// Store the merged properties in each contact's group, version 2
|
||||||
oneOf(db).getContacts(txn);
|
oneOf(db).getContacts(txn);
|
||||||
will(returnValue(Collections.singletonList(contact)));
|
will(returnValue(Collections.singletonList(contact)));
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
CLIENT_VERSION, contact);
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroup.getId());
|
contactGroup.getId());
|
||||||
@@ -638,28 +617,14 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
return new Message(messageId, g, timestamp, raw);
|
return new Message(messageId, g, timestamp, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expectGetLocalProperties(Transaction txn)
|
private void expectGetLocalProperties(Transaction txn) throws Exception {
|
||||||
throws Exception {
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
Map<MessageId, BdfDictionary> messageMetadata =
|
// The latest update for transport "foo" should be returned
|
||||||
new LinkedHashMap<>();
|
|
||||||
// The only update for transport "foo" should be returned
|
|
||||||
MessageId fooVersion999 = new MessageId(getRandomId());
|
MessageId fooVersion999 = new MessageId(getRandomId());
|
||||||
messageMetadata.put(fooVersion999, BdfDictionary.of(
|
messageMetadata.put(fooVersion999, BdfDictionary.of(
|
||||||
new BdfEntry("transportId", "foo"),
|
new BdfEntry("transportId", "foo"),
|
||||||
new BdfEntry("version", 999)
|
new BdfEntry("version", 999)
|
||||||
));
|
));
|
||||||
// An old update for transport "bar" should be deleted
|
|
||||||
MessageId barVersion2 = new MessageId(getRandomId());
|
|
||||||
messageMetadata.put(barVersion2, BdfDictionary.of(
|
|
||||||
new BdfEntry("transportId", "bar"),
|
|
||||||
new BdfEntry("version", 2)
|
|
||||||
));
|
|
||||||
// An even older update for transport "bar" should be deleted
|
|
||||||
MessageId barVersion1 = new MessageId(getRandomId());
|
|
||||||
messageMetadata.put(barVersion1, BdfDictionary.of(
|
|
||||||
new BdfEntry("transportId", "bar"),
|
|
||||||
new BdfEntry("version", 1)
|
|
||||||
));
|
|
||||||
// The latest update for transport "bar" should be returned
|
// The latest update for transport "bar" should be returned
|
||||||
MessageId barVersion3 = new MessageId(getRandomId());
|
MessageId barVersion3 = new MessageId(getRandomId());
|
||||||
messageMetadata.put(barVersion3, BdfDictionary.of(
|
messageMetadata.put(barVersion3, BdfDictionary.of(
|
||||||
@@ -674,8 +639,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
localGroup.getId());
|
localGroup.getId());
|
||||||
will(returnValue(messageMetadata));
|
will(returnValue(messageMetadata));
|
||||||
oneOf(db).removeMessage(txn, barVersion1);
|
|
||||||
oneOf(db).removeMessage(txn, barVersion2);
|
|
||||||
// Retrieve and parse the latest local properties
|
// Retrieve and parse the latest local properties
|
||||||
oneOf(clientHelper).getMessageAsList(txn, fooVersion999);
|
oneOf(clientHelper).getMessageAsList(txn, fooVersion999);
|
||||||
will(returnValue(fooUpdate));
|
will(returnValue(fooUpdate));
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.briarproject.bramble.sync;
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
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.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.sync.Ack;
|
import org.briarproject.bramble.api.sync.Ack;
|
||||||
import org.briarproject.bramble.api.sync.ClientId;
|
import org.briarproject.bramble.api.sync.ClientId;
|
||||||
@@ -22,7 +22,6 @@ import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -37,6 +36,7 @@ import javax.inject.Inject;
|
|||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
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.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -57,7 +57,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
|||||||
@Inject
|
@Inject
|
||||||
RecordWriterFactory recordWriterFactory;
|
RecordWriterFactory recordWriterFactory;
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponent crypto;
|
TransportCrypto transportCrypto;
|
||||||
|
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
@@ -79,9 +79,11 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
|||||||
headerKey = TestUtils.getSecretKey();
|
headerKey = TestUtils.getSecretKey();
|
||||||
streamNumber = 123;
|
streamNumber = 123;
|
||||||
// Create a group
|
// Create a group
|
||||||
ClientId clientId = new ClientId(StringUtils.getRandomString(5));
|
ClientId clientId = new ClientId(getRandomString(123));
|
||||||
|
int clientVersion = 1234567890;
|
||||||
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
|
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
|
||||||
Group group = groupFactory.createGroup(clientId, descriptor);
|
Group group = groupFactory.createGroup(clientId, clientVersion,
|
||||||
|
descriptor);
|
||||||
// Add two messages to the group
|
// Add two messages to the group
|
||||||
long timestamp = System.currentTimeMillis();
|
long timestamp = System.currentTimeMillis();
|
||||||
byte[] body = "Hello world".getBytes("UTF-8");
|
byte[] body = "Hello world".getBytes("UTF-8");
|
||||||
@@ -117,7 +119,8 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
|||||||
private void read(byte[] connectionData) throws Exception {
|
private void read(byte[] connectionData) throws Exception {
|
||||||
// Calculate the expected tag
|
// Calculate the expected tag
|
||||||
byte[] expectedTag = new byte[TAG_LENGTH];
|
byte[] expectedTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
|
transportCrypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION,
|
||||||
|
streamNumber);
|
||||||
|
|
||||||
// Read the tag
|
// Read the tag
|
||||||
InputStream in = new ByteArrayInputStream(connectionData);
|
InputStream in = new ByteArrayInputStream(connectionData);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.briarproject.bramble.transport;
|
package org.briarproject.bramble.transport;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
@@ -11,12 +11,11 @@ import org.briarproject.bramble.api.transport.IncomingKeys;
|
|||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.RunAction;
|
import org.briarproject.bramble.test.RunAction;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.Mockery;
|
|
||||||
import org.jmock.api.Action;
|
import org.jmock.api.Action;
|
||||||
import org.jmock.api.Invocation;
|
import org.jmock.api.Invocation;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -41,7 +40,15 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
public class TransportKeyManagerImplTest extends BrambleTestCase {
|
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);
|
||||||
|
|
||||||
private final TransportId transportId = new TransportId("id");
|
private final TransportId transportId = new TransportId("id");
|
||||||
private final long maxLatency = 30 * 1000; // 30 seconds
|
private final long maxLatency = 30 * 1000; // 30 seconds
|
||||||
@@ -55,14 +62,6 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedAtStartup() throws Exception {
|
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<>();
|
Map<ContactId, TransportKeys> loaded = new LinkedHashMap<>();
|
||||||
TransportKeys shouldRotate = createTransportKeys(900, 0);
|
TransportKeys shouldRotate = createTransportKeys(900, 0);
|
||||||
TransportKeys shouldNotRotate = createTransportKeys(1000, 0);
|
TransportKeys shouldNotRotate = createTransportKeys(1000, 0);
|
||||||
@@ -79,14 +78,15 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).getTransportKeys(txn, transportId);
|
oneOf(db).getTransportKeys(txn, transportId);
|
||||||
will(returnValue(loaded));
|
will(returnValue(loaded));
|
||||||
// Rotate the transport keys
|
// Rotate the transport keys
|
||||||
oneOf(crypto).rotateTransportKeys(shouldRotate, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(shouldRotate, 1000);
|
||||||
will(returnValue(rotated));
|
will(returnValue(rotated));
|
||||||
oneOf(crypto).rotateTransportKeys(shouldNotRotate, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
|
||||||
will(returnValue(shouldNotRotate));
|
will(returnValue(shouldNotRotate));
|
||||||
// Encode the tags (3 sets per contact)
|
// Encode the tags (3 sets per contact)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(6).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys that were rotated
|
// Save the keys that were rotated
|
||||||
@@ -97,161 +97,124 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
with(rotationPeriodLength - 1), with(MILLISECONDS));
|
with(rotationPeriodLength - 1), with(MILLISECONDS));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
transportKeyManager.start(txn);
|
transportKeyManager.start(txn);
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedWhenAddingContact() throws Exception {
|
public void testKeysAreRotatedWhenAddingContact() throws Exception {
|
||||||
Mockery context = new Mockery();
|
boolean alice = random.nextBoolean();
|
||||||
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 transportKeys = createTransportKeys(999, 0);
|
||||||
TransportKeys rotated = createTransportKeys(1000, 0);
|
TransportKeys rotated = createTransportKeys(1000, 0);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 999,
|
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||||
alice);
|
999, alice);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (1 ms after start of rotation period 1000)
|
// Get the current time (1 ms after start of rotation period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1000 + 1));
|
will(returnValue(rotationPeriodLength * 1000 + 1));
|
||||||
// Rotate the transport keys
|
// Rotate the transport keys
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(rotated));
|
will(returnValue(rotated));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, rotated);
|
oneOf(db).addTransportKeys(txn, contactId, rotated);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
// The timestamp is 1 ms before the start of rotation period 1000
|
// The timestamp is 1 ms before the start of rotation period 1000
|
||||||
long timestamp = rotationPeriodLength * 1000 - 1;
|
long timestamp = rotationPeriodLength * 1000 - 1;
|
||||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||||
alice);
|
alice);
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
||||||
throws Exception {
|
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);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
|
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Mockery context = new Mockery();
|
boolean alice = random.nextBoolean();
|
||||||
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
|
// The stream counter has been exhausted
|
||||||
TransportKeys transportKeys = createTransportKeys(1000,
|
TransportKeys transportKeys = createTransportKeys(1000,
|
||||||
MAX_32_BIT_UNSIGNED + 1);
|
MAX_32_BIT_UNSIGNED + 1);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||||
alice);
|
1000, alice);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of rotation period 1000)
|
// Get the current time (the start of rotation period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1000));
|
will(returnValue(rotationPeriodLength * 1000));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of rotation period 1000
|
// The timestamp is at the start of rotation period 1000
|
||||||
long timestamp = rotationPeriodLength * 1000;
|
long timestamp = rotationPeriodLength * 1000;
|
||||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||||
alice);
|
alice);
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOutgoingStreamCounterIsIncremented() throws Exception {
|
public void testOutgoingStreamCounterIsIncremented() throws Exception {
|
||||||
Mockery context = new Mockery();
|
boolean alice = random.nextBoolean();
|
||||||
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
|
// The stream counter can be used one more time before being exhausted
|
||||||
TransportKeys transportKeys = createTransportKeys(1000,
|
TransportKeys transportKeys = createTransportKeys(1000,
|
||||||
MAX_32_BIT_UNSIGNED);
|
MAX_32_BIT_UNSIGNED);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||||
alice);
|
1000, alice);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of rotation period 1000)
|
// Get the current time (the start of rotation period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1000));
|
will(returnValue(rotationPeriodLength * 1000));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
@@ -259,9 +222,9 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000);
|
oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of rotation period 1000
|
// The timestamp is at the start of rotation period 1000
|
||||||
long timestamp = rotationPeriodLength * 1000;
|
long timestamp = rotationPeriodLength * 1000;
|
||||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||||
@@ -277,94 +240,76 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
|
assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
|
||||||
// The second request should return null, the counter is exhausted
|
// The second request should return null, the counter is exhausted
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIncomingStreamContextIsNullIfTagIsNotFound()
|
public void testIncomingStreamContextIsNullIfTagIsNotFound()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Mockery context = new Mockery();
|
boolean alice = random.nextBoolean();
|
||||||
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);
|
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||||
alice);
|
1000, alice);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of rotation period 1000)
|
// Get the current time (the start of rotation period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1000));
|
will(returnValue(rotationPeriodLength * 1000));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of rotation period 1000
|
// The timestamp is at the start of rotation period 1000
|
||||||
long timestamp = rotationPeriodLength * 1000;
|
long timestamp = rotationPeriodLength * 1000;
|
||||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||||
alice);
|
alice);
|
||||||
assertNull(transportKeyManager.getStreamContext(txn,
|
assertNull(transportKeyManager.getStreamContext(txn,
|
||||||
new byte[TAG_LENGTH]));
|
new byte[TAG_LENGTH]));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTagIsNotRecognisedTwice() throws Exception {
|
public void testTagIsNotRecognisedTwice() throws Exception {
|
||||||
Mockery context = new Mockery();
|
boolean alice = random.nextBoolean();
|
||||||
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);
|
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||||
// Keep a copy of the tags
|
// Keep a copy of the tags
|
||||||
List<byte[]> tags = new ArrayList<>();
|
List<byte[]> tags = new ArrayList<>();
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||||
alice);
|
1000, alice);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Get the current time (the start of rotation period 1000)
|
// Get the current time (the start of rotation period 1000)
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1000));
|
will(returnValue(rotationPeriodLength * 1000));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction(tags));
|
will(new EncodeTagAction(tags));
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Save the keys
|
// Save the keys
|
||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
// Encode a new tag after sliding the window
|
// Encode a new tag after sliding the window
|
||||||
oneOf(crypto).encodeTag(with(any(byte[].class)),
|
oneOf(transportCrypto).encodeTag(with(any(byte[].class)),
|
||||||
with(tagKey), with(PROTOCOL_VERSION),
|
with(tagKey), with(PROTOCOL_VERSION),
|
||||||
with((long) REORDERING_WINDOW_SIZE));
|
with((long) REORDERING_WINDOW_SIZE));
|
||||||
will(new EncodeTagAction(tags));
|
will(new EncodeTagAction(tags));
|
||||||
@@ -373,9 +318,9 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
1, new byte[REORDERING_WINDOW_SIZE / 8]);
|
1, new byte[REORDERING_WINDOW_SIZE / 8]);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
// The timestamp is at the start of rotation period 1000
|
// The timestamp is at the start of rotation period 1000
|
||||||
long timestamp = rotationPeriodLength * 1000;
|
long timestamp = rotationPeriodLength * 1000;
|
||||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||||
@@ -395,20 +340,10 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
|
assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
|
||||||
// The second request should return null, the tag has already been used
|
// The second request should return null, the tag has already been used
|
||||||
assertNull(transportKeyManager.getStreamContext(txn, tag));
|
assertNull(transportKeyManager.getStreamContext(txn, tag));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeysAreRotatedToCurrentPeriod() throws Exception {
|
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);
|
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||||
Map<ContactId, TransportKeys> loaded =
|
Map<ContactId, TransportKeys> loaded =
|
||||||
Collections.singletonMap(contactId, transportKeys);
|
Collections.singletonMap(contactId, transportKeys);
|
||||||
@@ -424,12 +359,13 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).getTransportKeys(txn, transportId);
|
oneOf(db).getTransportKeys(txn, transportId);
|
||||||
will(returnValue(loaded));
|
will(returnValue(loaded));
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||||
will(returnValue(transportKeys));
|
will(returnValue(transportKeys));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Schedule key rotation at the start of the next rotation period
|
// Schedule key rotation at the start of the next rotation period
|
||||||
@@ -445,13 +381,14 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(rotationPeriodLength * 1001));
|
will(returnValue(rotationPeriodLength * 1001));
|
||||||
// Rotate the transport keys
|
// Rotate the transport keys
|
||||||
oneOf(crypto).rotateTransportKeys(with(any(TransportKeys.class)),
|
oneOf(transportCrypto).rotateTransportKeys(
|
||||||
with(1001L));
|
with(any(TransportKeys.class)), with(1001L));
|
||||||
will(returnValue(rotated));
|
will(returnValue(rotated));
|
||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(transportCrypto).encodeTag(
|
||||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
with(any(byte[].class)), with(tagKey),
|
||||||
|
with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys that were rotated
|
// Save the keys that were rotated
|
||||||
@@ -465,12 +402,10 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn1);
|
oneOf(db).endTransaction(txn1);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
TransportKeyManager
|
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
maxLatency);
|
||||||
transportKeyManager.start(txn);
|
transportKeyManager.start(txn);
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransportKeys createTransportKeys(long rotationPeriod,
|
private TransportKeys createTransportKeys(long rotationPeriod,
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ dependencyVerification {
|
|||||||
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||||
|
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||||
'net.java.dev.jna:jna-platform:4.4.0:jna-platform-4.4.0.jar:e9dda9e884fc107eb6367710540789a12dfa8ad28be9326b22ca6e352e325499',
|
'net.java.dev.jna:jna-platform:4.4.0:jna-platform-4.4.0.jar:e9dda9e884fc107eb6367710540789a12dfa8ad28be9326b22ca6e352e325499',
|
||||||
'net.java.dev.jna:jna:4.4.0:jna-4.4.0.jar:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
|
'net.java.dev.jna:jna:4.4.0:jna-4.4.0.jar:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
|
||||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ dependencies {
|
|||||||
}
|
}
|
||||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||||
implementation "com.android.support:support-annotations:$supportVersion"
|
implementation "com.android.support:support-annotations:$supportVersion"
|
||||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
implementation 'com.android.support.constraint:constraint-layout:1.1.0-beta3'
|
||||||
|
|
||||||
implementation('ch.acra:acra:4.8.5') {
|
implementation('ch.acra:acra:4.8.5') {
|
||||||
exclude module: 'support-v4'
|
exclude module: 'support-v4'
|
||||||
@@ -61,8 +61,8 @@ dependencyVerification {
|
|||||||
'ch.acra:acra:4.8.5:acra-4.8.5.aar:afd5b28934d5166b55f261c85685ad59e8a4ebe9ca1960906afaa8c76d8dc9eb',
|
'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',
|
'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.almworks.sqlite4java:sqlite4java:0.282:sqlite4java-0.282.jar:9e1d8dd83ca6003f841e3af878ce2dc7c22497493a7bb6d1b62ec1b0d0a83c05',
|
||||||
'com.android.support.constraint:constraint-layout-solver:1.0.2:constraint-layout-solver-1.0.2.jar:8c62525a9bc5cff5633a96cb9b32fffeccaf41b8841aa87fc22607070dea9b8d',
|
'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.0.2:constraint-layout-1.0.2.aar:b0c688cc2b7172608f8153a689d746da40f71e52d7e2fe2bfd9df2f92db77085',
|
'com.android.support.constraint:constraint-layout:1.1.0-beta3:constraint-layout-1.1.0-beta3.aar:1754a6bd135feae485aa2ebf9e170f0f3d3282b392f8aa3067d0ed668839db79',
|
||||||
'com.android.support:animated-vector-drawable:27.0.1:animated-vector-drawable-27.0.1.aar:365050110411c86c7eec86101b49ab53557ffe6667f60b19055f1d35c38a577b',
|
'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: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',
|
'com.android.support:cardview-v7:27.0.1:cardview-v7-27.0.1.aar:43fccd44086c51eaa9d78be2fcf0dfea1556c8876a6fd325ea8d24e860054202',
|
||||||
@@ -108,6 +108,7 @@ dependencyVerification {
|
|||||||
'nekohtml:xercesMinimal:1.9.6.2:xercesMinimal-1.9.6.2.jar:95b8b357d19f63797dd7d67622fd3f18374d64acbc6584faba1c7759a31e8438',
|
'nekohtml:xercesMinimal:1.9.6.2:xercesMinimal-1.9.6.2.jar:95b8b357d19f63797dd7d67622fd3f18374d64acbc6584faba1c7759a31e8438',
|
||||||
'net.bytebuddy:byte-buddy-agent:1.6.14:byte-buddy-agent-1.6.14.jar:c141a2d6809c3eeff4a43d25992826abccebdd4b793af3e7a5f346e88ae73a33',
|
'net.bytebuddy:byte-buddy-agent:1.6.14:byte-buddy-agent-1.6.14.jar:c141a2d6809c3eeff4a43d25992826abccebdd4b793af3e7a5f346e88ae73a33',
|
||||||
'net.bytebuddy:byte-buddy:1.6.14:byte-buddy-1.6.14.jar:917758b3c651e278a15a029ba1d42dbf802d8b0e1fe2aa4b81c5750c64f461c1',
|
'net.bytebuddy:byte-buddy:1.6.14:byte-buddy-1.6.14.jar:917758b3c651e278a15a029ba1d42dbf802d8b0e1fe2aa4b81c5750c64f461c1',
|
||||||
|
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||||
'org.apache.maven.wagon:wagon-file:1.0-beta-6:wagon-file-1.0-beta-6.jar:7298feeb36ff14dd933c38e62585fb9973fea32fb3c4bc5379428cb1aac5dd3c',
|
'org.apache.maven.wagon:wagon-file:1.0-beta-6:wagon-file-1.0-beta-6.jar:7298feeb36ff14dd933c38e62585fb9973fea32fb3c4bc5379428cb1aac5dd3c',
|
||||||
@@ -169,7 +170,7 @@ def getGitHash = { ->
|
|||||||
def stdout = new ByteArrayOutputStream()
|
def stdout = new ByteArrayOutputStream()
|
||||||
try {
|
try {
|
||||||
exec {
|
exec {
|
||||||
commandLine 'git', 'rev-parse', '--short', 'HEAD'
|
commandLine 'git', 'rev-parse', '--short=7', 'HEAD'
|
||||||
standardOutput = stdout
|
standardOutput = stdout
|
||||||
}
|
}
|
||||||
return stdout.toString().trim()
|
return stdout.toString().trim()
|
||||||
@@ -185,18 +186,18 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
versionCode 1612
|
versionCode 1700
|
||||||
versionName "0.16.12"
|
versionName "0.17.0"
|
||||||
applicationId "org.briarproject.briar.beta"
|
applicationId "org.briarproject.briar.android"
|
||||||
resValue "string", "app_package", "org.briarproject.briar.beta"
|
resValue "string", "app_package", "org.briarproject.briar.android"
|
||||||
resValue "string", "app_name", "Briar Beta"
|
resValue "string", "app_name", "Briar"
|
||||||
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
|
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
applicationIdSuffix ".debug"
|
applicationIdSuffix ".debug"
|
||||||
resValue "string", "app_package", "org.briarproject.briar.beta.debug"
|
resValue "string", "app_package", "org.briarproject.briar.android.debug"
|
||||||
resValue "string", "app_name", "Briar Debug"
|
resValue "string", "app_name", "Briar Debug"
|
||||||
shrinkResources false
|
shrinkResources false
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
@@ -125,7 +125,7 @@ public interface AndroidComponent
|
|||||||
|
|
||||||
ContactExchangeTask contactExchangeTask();
|
ContactExchangeTask contactExchangeTask();
|
||||||
|
|
||||||
KeyAgreementTaskFactory keyAgreementTaskFactory();
|
KeyAgreementTask keyAgreementTask();
|
||||||
|
|
||||||
PayloadEncoder payloadEncoder();
|
PayloadEncoder payloadEncoder();
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.StringRes;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.v4.app.TaskStackBuilder;
|
import android.support.v4.app.TaskStackBuilder;
|
||||||
|
|
||||||
@@ -44,6 +47,7 @@ import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
@@ -57,8 +61,10 @@ import javax.inject.Inject;
|
|||||||
import static android.app.Notification.DEFAULT_LIGHTS;
|
import static android.app.Notification.DEFAULT_LIGHTS;
|
||||||
import static android.app.Notification.DEFAULT_SOUND;
|
import static android.app.Notification.DEFAULT_SOUND;
|
||||||
import static android.app.Notification.DEFAULT_VIBRATE;
|
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.Context.NOTIFICATION_SERVICE;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
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_MESSAGE;
|
||||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
|
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
@@ -83,6 +89,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
private static final int BLOG_POST_NOTIFICATION_ID = 6;
|
private static final int BLOG_POST_NOTIFICATION_ID = 6;
|
||||||
private static final int INTRODUCTION_SUCCESS_NOTIFICATION_ID = 7;
|
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 long SOUND_DELAY = TimeUnit.SECONDS.toMillis(2);
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
@@ -91,8 +103,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final SettingsManager settingsManager;
|
private final SettingsManager settingsManager;
|
||||||
private final AndroidExecutor androidExecutor;
|
private final AndroidExecutor androidExecutor;
|
||||||
private final Context appContext;
|
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
|
private final Context appContext;
|
||||||
|
private final NotificationManager notificationManager;
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
// The following must only be accessed on the main UI thread
|
// The following must only be accessed on the main UI thread
|
||||||
@@ -121,6 +134,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
this.androidExecutor = androidExecutor;
|
this.androidExecutor = androidExecutor;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
appContext = app.getApplicationContext();
|
appContext = app.getApplicationContext();
|
||||||
|
notificationManager = (NotificationManager)
|
||||||
|
appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,6 +147,33 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
throw new ServiceException(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
|
@Override
|
||||||
@@ -156,44 +198,34 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
private void clearContactNotification() {
|
private void clearContactNotification() {
|
||||||
contactCounts.clear();
|
contactCounts.clear();
|
||||||
contactTotal = 0;
|
contactTotal = 0;
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
notificationManager.cancel(PRIVATE_MESSAGE_NOTIFICATION_ID);
|
||||||
NotificationManager nm = (NotificationManager) o;
|
|
||||||
nm.cancel(PRIVATE_MESSAGE_NOTIFICATION_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void clearGroupMessageNotification() {
|
private void clearGroupMessageNotification() {
|
||||||
groupCounts.clear();
|
groupCounts.clear();
|
||||||
groupTotal = 0;
|
groupTotal = 0;
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
notificationManager.cancel(GROUP_MESSAGE_NOTIFICATION_ID);
|
||||||
NotificationManager nm = (NotificationManager) o;
|
|
||||||
nm.cancel(GROUP_MESSAGE_NOTIFICATION_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void clearForumPostNotification() {
|
private void clearForumPostNotification() {
|
||||||
forumCounts.clear();
|
forumCounts.clear();
|
||||||
forumTotal = 0;
|
forumTotal = 0;
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
notificationManager.cancel(FORUM_POST_NOTIFICATION_ID);
|
||||||
NotificationManager nm = (NotificationManager) o;
|
|
||||||
nm.cancel(FORUM_POST_NOTIFICATION_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void clearBlogPostNotification() {
|
private void clearBlogPostNotification() {
|
||||||
blogCounts.clear();
|
blogCounts.clear();
|
||||||
blogTotal = 0;
|
blogTotal = 0;
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
notificationManager.cancel(BLOG_POST_NOTIFICATION_ID);
|
||||||
NotificationManager nm = (NotificationManager) o;
|
|
||||||
nm.cancel(BLOG_POST_NOTIFICATION_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void clearIntroductionSuccessNotification() {
|
private void clearIntroductionSuccessNotification() {
|
||||||
introductionTotal = 0;
|
introductionTotal = 0;
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
notificationManager.cancel(INTRODUCTION_SUCCESS_NOTIFICATION_ID);
|
||||||
NotificationManager nm = (NotificationManager) o;
|
|
||||||
nm.cancel(INTRODUCTION_SUCCESS_NOTIFICATION_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -269,8 +301,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
if (contactTotal == 0) {
|
if (contactTotal == 0) {
|
||||||
clearContactNotification();
|
clearContactNotification();
|
||||||
} else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) {
|
} else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) {
|
||||||
BriarNotificationBuilder b =
|
BriarNotificationBuilder b = new BriarNotificationBuilder(
|
||||||
new BriarNotificationBuilder(appContext);
|
appContext, CONTACT_CHANNEL_ID);
|
||||||
b.setSmallIcon(R.drawable.notification_private_message);
|
b.setSmallIcon(R.drawable.notification_private_message);
|
||||||
b.setColorRes(R.color.briar_primary);
|
b.setColorRes(R.color.briar_primary);
|
||||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||||
@@ -305,9 +337,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
t.addNextIntent(i);
|
t.addNextIntent(i);
|
||||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||||
}
|
}
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
notificationManager.notify(PRIVATE_MESSAGE_NOTIFICATION_ID,
|
||||||
NotificationManager nm = (NotificationManager) o;
|
b.build());
|
||||||
nm.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +409,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
clearGroupMessageNotification();
|
clearGroupMessageNotification();
|
||||||
} else if (settings.getBoolean(PREF_NOTIFY_GROUP, true)) {
|
} else if (settings.getBoolean(PREF_NOTIFY_GROUP, true)) {
|
||||||
BriarNotificationBuilder b =
|
BriarNotificationBuilder b =
|
||||||
new BriarNotificationBuilder(appContext);
|
new BriarNotificationBuilder(appContext, GROUP_CHANNEL_ID);
|
||||||
b.setSmallIcon(R.drawable.notification_private_group);
|
b.setSmallIcon(R.drawable.notification_private_group);
|
||||||
b.setColorRes(R.color.briar_primary);
|
b.setColorRes(R.color.briar_primary);
|
||||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||||
@@ -414,9 +445,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
t.addNextIntent(i);
|
t.addNextIntent(i);
|
||||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||||
}
|
}
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
notificationManager.notify(GROUP_MESSAGE_NOTIFICATION_ID,
|
||||||
NotificationManager nm = (NotificationManager) o;
|
b.build());
|
||||||
nm.notify(GROUP_MESSAGE_NOTIFICATION_ID, b.build());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +485,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
clearForumPostNotification();
|
clearForumPostNotification();
|
||||||
} else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) {
|
} else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) {
|
||||||
BriarNotificationBuilder b =
|
BriarNotificationBuilder b =
|
||||||
new BriarNotificationBuilder(appContext);
|
new BriarNotificationBuilder(appContext, FORUM_CHANNEL_ID);
|
||||||
b.setSmallIcon(R.drawable.notification_forum);
|
b.setSmallIcon(R.drawable.notification_forum);
|
||||||
b.setColorRes(R.color.briar_primary);
|
b.setColorRes(R.color.briar_primary);
|
||||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||||
@@ -491,9 +521,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
t.addNextIntent(i);
|
t.addNextIntent(i);
|
||||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||||
}
|
}
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
notificationManager.notify(FORUM_POST_NOTIFICATION_ID, b.build());
|
||||||
NotificationManager nm = (NotificationManager) o;
|
|
||||||
nm.notify(FORUM_POST_NOTIFICATION_ID, b.build());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,7 +560,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
clearBlogPostNotification();
|
clearBlogPostNotification();
|
||||||
} else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
|
} else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
|
||||||
BriarNotificationBuilder b =
|
BriarNotificationBuilder b =
|
||||||
new BriarNotificationBuilder(appContext);
|
new BriarNotificationBuilder(appContext, BLOG_CHANNEL_ID);
|
||||||
b.setSmallIcon(R.drawable.notification_blog);
|
b.setSmallIcon(R.drawable.notification_blog);
|
||||||
b.setColorRes(R.color.briar_primary);
|
b.setColorRes(R.color.briar_primary);
|
||||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||||
@@ -555,9 +583,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
t.addNextIntent(i);
|
t.addNextIntent(i);
|
||||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||||
|
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
notificationManager.notify(BLOG_POST_NOTIFICATION_ID, b.build());
|
||||||
NotificationManager nm = (NotificationManager) o;
|
|
||||||
nm.notify(BLOG_POST_NOTIFICATION_ID, b.build());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,7 +603,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void updateIntroductionNotification() {
|
private void updateIntroductionNotification() {
|
||||||
BriarNotificationBuilder b = new BriarNotificationBuilder(appContext);
|
BriarNotificationBuilder b =
|
||||||
|
new BriarNotificationBuilder(appContext, CONTACT_CHANNEL_ID);
|
||||||
b.setSmallIcon(R.drawable.notification_introduction);
|
b.setSmallIcon(R.drawable.notification_introduction);
|
||||||
b.setColorRes(R.color.briar_primary);
|
b.setColorRes(R.color.briar_primary);
|
||||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||||
@@ -599,9 +626,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
t.addNextIntent(i);
|
t.addNextIntent(i);
|
||||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||||
|
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
notificationManager.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID,
|
||||||
NotificationManager nm = (NotificationManager) o;
|
b.build());
|
||||||
nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ package org.briarproject.briar.android;
|
|||||||
*/
|
*/
|
||||||
public interface BriarApplication {
|
public interface BriarApplication {
|
||||||
|
|
||||||
// This build expires on 31 December 2017
|
// This build expires on 31 December 2018
|
||||||
long EXPIRY_DATE = 1514761200 * 1000L;
|
long EXPIRY_DATE = 1546214400 * 1000L;
|
||||||
|
|
||||||
AndroidComponent getApplicationComponent();
|
AndroidComponent getApplicationComponent();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
@@ -7,7 +8,6 @@ import android.content.ComponentName;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
@@ -25,9 +25,12 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
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.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
|
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.PRIORITY_MIN;
|
||||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||||
@@ -37,9 +40,21 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResul
|
|||||||
|
|
||||||
public class BriarService extends Service {
|
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 ONGOING_NOTIFICATION_ID = 1;
|
||||||
private static final int FAILURE_NOTIFICATION_ID = 2;
|
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 =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(BriarService.class.getName());
|
Logger.getLogger(BriarService.class.getName());
|
||||||
|
|
||||||
@@ -73,8 +88,26 @@ public class BriarService extends Service {
|
|||||||
stopSelf();
|
stopSelf();
|
||||||
return;
|
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);
|
||||||
|
}
|
||||||
// Show an ongoing notification that the service is running
|
// Show an ongoing notification that the service is running
|
||||||
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
|
NotificationCompat.Builder b =
|
||||||
|
new NotificationCompat.Builder(this, ONGOING_CHANNEL_ID);
|
||||||
b.setSmallIcon(R.drawable.notification_ongoing);
|
b.setSmallIcon(R.drawable.notification_ongoing);
|
||||||
b.setColor(ContextCompat.getColor(this, R.color.briar_primary));
|
b.setColor(ContextCompat.getColor(this, R.color.briar_primary));
|
||||||
b.setContentTitle(getText(R.string.ongoing_notification_title));
|
b.setContentTitle(getText(R.string.ongoing_notification_title));
|
||||||
@@ -84,37 +117,34 @@ public class BriarService extends Service {
|
|||||||
Intent i = new Intent(this, NavDrawerActivity.class);
|
Intent i = new Intent(this, NavDrawerActivity.class);
|
||||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (SDK_INT >= 21) {
|
||||||
b.setCategory(CATEGORY_SERVICE);
|
b.setCategory(CATEGORY_SERVICE);
|
||||||
b.setVisibility(VISIBILITY_SECRET);
|
b.setVisibility(VISIBILITY_SECRET);
|
||||||
}
|
}
|
||||||
b.setPriority(PRIORITY_MIN);
|
b.setPriority(PRIORITY_MIN);
|
||||||
startForeground(ONGOING_NOTIFICATION_ID, b.build());
|
startForeground(ONGOING_NOTIFICATION_ID, b.build());
|
||||||
// Start the services in a background thread
|
// Start the services in a background thread
|
||||||
new Thread() {
|
new Thread(() -> {
|
||||||
@Override
|
String nickname = databaseConfig.getLocalAuthorName();
|
||||||
public void run() {
|
StartResult result = lifecycleManager.startServices(nickname);
|
||||||
String nickname = databaseConfig.getLocalAuthorName();
|
if (result == SUCCESS) {
|
||||||
StartResult result = lifecycleManager.startServices(nickname);
|
started = true;
|
||||||
if (result == SUCCESS) {
|
} else if (result == ALREADY_RUNNING) {
|
||||||
started = true;
|
LOG.info("Already running");
|
||||||
} else if (result == ALREADY_RUNNING) {
|
stopSelf();
|
||||||
LOG.info("Already running");
|
} else {
|
||||||
stopSelf();
|
if (LOG.isLoggable(WARNING))
|
||||||
} else {
|
LOG.warning("Startup failed: " + result);
|
||||||
if (LOG.isLoggable(WARNING))
|
showStartupFailureNotification(result);
|
||||||
LOG.warning("Startup failed: " + result);
|
stopSelf();
|
||||||
showStartupFailureNotification(result);
|
|
||||||
stopSelf();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showStartupFailureNotification(StartResult result) {
|
private void showStartupFailureNotification(StartResult result) {
|
||||||
androidExecutor.runOnUiThread(() -> {
|
androidExecutor.runOnUiThread(() -> {
|
||||||
NotificationCompat.Builder b =
|
NotificationCompat.Builder b = new NotificationCompat.Builder(
|
||||||
new NotificationCompat.Builder(BriarService.this);
|
BriarService.this, FAILURE_CHANNEL_ID);
|
||||||
b.setSmallIcon(android.R.drawable.stat_notify_error);
|
b.setSmallIcon(android.R.drawable.stat_notify_error);
|
||||||
b.setContentTitle(getText(
|
b.setContentTitle(getText(
|
||||||
R.string.startup_failed_notification_title));
|
R.string.startup_failed_notification_title));
|
||||||
@@ -123,9 +153,8 @@ public class BriarService extends Service {
|
|||||||
Intent i = new Intent(BriarService.this,
|
Intent i = new Intent(BriarService.this,
|
||||||
StartupFailureActivity.class);
|
StartupFailureActivity.class);
|
||||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||||
i.putExtra("briar.START_RESULT", result);
|
i.putExtra(EXTRA_START_RESULT, result);
|
||||||
i.putExtra("briar.FAILURE_NOTIFICATION_ID",
|
i.putExtra(EXTRA_NOTIFICATION_ID, FAILURE_NOTIFICATION_ID);
|
||||||
FAILURE_NOTIFICATION_ID);
|
|
||||||
b.setContentIntent(PendingIntent.getActivity(BriarService.this,
|
b.setContentIntent(PendingIntent.getActivity(BriarService.this,
|
||||||
0, i, FLAG_UPDATE_CURRENT));
|
0, i, FLAG_UPDATE_CURRENT));
|
||||||
Object o = getSystemService(NOTIFICATION_SERVICE);
|
Object o = getSystemService(NOTIFICATION_SERVICE);
|
||||||
@@ -134,7 +163,7 @@ public class BriarService extends Service {
|
|||||||
// Bring the dashboard to the front to clear the back stack
|
// Bring the dashboard to the front to clear the back stack
|
||||||
i = new Intent(BriarService.this, NavDrawerActivity.class);
|
i = new Intent(BriarService.this, NavDrawerActivity.class);
|
||||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i.putExtra("briar.STARTUP_FAILED", true);
|
i.putExtra(EXTRA_STARTUP_FAILED, true);
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -155,12 +184,9 @@ public class BriarService extends Service {
|
|||||||
LOG.info("Destroyed");
|
LOG.info("Destroyed");
|
||||||
stopForeground(true);
|
stopForeground(true);
|
||||||
// Stop the services in a background thread
|
// Stop the services in a background thread
|
||||||
new Thread() {
|
new Thread(() -> {
|
||||||
@Override
|
if (started) lifecycleManager.stopServices();
|
||||||
public void run() {
|
}).start();
|
||||||
if (started) lifecycleManager.stopServices();
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import org.briarproject.briar.android.activity.ActivityComponent;
|
|||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
|
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 {
|
public class StartupFailureActivity extends BaseActivity {
|
||||||
|
|
||||||
@@ -27,8 +29,9 @@ public class StartupFailureActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleIntent(Intent i) {
|
private void handleIntent(Intent i) {
|
||||||
StartResult result = (StartResult) i.getSerializableExtra("briar.START_RESULT");
|
StartResult result =
|
||||||
int notificationId = i.getIntExtra("briar.FAILURE_NOTIFICATION_ID", -1);
|
(StartResult) i.getSerializableExtra(EXTRA_START_RESULT);
|
||||||
|
int notificationId = i.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
|
||||||
|
|
||||||
// cancel notification
|
// cancel notification
|
||||||
if (notificationId > -1) {
|
if (notificationId > -1) {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public interface TestingConstants {
|
|||||||
* Whether this is a beta build. This should be set to false for final
|
* Whether this is a beta build. This should be set to false for final
|
||||||
* release builds.
|
* release builds.
|
||||||
*/
|
*/
|
||||||
boolean IS_BETA_BUILD = true;
|
boolean IS_BETA_BUILD = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default log level. Disable logging for final release builds.
|
* Default log level. Disable logging for final release builds.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.os.Bundle;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.support.annotation.LayoutRes;
|
import android.support.annotation.LayoutRes;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -138,7 +139,9 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
dialogFrag =
|
dialogFrag =
|
||||||
ScreenFilterDialogFragment.newInstance(new ArrayList<>(apps));
|
ScreenFilterDialogFragment.newInstance(new ArrayList<>(apps));
|
||||||
dialogFrag.setCancelable(false);
|
dialogFrag.setCancelable(false);
|
||||||
dialogFrag.show(getSupportFragmentManager(), dialogFrag.getTag());
|
// Show dialog unless onSaveInstanceState() has been called, see #1112
|
||||||
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
|
if (!fm.isStateSaved()) dialogFrag.show(fm, dialogFrag.getTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ 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_EXCLUDE_FROM_RECENTS;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
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 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_DOZE_WHITELISTING;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
||||||
@@ -35,7 +36,6 @@ import static org.briarproject.briar.android.util.UiUtils.getDozeWhitelistingInt
|
|||||||
@SuppressLint("Registered")
|
@SuppressLint("Registered")
|
||||||
public abstract class BriarActivity extends BaseActivity {
|
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_ID = "briar.GROUP_ID";
|
||||||
public static final String GROUP_NAME = "briar.GROUP_NAME";
|
public static final String GROUP_NAME = "briar.GROUP_NAME";
|
||||||
|
|
||||||
@@ -79,6 +79,10 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
|
|
||||||
public void setSceneTransitionAnimation() {
|
public void setSceneTransitionAnimation() {
|
||||||
if (SDK_INT < 21) return;
|
if (SDK_INT < 21) return;
|
||||||
|
// workaround for #1007
|
||||||
|
if (SDK_INT == 24 && MANUFACTURER.equalsIgnoreCase("Samsung")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Transition slide = new Slide(Gravity.RIGHT);
|
Transition slide = new Slide(Gravity.RIGHT);
|
||||||
slide.excludeTarget(android.R.id.statusBarBackground, true);
|
slide.excludeTarget(android.R.id.statusBarBackground, true);
|
||||||
slide.excludeTarget(android.R.id.navigationBarBackground, true);
|
slide.excludeTarget(android.R.id.navigationBarBackground, true);
|
||||||
@@ -126,8 +130,8 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
b.setNegativeButton(R.string.cancel,
|
b.setNegativeButton(R.string.cancel,
|
||||||
(dialog, which) -> dialog.dismiss());
|
(dialog, which) -> dialog.dismiss());
|
||||||
b.setOnDismissListener(dialog -> {
|
b.setOnDismissListener(dialog -> {
|
||||||
CheckBox checkBox = (CheckBox) ((AlertDialog) dialog)
|
CheckBox checkBox =
|
||||||
.findViewById(R.id.checkbox);
|
((AlertDialog) dialog).findViewById(R.id.checkbox);
|
||||||
if (checkBox.isChecked())
|
if (checkBox.isChecked())
|
||||||
briarController.doNotAskAgainForDozeWhiteListing();
|
briarController.doNotAskAgainForDozeWhiteListing();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -123,25 +123,22 @@ public class BriarControllerImpl implements BriarController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void signOut(ResultHandler<Void> eventHandler) {
|
public void signOut(ResultHandler<Void> eventHandler) {
|
||||||
new Thread() {
|
new Thread(() -> {
|
||||||
@Override
|
try {
|
||||||
public void run() {
|
// Wait for the service to finish starting up
|
||||||
try {
|
IBinder binder = serviceConnection.waitForBinder();
|
||||||
// Wait for the service to finish starting up
|
BriarService service =
|
||||||
IBinder binder = serviceConnection.waitForBinder();
|
((BriarService.BriarBinder) binder).getService();
|
||||||
BriarService service =
|
service.waitForStartup();
|
||||||
((BriarService.BriarBinder) binder).getService();
|
// Shut down the service and wait for it to shut down
|
||||||
service.waitForStartup();
|
LOG.info("Shutting down service");
|
||||||
// Shut down the service and wait for it to shut down
|
service.shutdown();
|
||||||
LOG.info("Shutting down service");
|
service.waitForShutdown();
|
||||||
service.shutdown();
|
} catch (InterruptedException e) {
|
||||||
service.waitForShutdown();
|
LOG.warning("Interrupted while waiting for service");
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.warning("Interrupted while waiting for service");
|
|
||||||
}
|
|
||||||
eventHandler.onResult(null);
|
|
||||||
}
|
}
|
||||||
}.start();
|
eventHandler.onResult(null);
|
||||||
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unbindService() {
|
private void unbindService() {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.google.zxing.Result;
|
|||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
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.Payload;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||||
@@ -48,6 +47,7 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
||||||
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
|
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
|
||||||
@@ -68,7 +68,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
private static final Logger LOG = Logger.getLogger(TAG);
|
private static final Logger LOG = Logger.getLogger(TAG);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
KeyAgreementTaskFactory keyAgreementTaskFactory;
|
Provider<KeyAgreementTask> keyAgreementTaskProvider;
|
||||||
@Inject
|
@Inject
|
||||||
PayloadEncoder payloadEncoder;
|
PayloadEncoder payloadEncoder;
|
||||||
@Inject
|
@Inject
|
||||||
@@ -187,7 +187,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
@UiThread
|
@UiThread
|
||||||
private void startListening() {
|
private void startListening() {
|
||||||
KeyAgreementTask oldTask = task;
|
KeyAgreementTask oldTask = task;
|
||||||
KeyAgreementTask newTask = keyAgreementTaskFactory.createTask();
|
KeyAgreementTask newTask = keyAgreementTaskProvider.get();
|
||||||
task = newTask;
|
task = newTask;
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
if (oldTask != null) oldTask.stopListening();
|
if (oldTask != null) oldTask.stopListening();
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.login;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.login.PowerView.OnCheckedChangedListener;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
@@ -19,12 +21,15 @@ import static android.view.View.VISIBLE;
|
|||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
|
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
|
||||||
|
|
||||||
@TargetApi(23)
|
@NotNullByDefault
|
||||||
public class DozeFragment extends SetupFragment {
|
public class DozeFragment extends SetupFragment
|
||||||
|
implements OnCheckedChangedListener {
|
||||||
|
|
||||||
private final static String TAG = DozeFragment.class.getName();
|
private final static String TAG = DozeFragment.class.getName();
|
||||||
|
|
||||||
private Button dozeButton;
|
private DozeView dozeView;
|
||||||
|
private HuaweiView huaweiView;
|
||||||
|
private Button next;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private boolean secondAttempt = false;
|
private boolean secondAttempt = false;
|
||||||
|
|
||||||
@@ -33,15 +38,22 @@ public class DozeFragment extends SetupFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater,
|
||||||
Bundle savedInstanceState) {
|
@Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
getActivity().setTitle(getString(R.string.setup_doze_title));
|
getActivity().setTitle(getString(R.string.setup_doze_title));
|
||||||
|
setHasOptionsMenu(false);
|
||||||
View v = inflater.inflate(R.layout.fragment_setup_doze, container,
|
View v = inflater.inflate(R.layout.fragment_setup_doze, container,
|
||||||
false);
|
false);
|
||||||
dozeButton = v.findViewById(R.id.dozeButton);
|
dozeView = v.findViewById(R.id.dozeView);
|
||||||
|
dozeView.setOnCheckedChangedListener(this);
|
||||||
|
huaweiView = v.findViewById(R.id.huaweiView);
|
||||||
|
huaweiView.setOnCheckedChangedListener(this);
|
||||||
|
next = v.findViewById(R.id.next);
|
||||||
progressBar = v.findViewById(R.id.progress);
|
progressBar = v.findViewById(R.id.progress);
|
||||||
|
|
||||||
dozeButton.setOnClickListener(view -> askForDozeWhitelisting());
|
dozeView.setOnButtonClickListener(this::askForDozeWhitelisting);
|
||||||
|
next.setOnClickListener(this);
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@@ -65,25 +77,34 @@ public class DozeFragment extends SetupFragment {
|
|||||||
public void onActivityResult(int request, int result, Intent data) {
|
public void onActivityResult(int request, int result, Intent data) {
|
||||||
super.onActivityResult(request, result, data);
|
super.onActivityResult(request, result, data);
|
||||||
if (request == REQUEST_DOZE_WHITELISTING) {
|
if (request == REQUEST_DOZE_WHITELISTING) {
|
||||||
if (!setupController.needsDozeWhitelisting() || secondAttempt) {
|
if (!dozeView.needsToBeShown() || secondAttempt) {
|
||||||
dozeButton.setEnabled(false);
|
dozeView.setChecked(true);
|
||||||
onClick(dozeButton);
|
} else if (getContext() != null) {
|
||||||
} else {
|
|
||||||
secondAttempt = true;
|
secondAttempt = true;
|
||||||
showOnboardingDialog(getContext(), getHelpText());
|
showOnboardingDialog(getContext(), getHelpText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCheckedChanged() {
|
||||||
|
if (dozeView.isChecked() && huaweiView.isChecked()) {
|
||||||
|
next.setEnabled(true);
|
||||||
|
} else {
|
||||||
|
next.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("BatteryLife")
|
@SuppressLint("BatteryLife")
|
||||||
private void askForDozeWhitelisting() {
|
private void askForDozeWhitelisting() {
|
||||||
|
if (getContext() == null) return;
|
||||||
Intent i = UiUtils.getDozeWhitelistingIntent(getContext());
|
Intent i = UiUtils.getDozeWhitelistingIntent(getContext());
|
||||||
startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
|
startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
dozeButton.setVisibility(INVISIBLE);
|
next.setVisibility(INVISIBLE);
|
||||||
progressBar.setVisibility(VISIBLE);
|
progressBar.setVisibility(VISIBLE);
|
||||||
setupController.createAccount();
|
setupController.createAccount();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
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);
|
component.inject(this);
|
||||||
|
|
||||||
// the controller is not yet available in onCreateView()
|
// the controller is not yet available in onCreateView()
|
||||||
if (!setupController.needsDozeWhitelisting()) {
|
if (!setupController.needToShowDozeFragment()) {
|
||||||
nextButton.setText(R.string.create_account_button);
|
nextButton.setText(R.string.create_account_button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ public class PasswordFragment extends SetupFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
if (!setupController.needsDozeWhitelisting()) {
|
if (!setupController.needToShowDozeFragment()) {
|
||||||
nextButton.setVisibility(INVISIBLE);
|
nextButton.setVisibility(INVISIBLE);
|
||||||
progressBar.setVisibility(VISIBLE);
|
progressBar.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
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);
|
void setSetupActivity(SetupActivity setupActivity);
|
||||||
|
|
||||||
boolean needsDozeWhitelisting();
|
boolean needToShowDozeFragment();
|
||||||
|
|
||||||
void setAuthorName(String authorName);
|
void setAuthorName(String authorName);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@@ -41,9 +40,10 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean needsDozeWhitelisting() {
|
public boolean needToShowDozeFragment() {
|
||||||
if (setupActivity == null) throw new IllegalStateException();
|
if (setupActivity == null) throw new IllegalStateException();
|
||||||
return UiUtils.needsDozeWhitelisting(setupActivity);
|
return DozeView.needsToBeShown(setupActivity) ||
|
||||||
|
HuaweiView.needsToBeShown(setupActivity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,7 +61,7 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
|||||||
@Override
|
@Override
|
||||||
public void showDozeOrCreateAccount() {
|
public void showDozeOrCreateAccount() {
|
||||||
if (setupActivity == null) throw new IllegalStateException();
|
if (setupActivity == null) throw new IllegalStateException();
|
||||||
if (needsDozeWhitelisting()) {
|
if (needToShowDozeFragment()) {
|
||||||
setupActivity.showDozeFragment();
|
setupActivity.showDozeFragment();
|
||||||
} else {
|
} else {
|
||||||
createAccount();
|
createAccount();
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import static android.support.v4.view.GravityCompat.START;
|
|||||||
import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
|
import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
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.activity.RequestCodes.REQUEST_PASSWORD;
|
||||||
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.NO;
|
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.NO;
|
||||||
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.UPDATE;
|
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.UPDATE;
|
||||||
@@ -167,7 +168,7 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void exitIfStartupFailed(Intent intent) {
|
private void exitIfStartupFailed(Intent intent) {
|
||||||
if (intent.getBooleanExtra(KEY_STARTUP_FAILED, false)) {
|
if (intent.getBooleanExtra(EXTRA_STARTUP_FAILED, false)) {
|
||||||
finish();
|
finish();
|
||||||
LOG.info("Exiting");
|
LOG.info("Exiting");
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import javax.inject.Inject;
|
|||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
|
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
|
||||||
|
import static org.briarproject.briar.android.TestingConstants.IS_BETA_BUILD;
|
||||||
|
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||||
import static org.briarproject.briar.android.controller.BriarControllerImpl.DOZE_ASK_AGAIN;
|
import static org.briarproject.briar.android.controller.BriarControllerImpl.DOZE_ASK_AGAIN;
|
||||||
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.NO;
|
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.NO;
|
||||||
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.SHOW;
|
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.SHOW;
|
||||||
@@ -106,6 +108,10 @@ public class NavDrawerControllerImpl extends DbControllerImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showExpiryWarning(ResultHandler<ExpiryWarning> handler) {
|
public void showExpiryWarning(ResultHandler<ExpiryWarning> handler) {
|
||||||
|
if (!IS_DEBUG_BUILD && !IS_BETA_BUILD) {
|
||||||
|
handler.onResult(NO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
Settings settings =
|
Settings settings =
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
|||||||
|
|
||||||
public class BriarNotificationBuilder extends NotificationCompat.Builder {
|
public class BriarNotificationBuilder extends NotificationCompat.Builder {
|
||||||
|
|
||||||
public BriarNotificationBuilder(Context context) {
|
public BriarNotificationBuilder(Context context, String channelId) {
|
||||||
super(context);
|
super(context, channelId);
|
||||||
// Auto-cancel does not fire the delete intent, see
|
// Auto-cancel does not fire the delete intent, see
|
||||||
// https://issuetracker.google.com/issues/36961721
|
// https://issuetracker.google.com/issues/36961721
|
||||||
setAutoCancel(true);
|
setAutoCancel(true);
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<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>
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<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:hintEnabled="false"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<android.support.design.widget.TextInputEditText
|
<android.support.design.widget.TextInputEditText
|
||||||
android:id="@+id/nickname_entry"
|
android:id="@+id/nickname_entry"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<ScrollView
|
<ScrollView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fillViewport="true">
|
android:fillViewport="true">
|
||||||
@@ -16,38 +17,47 @@
|
|||||||
android:paddingStart="@dimen/margin_activity_horizontal"
|
android:paddingStart="@dimen/margin_activity_horizontal"
|
||||||
android:paddingTop="@dimen/margin_activity_vertical">
|
android:paddingTop="@dimen/margin_activity_vertical">
|
||||||
|
|
||||||
<TextView
|
<org.briarproject.briar.android.login.DozeView
|
||||||
android:id="@+id/setup_explanation"
|
android:id="@+id/dozeView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/setup_doze_intro"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
android:textSize="@dimen/text_size_medium"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="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
|
<Button
|
||||||
android:id="@+id/dozeButton"
|
android:id="@+id/next"
|
||||||
style="@style/BriarButton.Default"
|
style="@style/BriarButton.Default"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/margin_activity_horizontal"
|
android:enabled="false"
|
||||||
android:text="@string/setup_doze_button"
|
android:text="@string/create_account_button"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/setup_explanation"/>
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/huaweiView"
|
||||||
|
app:layout_constraintVertical_bias="1.0"
|
||||||
|
tools:enabled="true"/>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progress"
|
android:id="@+id/progress"
|
||||||
style="?android:attr/progressBarStyle"
|
style="?android:attr/progressBarStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:visibility="invisible"
|
android:visibility="invisible"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/dozeButton"
|
app:layout_constraintBottom_toBottomOf="@+id/next"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/dozeButton"/>
|
app:layout_constraintTop_toTopOf="@+id/next"/>
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
||||||
|
|
||||||
|
|||||||
56
briar-android/src/main/res/layout/power_view.xml
Normal file
56
briar-android/src/main/res/layout/power_view.xml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?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,6 +16,7 @@
|
|||||||
<color name="briar_primary_dark">@color/briar_blue_dark</color>
|
<color name="briar_primary_dark">@color/briar_blue_dark</color>
|
||||||
|
|
||||||
<color name="briar_accent">@color/briar_blue</color>
|
<color name="briar_accent">@color/briar_blue</color>
|
||||||
|
<color name="control_normal_light">#757575</color>
|
||||||
|
|
||||||
<!-- text colors -->
|
<!-- text colors -->
|
||||||
<color name="briar_text_link">#06b9ff</color>
|
<color name="briar_text_link">#06b9ff</color>
|
||||||
|
|||||||
@@ -21,6 +21,11 @@
|
|||||||
<string name="more_info">More Information</string>
|
<string name="more_info">More Information</string>
|
||||||
<string name="don_t_ask_again">Don\'t ask again</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 -->
|
<!-- Login -->
|
||||||
<string name="enter_password">Enter your password:</string>
|
<string name="enter_password">Enter your password:</string>
|
||||||
<string name="try_again">Wrong password, try again</string>
|
<string name="try_again">Wrong password, try again</string>
|
||||||
@@ -34,10 +39,10 @@
|
|||||||
<string name="startup_failed_db_error">For some reason, your Briar database is corrupted beyond repair. Your account, your data and all your contacts are lost. Unfortunately, you need to reinstall Briar and set up a new account.</string>
|
<string name="startup_failed_db_error">For some reason, your Briar database is corrupted beyond repair. Your account, your data and all your contacts are lost. Unfortunately, you need to reinstall Briar and set up a new account.</string>
|
||||||
<string name="startup_failed_service_error">Briar was unable to start a required plugin. Reinstalling Briar usually solves this problem. However, please note that you will then lose your account and all data associated with it since Briar is not using central servers to store your data on.</string>
|
<string name="startup_failed_service_error">Briar was unable to start a required plugin. Reinstalling Briar usually solves this problem. However, please note that you will then lose your account and all data associated with it since Briar is not using central servers to store your data on.</string>
|
||||||
<plurals name="expiry_warning">
|
<plurals name="expiry_warning">
|
||||||
<item quantity="one">This is a beta version of Briar. Your account will expire in %d day and cannot be renewed.</item>
|
<item quantity="one">This is a test version of Briar. Your account will expire in %d day and cannot be renewed.</item>
|
||||||
<item quantity="other">This is a beta version of Briar. Your account will expire in %d days and cannot be renewed.</item>
|
<item quantity="other">This is a test version of Briar. Your account will expire in %d days and cannot be renewed.</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="expiry_update">The beta expiry date has been extended. Your account will now expire in %d days.</string>
|
<string name="expiry_update">The testing expiry date has been extended. Your account will now expire in %d days.</string>
|
||||||
<string name="expiry_date_reached">This software has expired.\nThank you for testing!</string>
|
<string name="expiry_date_reached">This software has expired.\nThank you for testing!</string>
|
||||||
|
|
||||||
<!-- Navigation Drawer -->
|
<!-- Navigation Drawer -->
|
||||||
@@ -95,8 +100,8 @@
|
|||||||
<string name="ellipsis">…</string>
|
<string name="ellipsis">…</string>
|
||||||
<string name="text_too_long">The entered text is too long</string>
|
<string name="text_too_long">The entered text is too long</string>
|
||||||
<string name="show_onboarding">Show Help Dialog</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="fix">Fix</string>
|
||||||
|
<string name="help">Help</string>
|
||||||
|
|
||||||
<!-- Contacts and Private Conversations-->
|
<!-- 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>
|
<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,6 +5,7 @@
|
|||||||
<item name="colorPrimary">@color/briar_primary</item>
|
<item name="colorPrimary">@color/briar_primary</item>
|
||||||
<item name="colorPrimaryDark">@color/briar_primary_dark</item>
|
<item name="colorPrimaryDark">@color/briar_primary_dark</item>
|
||||||
<item name="colorAccent">@color/briar_accent</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:textColorLink">@color/briar_text_link</item>
|
||||||
<item name="android:windowBackground">@color/window_background</item>
|
<item name="android:windowBackground">@color/window_background</item>
|
||||||
<item name="android:windowAnimationStyle">@style/ActivityAnimation</item>
|
<item name="android:windowAnimationStyle">@style/ActivityAnimation</item>
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public class PasswordFragmentTest {
|
|||||||
String safePass = "really.safe.password";
|
String safePass = "really.safe.password";
|
||||||
|
|
||||||
passwordFragment.setupController = setupController;
|
passwordFragment.setupController = setupController;
|
||||||
when(setupController.needsDozeWhitelisting()).thenReturn(false);
|
when(setupController.needToShowDozeFragment()).thenReturn(false);
|
||||||
when(setupController.estimatePasswordStrength(safePass))
|
when(setupController.estimatePasswordStrength(safePass))
|
||||||
.thenReturn(STRONG);
|
.thenReturn(STRONG);
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,15 @@ import javax.annotation.Nullable;
|
|||||||
public interface BlogManager {
|
public interface BlogManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unique ID of the blog client.
|
* The unique ID of the blog client.
|
||||||
*/
|
*/
|
||||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog");
|
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).}
|
* Adds the given {@link Blog).}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface BlogPostFactory {
|
public interface BlogPostFactory {
|
||||||
|
|
||||||
String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
|
String SIGNING_LABEL_POST = CLIENT_ID.getString() + "/POST";
|
||||||
String SIGNING_LABEL_COMMENT = CLIENT_ID + "/COMMENT";
|
String SIGNING_LABEL_COMMENT = CLIENT_ID.getString() + "/COMMENT";
|
||||||
|
|
||||||
BlogPost createBlogPost(GroupId groupId, long timestamp,
|
BlogPost createBlogPost(GroupId groupId, long timestamp,
|
||||||
@Nullable MessageId parent, LocalAuthor author, String body)
|
@Nullable MessageId parent, LocalAuthor author, String body)
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ import org.briarproject.briar.api.sharing.SharingManager;
|
|||||||
|
|
||||||
public interface BlogSharingManager extends SharingManager<Blog> {
|
public interface BlogSharingManager extends SharingManager<Blog> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique ID of the blog sharing client.
|
||||||
|
*/
|
||||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog.sharing");
|
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog.sharing");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current version of the blog sharing client.
|
||||||
|
*/
|
||||||
|
int CLIENT_VERSION = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ public interface FeedManager {
|
|||||||
*/
|
*/
|
||||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.feed");
|
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.
|
* Adds an RSS feed as a new dedicated blog.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ public interface ForumManager {
|
|||||||
*/
|
*/
|
||||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum");
|
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current version of the forum client.
|
||||||
|
*/
|
||||||
|
int CLIENT_VERSION = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes to a forum.
|
* Subscribes to a forum.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user