mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
27 Commits
elliptic-c
...
beta-0.16.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
459538e40c | ||
|
|
183f501761 | ||
|
|
65ee5f539b | ||
|
|
604339326c | ||
|
|
0acec1343f | ||
|
|
0434756bbd | ||
|
|
e233433140 | ||
|
|
c63f285f53 | ||
|
|
0800188718 | ||
|
|
6188e48beb | ||
|
|
5726e29b56 | ||
|
|
5d70399de0 | ||
|
|
73202dde5e | ||
|
|
a98ac8233c | ||
|
|
bee3e244fc | ||
|
|
da25999a15 | ||
|
|
62049df342 | ||
|
|
024e5aa90f | ||
|
|
6d791481d5 | ||
|
|
0a807d0893 | ||
|
|
23596bbdd4 | ||
|
|
fe79954138 | ||
|
|
9902c023ca | ||
|
|
e8baee6734 | ||
|
|
a8dc029e56 | ||
|
|
74e3fee7aa | ||
|
|
05aac696b7 |
@@ -12,8 +12,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 1700
|
||||
versionName "0.17.0"
|
||||
versionCode 1616
|
||||
versionName "0.16.16"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ dependencyVerification {
|
||||
'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.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.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',
|
||||
@@ -55,16 +54,16 @@ dependencyVerification {
|
||||
}
|
||||
|
||||
ext.torBinaryDir = 'src/main/res/raw'
|
||||
ext.torVersion = '0.2.9.12'
|
||||
ext.geoipVersion = '2017-09-06'
|
||||
ext.torVersion = '0.2.9.14'
|
||||
ext.geoipVersion = '2017-11-06'
|
||||
ext.torDownloadUrl = 'https://briarproject.org/build/'
|
||||
|
||||
def torBinaries = [
|
||||
"tor_arm" : '8ed0b347ffed1d6a4d2fd14495118eb92be83e9cc06e057e15220dc288b31688',
|
||||
"tor_arm_pie": '64403262511c29f462ca5e7c7621bfc3c944898364d1d5ad35a016bb8a034283',
|
||||
"tor_x86" : '61e014607a2079bcf1646289c67bff6372b1aded6e1d8d83d7791efda9a4d5ab',
|
||||
"tor_x86_pie": '18fbc98356697dd0895836ab46d5c9877d1c539193464f7db1e82a65adaaf288',
|
||||
"geoip" : 'fe49d3adb86d3c512373101422a017dbb86c85a570524663f09dd8ce143a24f3'
|
||||
"tor_arm" : '1710ea6c47b7f4c1a88bdf4858c7893837635db10e8866854eed8d61629f50e8',
|
||||
"tor_arm_pie": '974e6949507db8fa2ea45231817c2c3677ed4ccf5488a2252317d744b0be1917',
|
||||
"tor_x86" : '3a5e45b3f051fcda9353b098b7086e762ffe7ba9242f7d7c8bf6523faaa8b1e9',
|
||||
"tor_x86_pie": 'd1d96d8ce1a4b68accf04850185780d10cd5563d3552f7e1f040f8ca32cb4e51',
|
||||
"geoip" : '8239b98374493529a29096e45fc5877d4d6fdad0146ad8380b291f90d61484ea'
|
||||
]
|
||||
|
||||
def downloadBinary(name) {
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
-dontwarn dagger.**
|
||||
-dontnote dagger.**
|
||||
|
||||
-keep class net.i2p.crypto.eddsa.** { *; }
|
||||
|
||||
-dontwarn sun.misc.Unsafe
|
||||
-dontnote com.google.common.**
|
||||
|
||||
|
||||
@@ -12,19 +12,18 @@ public interface ContactGroupFactory {
|
||||
/**
|
||||
* Creates a group that is not shared with any contacts.
|
||||
*/
|
||||
Group createLocalGroup(ClientId clientId, int clientVersion);
|
||||
Group createLocalGroup(ClientId clientId);
|
||||
|
||||
/**
|
||||
* Creates a group for the given client to share with the given contact.
|
||||
*/
|
||||
Group createContactGroup(ClientId clientId, int clientVersion,
|
||||
Contact contact);
|
||||
Group createContactGroup(ClientId clientId, Contact contact);
|
||||
|
||||
/**
|
||||
* Creates a group for the given client to share between the given authors
|
||||
* identified by their AuthorIds.
|
||||
*/
|
||||
Group createContactGroup(ClientId clientId, int clientVersion,
|
||||
AuthorId authorId1, AuthorId authorId2);
|
||||
Group createContactGroup(ClientId clientId, AuthorId authorId1,
|
||||
AuthorId authorId2);
|
||||
|
||||
}
|
||||
|
||||
@@ -12,32 +12,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
@NotNullByDefault
|
||||
public interface ContactExchangeTask {
|
||||
|
||||
/**
|
||||
* The current version of the contact exchange protocol
|
||||
*/
|
||||
int PROTOCOL_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's header key from the master secret.
|
||||
*/
|
||||
String ALICE_KEY_LABEL =
|
||||
"org.briarproject.bramble.contact/ALICE_HEADER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's header key from the master secret.
|
||||
*/
|
||||
String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's key binding nonce from the master secret.
|
||||
*/
|
||||
String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's key binding nonce from the master secret.
|
||||
*/
|
||||
String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE";
|
||||
|
||||
/**
|
||||
* Exchanges contact information with a remote peer.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.briarproject.bramble.api.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
@@ -17,98 +20,156 @@ public interface CryptoComponent {
|
||||
|
||||
KeyParser getSignatureKeyParser();
|
||||
|
||||
KeyPair generateEdKeyPair();
|
||||
|
||||
KeyParser getEdKeyParser();
|
||||
|
||||
KeyParser getMessageKeyParser();
|
||||
|
||||
/**
|
||||
* Derives another secret key from the given secret key.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of the derived
|
||||
* key, to prevent it from being repurposed or colliding with a key derived
|
||||
* for another purpose
|
||||
* Derives a stream header key from the given master secret.
|
||||
* @param alice whether the key is for use by Alice or Bob.
|
||||
*/
|
||||
SecretKey deriveKey(String label, SecretKey k, byte[]... inputs);
|
||||
SecretKey deriveHeaderKey(SecretKey master, boolean alice);
|
||||
|
||||
/**
|
||||
* Derives a message authentication code key from the given master secret.
|
||||
* @param alice whether the key is for use by Alice or Bob.
|
||||
*/
|
||||
SecretKey deriveMacKey(SecretKey master, boolean alice);
|
||||
|
||||
/**
|
||||
* Derives a nonce from the given master secret for one of the parties to
|
||||
* sign.
|
||||
* @param alice whether the nonce is for use by Alice or Bob.
|
||||
*/
|
||||
byte[] deriveSignatureNonce(SecretKey master, boolean alice);
|
||||
|
||||
/**
|
||||
* Derives a commitment to the provided public key.
|
||||
* <p/>
|
||||
* Part of BQP.
|
||||
*
|
||||
* @param publicKey the public key
|
||||
* @return the commitment to the provided public key.
|
||||
*/
|
||||
byte[] deriveKeyCommitment(byte[] publicKey);
|
||||
|
||||
/**
|
||||
* Derives a common shared secret from two public keys and one of the
|
||||
* corresponding private keys.
|
||||
* <p/>
|
||||
* Part of BQP.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of this shared
|
||||
* secret, to prevent it from being repurposed or colliding with a shared
|
||||
* secret derived for another purpose
|
||||
* @param theirPublicKey the public key of the remote party
|
||||
* @param ourKeyPair the key pair of the local party
|
||||
* @param theirPublicKey the ephemeral public key of the remote party
|
||||
* @param ourKeyPair our ephemeral keypair
|
||||
* @param alice true if ourKeyPair belongs to Alice
|
||||
* @return the shared secret
|
||||
* @throws GeneralSecurityException
|
||||
*/
|
||||
SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||
KeyPair ourKeyPair, byte[]... inputs)
|
||||
throws GeneralSecurityException;
|
||||
SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
|
||||
boolean alice) throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Signs the given byte[] with the given ECDSA private key.
|
||||
* Derives the content of a confirmation record.
|
||||
* <p/>
|
||||
* Part of BQP.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of this
|
||||
* signature, to prevent it from being repurposed or colliding with a
|
||||
* signature created for another purpose
|
||||
* @param sharedSecret the common shared secret
|
||||
* @param theirPayload the commit payload from the remote party
|
||||
* @param ourPayload the commit payload we sent
|
||||
* @param theirPublicKey the ephemeral public key of the remote party
|
||||
* @param ourKeyPair our ephemeral keypair
|
||||
* @param alice true if ourKeyPair belongs to Alice
|
||||
* @param aliceRecord true if the confirmation record is for use by Alice
|
||||
* @return the confirmation record
|
||||
*/
|
||||
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||
byte[] theirPayload, byte[] ourPayload,
|
||||
byte[] theirPublicKey, KeyPair ourKeyPair,
|
||||
boolean alice, boolean aliceRecord);
|
||||
|
||||
/**
|
||||
* Derives a master secret from the given shared secret.
|
||||
* <p/>
|
||||
* Part of BQP.
|
||||
*
|
||||
* @param sharedSecret the common shared secret
|
||||
* @return the master secret
|
||||
*/
|
||||
SecretKey deriveMasterSecret(SecretKey sharedSecret);
|
||||
|
||||
/**
|
||||
* Derives a master secret from two public keys and one of the corresponding
|
||||
* private keys.
|
||||
* <p/>
|
||||
* This is a helper method that calls
|
||||
* deriveMasterSecret(deriveSharedSecret(theirPublicKey, ourKeyPair, alice))
|
||||
*
|
||||
* @param theirPublicKey the ephemeral public key of the remote party
|
||||
* @param ourKeyPair our ephemeral keypair
|
||||
* @param alice true if ourKeyPair belongs to Alice
|
||||
* @return the shared secret
|
||||
* @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)
|
||||
throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Signs the given byte[] with the given Ed25519 private key.
|
||||
* Verifies that the given signature is valid for the signedData
|
||||
* and the given publicKey.
|
||||
*
|
||||
* @param label A label specific to this signature
|
||||
* @param label A label that was specific to this signature
|
||||
* 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.
|
||||
*/
|
||||
boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||
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
|
||||
* combined by prefixing each input with its length.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of this hash, to
|
||||
* prevent it from being repurposed or colliding with a hash created for
|
||||
* another purpose
|
||||
* @param label A label specific to this hash to ensure that hashes
|
||||
* calculated for distinct purposes don't collide.
|
||||
*/
|
||||
byte[] hash(String label, byte[]... inputs);
|
||||
|
||||
/**
|
||||
* Returns the length of hashes produced by
|
||||
* the {@link CryptoComponent#hash(String, byte[]...)} method.
|
||||
*/
|
||||
int getHashLength();
|
||||
|
||||
/**
|
||||
* Returns a message authentication code with the given key over the
|
||||
* given inputs. The inputs are unambiguously combined by prefixing each
|
||||
* input with its length.
|
||||
*
|
||||
* @param label a namespaced label indicating the purpose of this MAC, to
|
||||
* prevent it from being repurposed or colliding with a MAC created for
|
||||
* another purpose
|
||||
*/
|
||||
byte[] mac(String label, SecretKey macKey, byte[]... inputs);
|
||||
byte[] mac(SecretKey macKey, byte[]... inputs);
|
||||
|
||||
/**
|
||||
* Encrypts and authenticates the given plaintext so it can be written to
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package org.briarproject.bramble.api.crypto;
|
||||
|
||||
/**
|
||||
* Crypto operations for the key agreement protocol - see
|
||||
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BQP.md
|
||||
*/
|
||||
public interface KeyAgreementCrypto {
|
||||
|
||||
/**
|
||||
* Hash label for public key commitment.
|
||||
*/
|
||||
String COMMIT_LABEL = "org.briarproject.bramble.keyagreement/COMMIT";
|
||||
|
||||
/**
|
||||
* Key derivation label for confirmation record.
|
||||
*/
|
||||
String CONFIRMATION_KEY_LABEL =
|
||||
"org.briarproject.bramble.keyagreement/CONFIRMATION_KEY";
|
||||
|
||||
/**
|
||||
* MAC label for confirmation record.
|
||||
*/
|
||||
String CONFIRMATION_MAC_LABEL =
|
||||
"org.briarproject.bramble.keyagreement/CONFIRMATION_MAC";
|
||||
|
||||
/**
|
||||
* Derives a commitment to the provided public key.
|
||||
*
|
||||
* @param publicKey the public key
|
||||
* @return the commitment to the provided public key.
|
||||
*/
|
||||
byte[] deriveKeyCommitment(PublicKey publicKey);
|
||||
|
||||
/**
|
||||
* Derives the content of a confirmation record.
|
||||
*
|
||||
* @param sharedSecret the common shared secret
|
||||
* @param theirPayload the key exchange payload of the remote party
|
||||
* @param ourPayload the key exchange payload of the local party
|
||||
* @param theirPublicKey the ephemeral public key of the remote party
|
||||
* @param ourKeyPair our ephemeral key pair of the local party
|
||||
* @param alice true if the local party is Alice
|
||||
* @param aliceRecord true if the confirmation record is for use by Alice
|
||||
* @return the confirmation record
|
||||
*/
|
||||
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||
byte[] theirPayload, byte[] ourPayload,
|
||||
PublicKey theirPublicKey, KeyPair ourKeyPair,
|
||||
boolean alice, boolean aliceRecord);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.briarproject.bramble.api.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
|
||||
/**
|
||||
* Crypto operations for the transport security protocol - see
|
||||
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BTP.md
|
||||
*/
|
||||
public interface TransportCrypto {
|
||||
|
||||
/**
|
||||
* Derives initial transport keys for the given transport in the given
|
||||
* rotation period from the given master secret.
|
||||
*
|
||||
* @param alice whether the keys are for use by Alice or Bob.
|
||||
*/
|
||||
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
|
||||
long rotationPeriod, boolean alice);
|
||||
|
||||
/**
|
||||
* Rotates the given transport keys to the given rotation period. If the
|
||||
* keys are for the given period or any later period they are not rotated.
|
||||
*/
|
||||
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
|
||||
|
||||
/**
|
||||
* Encodes the pseudo-random tag that is used to recognise a stream.
|
||||
*/
|
||||
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||
long streamNumber);
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public class AuthorId extends UniqueId {
|
||||
/**
|
||||
* Label for hashing authors to calculate their identities.
|
||||
*/
|
||||
public static final String LABEL = "org.briarproject.bramble/AUTHOR_ID";
|
||||
public static final String LABEL = "org.briarproject.bramble.AUTHOR_ID";
|
||||
|
||||
public AuthorId(byte[] id) {
|
||||
super(id);
|
||||
|
||||
@@ -5,7 +5,7 @@ public interface KeyAgreementConstants {
|
||||
/**
|
||||
* The current version of the BQP protocol.
|
||||
*/
|
||||
byte PROTOCOL_VERSION = 3;
|
||||
byte PROTOCOL_VERSION = 2;
|
||||
|
||||
/**
|
||||
* The length of the record header in bytes.
|
||||
@@ -22,10 +22,7 @@ public interface KeyAgreementConstants {
|
||||
*/
|
||||
int COMMIT_LENGTH = 16;
|
||||
|
||||
/**
|
||||
* The connection timeout in milliseconds.
|
||||
*/
|
||||
long CONNECTION_TIMEOUT = 20 * 1000;
|
||||
long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds
|
||||
|
||||
/**
|
||||
* The transport identifier for Bluetooth.
|
||||
@@ -36,16 +33,4 @@ public interface KeyAgreementConstants {
|
||||
* The transport identifier for LAN.
|
||||
*/
|
||||
int TRANSPORT_ID_LAN = 1;
|
||||
|
||||
/**
|
||||
* Label for deriving the shared secret.
|
||||
*/
|
||||
String SHARED_SECRET_LABEL =
|
||||
"org.briarproject.bramble.keyagreement/SHARED_SECRET";
|
||||
|
||||
/**
|
||||
* Label for deriving the master secret.
|
||||
*/
|
||||
String MASTER_SECRET_LABEL =
|
||||
"org.briarproject.bramble.keyagreement/MASTER_SECRET";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.briarproject.bramble.api.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
/**
|
||||
* Manages tasks for conducting key agreements with remote peers.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
public interface KeyAgreementTaskFactory {
|
||||
|
||||
/**
|
||||
* Gets the current key agreement task.
|
||||
*/
|
||||
KeyAgreementTask createTask();
|
||||
}
|
||||
@@ -17,11 +17,6 @@ public interface TransportPropertyManager {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties");
|
||||
|
||||
/**
|
||||
* The current version of the transport property client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Stores the given properties received while adding a contact - they will
|
||||
* be superseded by any properties synced from the contact.
|
||||
|
||||
@@ -36,8 +36,4 @@ public class ClientId implements Comparable<ClientId> {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
public interface GroupFactory {
|
||||
|
||||
/**
|
||||
* Creates a group with the given client ID, client version and descriptor.
|
||||
* Creates a group with the given client ID and descriptor.
|
||||
*/
|
||||
Group createGroup(ClientId c, int clientVersion, byte[] descriptor);
|
||||
Group createGroup(ClientId c, byte[] descriptor);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class GroupId extends UniqueId {
|
||||
/**
|
||||
* Label for hashing groups to calculate their identifiers.
|
||||
*/
|
||||
public static final String LABEL = "org.briarproject.bramble/GROUP_ID";
|
||||
public static final String LABEL = "org.briarproject.bramble.GROUP_ID";
|
||||
|
||||
public GroupId(byte[] id) {
|
||||
super(id);
|
||||
|
||||
@@ -16,7 +16,7 @@ public class MessageId extends UniqueId {
|
||||
/**
|
||||
* Label for hashing messages to calculate their identifiers.
|
||||
*/
|
||||
public static final String LABEL = "org.briarproject.bramble/MESSAGE_ID";
|
||||
public static final String LABEL = "org.briarproject.bramble.MESSAGE_ID";
|
||||
|
||||
public MessageId(byte[] id) {
|
||||
super(id);
|
||||
|
||||
@@ -7,7 +7,7 @@ public interface TransportConstants {
|
||||
/**
|
||||
* The current version of the transport protocol.
|
||||
*/
|
||||
int PROTOCOL_VERSION = 4;
|
||||
int PROTOCOL_VERSION = 3;
|
||||
|
||||
/**
|
||||
* The length of the pseudo-random tag in bytes.
|
||||
@@ -80,32 +80,4 @@ public interface TransportConstants {
|
||||
* The size of the reordering window.
|
||||
*/
|
||||
int REORDERING_WINDOW_SIZE = 32;
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's initial tag key from the master secret.
|
||||
*/
|
||||
String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's initial tag key from the master secret.
|
||||
*/
|
||||
String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's initial header key from the master secret.
|
||||
*/
|
||||
String ALICE_HEADER_LABEL =
|
||||
"org.briarproject.bramble.transport/ALICE_HEADER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's initial header key from the master secret.
|
||||
*/
|
||||
String BOB_HEADER_LABEL =
|
||||
"org.briarproject.bramble.transport/BOB_HEADER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving the next period's key in key rotation.
|
||||
*/
|
||||
String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE";
|
||||
|
||||
}
|
||||
|
||||
@@ -9,21 +9,18 @@ apply plugin: 'witness'
|
||||
dependencies {
|
||||
implementation project(path: ':bramble-api', configuration: 'default')
|
||||
implementation 'com.madgag.spongycastle:core:1.58.0.0'
|
||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||
implementation 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
|
||||
implementation 'org.bitlet:weupnp:0.1.4'
|
||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||
|
||||
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||
testImplementation "org.whispersystems:curve25519-java:0.4.1"
|
||||
|
||||
testApt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
}
|
||||
@@ -40,21 +37,18 @@ dependencyVerification {
|
||||
'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',
|
||||
'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:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||
'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.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
|
||||
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
|
||||
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||
'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -32,25 +32,23 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createLocalGroup(ClientId clientId, int clientVersion) {
|
||||
return groupFactory.createGroup(clientId, clientVersion,
|
||||
LOCAL_GROUP_DESCRIPTOR);
|
||||
public Group createLocalGroup(ClientId clientId) {
|
||||
return groupFactory.createGroup(clientId, LOCAL_GROUP_DESCRIPTOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createContactGroup(ClientId clientId, int clientVersion,
|
||||
Contact contact) {
|
||||
public Group createContactGroup(ClientId clientId, Contact contact) {
|
||||
AuthorId local = contact.getLocalAuthorId();
|
||||
AuthorId remote = contact.getAuthor().getId();
|
||||
byte[] descriptor = createGroupDescriptor(local, remote);
|
||||
return groupFactory.createGroup(clientId, clientVersion, descriptor);
|
||||
return groupFactory.createGroup(clientId, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createContactGroup(ClientId clientId, int clientVersion,
|
||||
AuthorId authorId1, AuthorId authorId2) {
|
||||
public Group createContactGroup(ClientId clientId, AuthorId authorId1,
|
||||
AuthorId authorId2) {
|
||||
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
|
||||
return groupFactory.createGroup(clientId, clientVersion, descriptor);
|
||||
return groupFactory.createGroup(clientId, descriptor);
|
||||
}
|
||||
|
||||
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
|
||||
|
||||
@@ -141,10 +141,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
}
|
||||
|
||||
// Derive the header keys for the transport streams
|
||||
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL,
|
||||
masterSecret, new byte[] {PROTOCOL_VERSION});
|
||||
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret,
|
||||
new byte[] {PROTOCOL_VERSION});
|
||||
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
|
||||
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
|
||||
|
||||
// Create the readers
|
||||
InputStream streamReader =
|
||||
@@ -158,10 +156,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
||||
|
||||
// Derive the nonces to be signed
|
||||
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
|
||||
new byte[] {PROTOCOL_VERSION});
|
||||
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
|
||||
new byte[] {PROTOCOL_VERSION});
|
||||
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
|
||||
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
|
||||
|
||||
// Exchange pseudonyms, signed nonces, and timestamps
|
||||
long localTimestamp = clock.currentTimeMillis();
|
||||
@@ -200,8 +196,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
|
||||
try {
|
||||
// Add the contact
|
||||
ContactId contactId = addContact(remoteAuthor, timestamp,
|
||||
remoteProperties);
|
||||
ContactId contactId = addContact(remoteAuthor, masterSecret,
|
||||
timestamp, alice, remoteProperties);
|
||||
// Reuse the connection as a transport connection
|
||||
connectionManager.manageOutgoingConnection(contactId, transportId,
|
||||
conn);
|
||||
@@ -298,15 +294,15 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
return remote;
|
||||
}
|
||||
|
||||
private ContactId addContact(Author remoteAuthor, long timestamp,
|
||||
private ContactId addContact(Author remoteAuthor, SecretKey master,
|
||||
long timestamp, boolean alice,
|
||||
Map<TransportId, TransportProperties> remoteProperties)
|
||||
throws DbException {
|
||||
ContactId contactId;
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
contactId = contactManager.addContact(txn, remoteAuthor,
|
||||
localAuthor.getId(), masterSecret, timestamp, alice,
|
||||
true, true);
|
||||
localAuthor.getId(), master, timestamp, alice, true, true);
|
||||
transportPropertyManager.addRemoteProperties(txn, contactId,
|
||||
remoteProperties);
|
||||
db.commitTransaction(txn);
|
||||
@@ -316,7 +312,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
private void tryToClose(DuplexTransportConnection conn, boolean exception) {
|
||||
private void tryToClose(DuplexTransportConnection conn,
|
||||
boolean exception) {
|
||||
try {
|
||||
LOG.info("Closing connection");
|
||||
conn.getReader().dispose(exception, true);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
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.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||
@@ -26,6 +26,7 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
@@ -39,8 +40,14 @@ import java.util.logging.Logger;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
class CryptoComponentImpl implements CryptoComponent {
|
||||
|
||||
@@ -49,19 +56,49 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
|
||||
private static final int AGREEMENT_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 PBKDF_SALT_BYTES = 32; // 256 bits
|
||||
private static final int PBKDF_TARGET_MILLIS = 500;
|
||||
private static final int PBKDF_SAMPLES = 30;
|
||||
private static final int HASH_SIZE = 256 / 8;
|
||||
|
||||
private static byte[] ascii(String s) {
|
||||
return s.getBytes(Charset.forName("US-ASCII"));
|
||||
}
|
||||
|
||||
// KDF labels for contact exchange stream header key derivation
|
||||
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
|
||||
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
|
||||
// KDF labels for contact exchange signature nonce derivation
|
||||
private static final byte[] A_SIG_NONCE = ascii("ALICE_SIGNATURE_NONCE");
|
||||
private static final byte[] B_SIG_NONCE = ascii("BOB_SIGNATURE_NONCE");
|
||||
// Hash label for BQP public key commitment derivation
|
||||
private static final String COMMIT =
|
||||
"org.briarproject.bramble.COMMIT";
|
||||
// Hash label for shared secret derivation
|
||||
private static final String SHARED_SECRET =
|
||||
"org.briarproject.bramble.SHARED_SECRET";
|
||||
// KDF label for BQP confirmation key derivation
|
||||
private static final byte[] CONFIRMATION_KEY = ascii("CONFIRMATION_KEY");
|
||||
// KDF label for master key derivation
|
||||
private static final byte[] MASTER_KEY = ascii("MASTER_KEY");
|
||||
// KDF labels for tag key derivation
|
||||
private static final byte[] A_TAG = ascii("ALICE_TAG_KEY");
|
||||
private static final byte[] B_TAG = ascii("BOB_TAG_KEY");
|
||||
// KDF labels for header key derivation
|
||||
private static final byte[] A_HEADER = ascii("ALICE_HEADER_KEY");
|
||||
private static final byte[] B_HEADER = ascii("BOB_HEADER_KEY");
|
||||
// KDF labels for MAC key derivation
|
||||
private static final byte[] A_MAC = ascii("ALICE_MAC_KEY");
|
||||
private static final byte[] B_MAC = ascii("BOB_MAC_KEY");
|
||||
// KDF label for key rotation
|
||||
private static final byte[] ROTATE = ascii("ROTATE");
|
||||
|
||||
private final SecureRandom secureRandom;
|
||||
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
||||
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
||||
private final KeyParser agreementKeyParser, signatureKeyParser;
|
||||
private final MessageEncrypter messageEncrypter;
|
||||
private final KeyPairGenerator edKeyPairGenerator;
|
||||
private final KeyParser edKeyParser;
|
||||
|
||||
@Inject
|
||||
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
||||
@@ -95,9 +132,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
signatureKeyParser = new Sec1KeyParser(PARAMETERS,
|
||||
SIGNATURE_KEY_PAIR_BITS);
|
||||
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
|
||||
@@ -156,21 +190,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
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
|
||||
public KeyPair generateAgreementKeyPair() {
|
||||
AsymmetricCipherKeyPair keyPair =
|
||||
@@ -219,63 +238,197 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String label, SecretKey k, byte[]... inputs) {
|
||||
byte[] mac = mac(label, k, inputs);
|
||||
if (mac.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||
return new SecretKey(mac);
|
||||
public SecretKey deriveHeaderKey(SecretKey master,
|
||||
boolean alice) {
|
||||
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||
KeyPair ourKeyPair, byte[]... inputs)
|
||||
throws GeneralSecurityException {
|
||||
public SecretKey deriveMacKey(SecretKey master, boolean alice) {
|
||||
return new SecretKey(macKdf(master, alice ? A_MAC : B_MAC));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveSignatureNonce(SecretKey master,
|
||||
boolean alice) {
|
||||
return macKdf(master, alice ? A_SIG_NONCE : B_SIG_NONCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveKeyCommitment(byte[] publicKey) {
|
||||
byte[] hash = hash(COMMIT, publicKey);
|
||||
// The output is the first COMMIT_LENGTH bytes of the hash
|
||||
byte[] commitment = new byte[COMMIT_LENGTH];
|
||||
System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH);
|
||||
return commitment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveSharedSecret(byte[] theirPublicKey,
|
||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
||||
PrivateKey ourPriv = ourKeyPair.getPrivate();
|
||||
byte[][] hashInputs = new byte[inputs.length + 1][];
|
||||
hashInputs[0] = performRawKeyAgreement(ourPriv, theirPublicKey);
|
||||
System.arraycopy(inputs, 0, hashInputs, 1, inputs.length);
|
||||
byte[] hash = hash(label, hashInputs);
|
||||
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||
return new SecretKey(hash);
|
||||
PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
|
||||
byte[] raw = performRawKeyAgreement(ourPriv, theirPub);
|
||||
byte[] alicePub, bobPub;
|
||||
if (alice) {
|
||||
alicePub = ourKeyPair.getPublic().getEncoded();
|
||||
bobPub = theirPublicKey;
|
||||
} else {
|
||||
alicePub = theirPublicKey;
|
||||
bobPub = ourKeyPair.getPublic().getEncoded();
|
||||
}
|
||||
return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||
byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey,
|
||||
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
|
||||
SecretKey ck = new SecretKey(macKdf(sharedSecret, CONFIRMATION_KEY));
|
||||
byte[] alicePayload, alicePub, bobPayload, bobPub;
|
||||
if (alice) {
|
||||
alicePayload = ourPayload;
|
||||
alicePub = ourKeyPair.getPublic().getEncoded();
|
||||
bobPayload = theirPayload;
|
||||
bobPub = theirPublicKey;
|
||||
} else {
|
||||
alicePayload = theirPayload;
|
||||
alicePub = theirPublicKey;
|
||||
bobPayload = ourPayload;
|
||||
bobPub = ourKeyPair.getPublic().getEncoded();
|
||||
}
|
||||
if (aliceRecord)
|
||||
return macKdf(ck, alicePayload, alicePub, bobPayload, bobPub);
|
||||
else
|
||||
return macKdf(ck, bobPayload, bobPub, alicePayload, alicePub);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveMasterSecret(SecretKey sharedSecret) {
|
||||
return new SecretKey(macKdf(sharedSecret, MASTER_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
||||
return deriveMasterSecret(deriveSharedSecret(
|
||||
theirPublicKey, ourKeyPair, alice));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeys deriveTransportKeys(TransportId t,
|
||||
SecretKey master, long rotationPeriod, boolean alice) {
|
||||
// Keys for the previous period are derived from the master secret
|
||||
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
|
||||
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
|
||||
SecretKey outTagPrev = deriveTagKey(master, t, alice);
|
||||
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
|
||||
// Derive the keys for the current and next periods
|
||||
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
|
||||
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
|
||||
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
|
||||
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
|
||||
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
|
||||
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
|
||||
// Initialise the reordering windows and stream counters
|
||||
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
|
||||
rotationPeriod - 1);
|
||||
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
|
||||
rotationPeriod);
|
||||
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
|
||||
rotationPeriod + 1);
|
||||
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
|
||||
rotationPeriod);
|
||||
// Collect and return the keys
|
||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeys rotateTransportKeys(TransportKeys k,
|
||||
long rotationPeriod) {
|
||||
if (k.getRotationPeriod() >= rotationPeriod) return k;
|
||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||
long startPeriod = outCurr.getRotationPeriod();
|
||||
// Rotate the keys
|
||||
for (long p = startPeriod + 1; p <= rotationPeriod; p++) {
|
||||
inPrev = inCurr;
|
||||
inCurr = inNext;
|
||||
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
|
||||
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
|
||||
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
|
||||
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
|
||||
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
|
||||
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p);
|
||||
}
|
||||
// Collect and return the keys
|
||||
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
||||
outCurr);
|
||||
}
|
||||
|
||||
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
|
||||
byte[] period = new byte[INT_64_BYTES];
|
||||
ByteUtils.writeUint64(rotationPeriod, period, 0);
|
||||
return new SecretKey(macKdf(k, ROTATE, period));
|
||||
}
|
||||
|
||||
private SecretKey deriveTagKey(SecretKey master, TransportId t,
|
||||
boolean alice) {
|
||||
byte[] id = StringUtils.toUtf8(t.getString());
|
||||
return new SecretKey(macKdf(master, alice ? A_TAG : B_TAG, id));
|
||||
}
|
||||
|
||||
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
|
||||
boolean alice) {
|
||||
byte[] id = StringUtils.toUtf8(t.getString());
|
||||
return new SecretKey(macKdf(master, alice ? A_HEADER : B_HEADER, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||
long streamNumber) {
|
||||
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
// Initialise the PRF
|
||||
Digest prf = new Blake2sDigest(tagKey.getBytes());
|
||||
// The output of the PRF must be long enough to use as a tag
|
||||
int macLength = prf.getDigestSize();
|
||||
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
||||
// The input is the protocol version as a 16-bit integer, followed by
|
||||
// the stream number as a 64-bit integer
|
||||
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
|
||||
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
|
||||
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
|
||||
byte[] streamNumberBytes = new byte[INT_64_BYTES];
|
||||
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
|
||||
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
|
||||
byte[] mac = new byte[macLength];
|
||||
prf.doFinal(mac, 0);
|
||||
// The output is the first TAG_LENGTH bytes of the MAC
|
||||
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||
throws GeneralSecurityException {
|
||||
return sign(new SignatureImpl(secureRandom), signatureKeyParser, label,
|
||||
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 {
|
||||
Signature signature = new SignatureImpl(secureRandom);
|
||||
KeyParser keyParser = getSignatureKeyParser();
|
||||
PrivateKey key = keyParser.parsePrivateKey(privateKey);
|
||||
sig.initSign(key);
|
||||
updateSignature(sig, label, toSign);
|
||||
return sig.sign();
|
||||
signature.initSign(key);
|
||||
updateSignature(signature, label, toSign);
|
||||
return signature.sign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||
byte[] signature) throws GeneralSecurityException {
|
||||
return verify(new SignatureImpl(secureRandom), signatureKeyParser,
|
||||
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 {
|
||||
Signature sig = new SignatureImpl(secureRandom);
|
||||
KeyParser keyParser = getSignatureKeyParser();
|
||||
PublicKey key = keyParser.parsePublicKey(publicKey);
|
||||
sig.initVerify(key);
|
||||
updateSignature(sig, label, signedData);
|
||||
@@ -283,7 +436,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
private void updateSignature(Signature signature, String label,
|
||||
byte[] toSign) throws GeneralSecurityException {
|
||||
byte[] toSign) {
|
||||
byte[] labelBytes = StringUtils.toUtf8(label);
|
||||
byte[] length = new byte[INT_32_BYTES];
|
||||
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
||||
@@ -313,13 +466,14 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] mac(String label, SecretKey macKey, byte[]... inputs) {
|
||||
byte[] labelBytes = StringUtils.toUtf8(label);
|
||||
public int getHashLength() {
|
||||
return HASH_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] mac(SecretKey macKey, byte[]... inputs) {
|
||||
Digest mac = new Blake2sDigest(macKey.getBytes());
|
||||
byte[] length = new byte[INT_32_BYTES];
|
||||
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
||||
mac.update(length, 0, length.length);
|
||||
mac.update(labelBytes, 0, labelBytes.length);
|
||||
for (byte[] input : inputs) {
|
||||
ByteUtils.writeUint32(input.length, length, 0);
|
||||
mac.update(length, 0, length.length);
|
||||
@@ -411,6 +565,30 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
return AsciiArmour.wrap(b, lineLength);
|
||||
}
|
||||
|
||||
// Key derivation function based on a pseudo-random function - see
|
||||
// NIST SP 800-108, section 5.1
|
||||
private byte[] macKdf(SecretKey key, byte[]... inputs) {
|
||||
// Initialise the PRF
|
||||
Digest prf = new Blake2sDigest(key.getBytes());
|
||||
// The output of the PRF must be long enough to use as a key
|
||||
int macLength = prf.getDigestSize();
|
||||
if (macLength < SecretKey.LENGTH) throw new IllegalStateException();
|
||||
// Calculate the PRF over the concatenated length-prefixed inputs
|
||||
byte[] length = new byte[INT_32_BYTES];
|
||||
for (byte[] input : inputs) {
|
||||
ByteUtils.writeUint32(input.length, length, 0);
|
||||
prf.update(length, 0, length.length);
|
||||
prf.update(input, 0, input.length);
|
||||
}
|
||||
byte[] mac = new byte[macLength];
|
||||
prf.doFinal(mac, 0);
|
||||
// The output is the first SecretKey.LENGTH bytes of the MAC
|
||||
if (mac.length == SecretKey.LENGTH) return mac;
|
||||
byte[] truncated = new byte[SecretKey.LENGTH];
|
||||
System.arraycopy(mac, 0, truncated, 0, truncated.length);
|
||||
return truncated;
|
||||
}
|
||||
|
||||
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
|
||||
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
||||
byte[] utf8 = StringUtils.toUtf8(password);
|
||||
|
||||
@@ -3,11 +3,9 @@ package org.briarproject.bramble.crypto;
|
||||
import org.briarproject.bramble.TimeLoggingExecutor;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
|
||||
@@ -76,12 +74,6 @@ public class CryptoModule {
|
||||
return new PasswordStrengthEstimatorImpl();
|
||||
}
|
||||
|
||||
@Provides
|
||||
TransportCrypto provideTransportCrypto(
|
||||
TransportCryptoImpl transportCrypto) {
|
||||
return transportCrypto;
|
||||
}
|
||||
|
||||
@Provides
|
||||
StreamDecrypterFactory provideStreamDecrypterFactory(
|
||||
Provider<AuthenticatedCipher> cipherProvider) {
|
||||
@@ -89,17 +81,9 @@ public class CryptoModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
StreamEncrypterFactory provideStreamEncrypterFactory(
|
||||
CryptoComponent crypto, TransportCrypto transportCrypto,
|
||||
StreamEncrypterFactory provideStreamEncrypterFactory(CryptoComponent crypto,
|
||||
Provider<AuthenticatedCipher> cipherProvider) {
|
||||
return new StreamEncrypterFactoryImpl(crypto, transportCrypto,
|
||||
cipherProvider);
|
||||
}
|
||||
|
||||
@Provides
|
||||
KeyAgreementCrypto provideKeyAgreementCrypto(
|
||||
KeyAgreementCryptoImpl keyAgreementCrypto) {
|
||||
return keyAgreementCrypto;
|
||||
return new StreamEncrypterFactoryImpl(crypto, cipherProvider);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
class EdPrivateKey extends Bytes implements PrivateKey {
|
||||
|
||||
EdPrivateKey(byte[] bytes) {
|
||||
super(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
return getBytes();
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
class EdPublicKey extends Bytes implements PublicKey {
|
||||
|
||||
EdPublicKey(byte[] bytes) {
|
||||
super(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
return getBytes();
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
|
||||
class KeyAgreementCryptoImpl implements KeyAgreementCrypto {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
@Inject
|
||||
KeyAgreementCryptoImpl(CryptoComponent crypto) {
|
||||
this.crypto = crypto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveKeyCommitment(PublicKey publicKey) {
|
||||
byte[] hash = crypto.hash(COMMIT_LABEL, publicKey.getEncoded());
|
||||
// The output is the first COMMIT_LENGTH bytes of the hash
|
||||
byte[] commitment = new byte[COMMIT_LENGTH];
|
||||
System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH);
|
||||
return commitment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||
byte[] theirPayload, byte[] ourPayload, PublicKey theirPublicKey,
|
||||
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
|
||||
SecretKey ck = crypto.deriveKey(CONFIRMATION_KEY_LABEL, sharedSecret);
|
||||
byte[] alicePayload, alicePub, bobPayload, bobPub;
|
||||
if (alice) {
|
||||
alicePayload = ourPayload;
|
||||
alicePub = ourKeyPair.getPublic().getEncoded();
|
||||
bobPayload = theirPayload;
|
||||
bobPub = theirPublicKey.getEncoded();
|
||||
} else {
|
||||
alicePayload = theirPayload;
|
||||
alicePub = theirPublicKey.getEncoded();
|
||||
bobPayload = ourPayload;
|
||||
bobPub = ourKeyPair.getPublic().getEncoded();
|
||||
}
|
||||
if (aliceRecord) {
|
||||
return crypto.mac(CONFIRMATION_MAC_LABEL, ck, alicePayload,
|
||||
alicePub, bobPayload, bobPub);
|
||||
} else {
|
||||
return crypto.mac(CONFIRMATION_MAC_LABEL, ck, bobPayload, bobPub,
|
||||
alicePayload, alicePub);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,25 +22,25 @@ interface Signature {
|
||||
/**
|
||||
* @see {@link java.security.Signature#update(byte)}
|
||||
*/
|
||||
void update(byte b) throws GeneralSecurityException;
|
||||
void update(byte b);
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#update(byte[])}
|
||||
*/
|
||||
void update(byte[] b) throws GeneralSecurityException;
|
||||
void update(byte[] b);
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#update(byte[], int, int)}
|
||||
*/
|
||||
void update(byte[] b, int off, int len) throws GeneralSecurityException;
|
||||
void update(byte[] b, int off, int len);
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#sign()}
|
||||
*/
|
||||
byte[] sign() throws GeneralSecurityException;
|
||||
byte[] sign();
|
||||
|
||||
/**
|
||||
* @see {@link java.security.Signature#verify(byte[])}
|
||||
*/
|
||||
boolean verify(byte[] signature) throws GeneralSecurityException;
|
||||
boolean verify(byte[] signature);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.transport.StreamContext;
|
||||
|
||||
@@ -23,15 +22,12 @@ import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENG
|
||||
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final TransportCrypto transportCrypto;
|
||||
private final Provider<AuthenticatedCipher> cipherProvider;
|
||||
|
||||
@Inject
|
||||
StreamEncrypterFactoryImpl(CryptoComponent crypto,
|
||||
TransportCrypto transportCrypto,
|
||||
Provider<AuthenticatedCipher> cipherProvider) {
|
||||
this.crypto = crypto;
|
||||
this.transportCrypto = transportCrypto;
|
||||
this.cipherProvider = cipherProvider;
|
||||
}
|
||||
|
||||
@@ -41,8 +37,7 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||
AuthenticatedCipher cipher = cipherProvider.get();
|
||||
long streamNumber = ctx.getStreamNumber();
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
|
||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
||||
SecretKey frameKey = crypto.generateSecretKey();
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.spongycastle.crypto.Digest;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_TAG_LABEL;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_LABEL;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_TAG_LABEL;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
class TransportCryptoImpl implements TransportCrypto {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
@Inject
|
||||
TransportCryptoImpl(CryptoComponent crypto) {
|
||||
this.crypto = crypto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeys deriveTransportKeys(TransportId t,
|
||||
SecretKey master, long rotationPeriod, boolean alice) {
|
||||
// Keys for the previous period are derived from the master secret
|
||||
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
|
||||
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
|
||||
SecretKey outTagPrev = deriveTagKey(master, t, alice);
|
||||
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
|
||||
// Derive the keys for the current and next periods
|
||||
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
|
||||
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
|
||||
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
|
||||
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
|
||||
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
|
||||
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
|
||||
// Initialise the reordering windows and stream counters
|
||||
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
|
||||
rotationPeriod - 1);
|
||||
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
|
||||
rotationPeriod);
|
||||
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
|
||||
rotationPeriod + 1);
|
||||
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
|
||||
rotationPeriod);
|
||||
// Collect and return the keys
|
||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeys rotateTransportKeys(TransportKeys k,
|
||||
long rotationPeriod) {
|
||||
if (k.getRotationPeriod() >= rotationPeriod) return k;
|
||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||
long startPeriod = outCurr.getRotationPeriod();
|
||||
// Rotate the keys
|
||||
for (long p = startPeriod + 1; p <= rotationPeriod; p++) {
|
||||
inPrev = inCurr;
|
||||
inCurr = inNext;
|
||||
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
|
||||
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
|
||||
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
|
||||
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
|
||||
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
|
||||
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p);
|
||||
}
|
||||
// Collect and return the keys
|
||||
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
||||
outCurr);
|
||||
}
|
||||
|
||||
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
|
||||
byte[] period = new byte[INT_64_BYTES];
|
||||
ByteUtils.writeUint64(rotationPeriod, period, 0);
|
||||
return crypto.deriveKey(ROTATE_LABEL, k, period);
|
||||
}
|
||||
|
||||
private SecretKey deriveTagKey(SecretKey master, TransportId t,
|
||||
boolean alice) {
|
||||
String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
|
||||
byte[] id = StringUtils.toUtf8(t.getString());
|
||||
return crypto.deriveKey(label, master, id);
|
||||
}
|
||||
|
||||
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
|
||||
boolean alice) {
|
||||
String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL;
|
||||
byte[] id = StringUtils.toUtf8(t.getString());
|
||||
return crypto.deriveKey(label, master, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||
long streamNumber) {
|
||||
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
// Initialise the PRF
|
||||
Digest prf = new Blake2sDigest(tagKey.getBytes());
|
||||
// The output of the PRF must be long enough to use as a tag
|
||||
int macLength = prf.getDigestSize();
|
||||
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
||||
// The input is the protocol version as a 16-bit integer, followed by
|
||||
// the stream number as a 64-bit integer
|
||||
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
|
||||
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
|
||||
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
|
||||
byte[] streamNumberBytes = new byte[INT_64_BYTES];
|
||||
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
|
||||
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
|
||||
byte[] mac = new byte[macLength];
|
||||
prf.doFinal(mac, 0);
|
||||
// The output is the first TAG_LENGTH bytes of the MAC
|
||||
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
||||
}
|
||||
}
|
||||
@@ -22,23 +22,21 @@ import javax.inject.Inject;
|
||||
class H2Database extends JdbcDatabase {
|
||||
|
||||
private static final String HASH_TYPE = "BINARY(32)";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
private static final String BINARY_TYPE = "BINARY";
|
||||
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
|
||||
@Inject
|
||||
H2Database(DatabaseConfig config, Clock clock) {
|
||||
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
clock);
|
||||
super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE, clock);
|
||||
this.config = config;
|
||||
File dir = config.getDatabaseDirectory();
|
||||
String path = new File(dir, "db").getAbsolutePath();
|
||||
url = "jdbc:h2:split:" + path + ";CIPHER=AES;MULTI_THREADED=1"
|
||||
+ ";WRITE_DELAY=0";
|
||||
+ ";WRITE_DELAY=0;DB_CLOSE_ON_EXIT=false";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Contains all the HSQLDB-specific code for the database.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
class HyperSqlDatabase extends JdbcDatabase {
|
||||
|
||||
private static final String HASH_TYPE = "BINARY(32)";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
private static final String BINARY_TYPE = "BINARY";
|
||||
private static final String COUNTER_TYPE =
|
||||
"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1)";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
|
||||
@Inject
|
||||
HyperSqlDatabase(DatabaseConfig config, Clock clock) {
|
||||
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
clock);
|
||||
this.config = config;
|
||||
File dir = config.getDatabaseDirectory();
|
||||
String path = new File(dir, "db").getAbsolutePath();
|
||||
url = "jdbc:hsqldb:file:" + path
|
||||
+ ";sql.enforce_size=false;allow_empty_batch=true"
|
||||
+ ";encrypt_lobs=true;crypt_type=AES";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() throws DbException {
|
||||
boolean reopen = config.databaseExists();
|
||||
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws DbException {
|
||||
try {
|
||||
super.closeAllConnections();
|
||||
Connection c = createConnection();
|
||||
Statement s = c.createStatement();
|
||||
s.executeQuery("SHUTDOWN");
|
||||
s.close();
|
||||
c.close();
|
||||
} catch (SQLException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFreeSpace() throws DbException {
|
||||
File dir = config.getDatabaseDirectory();
|
||||
long maxSize = config.getMaxSize();
|
||||
long free = dir.getFreeSpace();
|
||||
long used = getDiskSpace(dir);
|
||||
long quota = maxSize - used;
|
||||
return Math.min(free, quota);
|
||||
}
|
||||
|
||||
private long getDiskSpace(File f) {
|
||||
if (f.isDirectory()) {
|
||||
long total = 0;
|
||||
File[] children = f.listFiles();
|
||||
if (children != null)
|
||||
for (File child : children) total += getDiskSpace(child);
|
||||
return total;
|
||||
} else if (f.isFile()) {
|
||||
return f.length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection createConnection() throws SQLException {
|
||||
SecretKey key = config.getEncryptionKey();
|
||||
if (key == null) throw new IllegalStateException();
|
||||
String hex = StringUtils.toHexString(key.getBytes());
|
||||
return DriverManager.getConnection(url + ";crypt_key=" + hex);
|
||||
}
|
||||
}
|
||||
@@ -68,32 +68,32 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
||||
@NotNullByDefault
|
||||
abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final int SCHEMA_VERSION = 31;
|
||||
private static final int MIN_SCHEMA_VERSION = 31;
|
||||
private static final int SCHEMA_VERSION = 30;
|
||||
private static final int MIN_SCHEMA_VERSION = 30;
|
||||
|
||||
private static final String CREATE_SETTINGS =
|
||||
"CREATE TABLE settings"
|
||||
+ " (namespace _STRING NOT NULL,"
|
||||
+ " settingKey _STRING NOT NULL,"
|
||||
+ " value _STRING NOT NULL,"
|
||||
+ " PRIMARY KEY (namespace, settingKey))";
|
||||
+ " (namespace VARCHAR NOT NULL,"
|
||||
+ " key VARCHAR NOT NULL,"
|
||||
+ " value VARCHAR NOT NULL,"
|
||||
+ " PRIMARY KEY (namespace, key))";
|
||||
|
||||
private static final String CREATE_LOCAL_AUTHORS =
|
||||
"CREATE TABLE localAuthors"
|
||||
+ " (authorId _HASH NOT NULL,"
|
||||
+ " name _STRING NOT NULL,"
|
||||
+ " publicKey _BINARY NOT NULL,"
|
||||
+ " privateKey _BINARY NOT NULL,"
|
||||
+ " (authorId HASH NOT NULL,"
|
||||
+ " name VARCHAR NOT NULL,"
|
||||
+ " publicKey BINARY NOT NULL,"
|
||||
+ " privateKey BINARY NOT NULL,"
|
||||
+ " created BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (authorId))";
|
||||
|
||||
private static final String CREATE_CONTACTS =
|
||||
"CREATE TABLE contacts"
|
||||
+ " (contactId _COUNTER,"
|
||||
+ " authorId _HASH NOT NULL,"
|
||||
+ " name _STRING NOT NULL,"
|
||||
+ " publicKey _BINARY NOT NULL,"
|
||||
+ " localAuthorId _HASH NOT NULL,"
|
||||
+ " (contactId COUNTER,"
|
||||
+ " authorId HASH NOT NULL,"
|
||||
+ " name VARCHAR NOT NULL,"
|
||||
+ " publicKey BINARY NOT NULL,"
|
||||
+ " localAuthorId HASH NOT NULL,"
|
||||
+ " verified BOOLEAN NOT NULL,"
|
||||
+ " active BOOLEAN NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId),"
|
||||
@@ -103,17 +103,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_GROUPS =
|
||||
"CREATE TABLE groups"
|
||||
+ " (groupId _HASH NOT NULL,"
|
||||
+ " clientId _STRING NOT NULL,"
|
||||
+ " descriptor _BINARY NOT NULL,"
|
||||
+ " (groupId HASH NOT NULL,"
|
||||
+ " clientId VARCHAR NOT NULL,"
|
||||
+ " descriptor BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (groupId))";
|
||||
|
||||
private static final String CREATE_GROUP_METADATA =
|
||||
"CREATE TABLE groupMetadata"
|
||||
+ " (groupId _HASH NOT NULL,"
|
||||
+ " metaKey _STRING NOT NULL,"
|
||||
+ " value _BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (groupId, metaKey),"
|
||||
+ " (groupId HASH NOT NULL,"
|
||||
+ " key VARCHAR NOT NULL,"
|
||||
+ " value BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (groupId, key),"
|
||||
+ " FOREIGN KEY (groupId)"
|
||||
+ " REFERENCES groups (groupId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
@@ -121,7 +121,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
private static final String CREATE_GROUP_VISIBILITIES =
|
||||
"CREATE TABLE groupVisibilities"
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " groupId _HASH NOT NULL,"
|
||||
+ " groupId HASH NOT NULL,"
|
||||
+ " shared BOOLEAN NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, groupId),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
@@ -133,8 +133,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_MESSAGES =
|
||||
"CREATE TABLE messages"
|
||||
+ " (messageId _HASH NOT NULL,"
|
||||
+ " groupId _HASH NOT NULL,"
|
||||
+ " (messageId HASH NOT NULL,"
|
||||
+ " groupId HASH NOT NULL,"
|
||||
+ " timestamp BIGINT NOT NULL,"
|
||||
+ " state INT NOT NULL,"
|
||||
+ " shared BOOLEAN NOT NULL,"
|
||||
@@ -147,19 +147,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_MESSAGE_METADATA =
|
||||
"CREATE TABLE messageMetadata"
|
||||
+ " (messageId _HASH NOT NULL,"
|
||||
+ " metaKey _STRING NOT NULL,"
|
||||
+ " value _BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (messageId, metaKey),"
|
||||
+ " (messageId HASH NOT NULL,"
|
||||
+ " key VARCHAR NOT NULL,"
|
||||
+ " value BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (messageId, key),"
|
||||
+ " FOREIGN KEY (messageId)"
|
||||
+ " REFERENCES messages (messageId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
private static final String CREATE_MESSAGE_DEPENDENCIES =
|
||||
"CREATE TABLE messageDependencies"
|
||||
+ " (groupId _HASH NOT NULL,"
|
||||
+ " messageId _HASH NOT NULL,"
|
||||
+ " dependencyId _HASH NOT NULL," // Not a foreign key
|
||||
+ " (groupId HASH NOT NULL,"
|
||||
+ " messageId HASH NOT NULL,"
|
||||
+ " dependencyId HASH NOT NULL," // Not a foreign key
|
||||
+ " FOREIGN KEY (groupId)"
|
||||
+ " REFERENCES groups (groupId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
@@ -169,7 +169,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_OFFERS =
|
||||
"CREATE TABLE offers"
|
||||
+ " (messageId _HASH NOT NULL," // Not a foreign key
|
||||
+ " (messageId HASH NOT NULL," // Not a foreign key
|
||||
+ " contactId INT NOT NULL,"
|
||||
+ " PRIMARY KEY (messageId, contactId),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
@@ -178,7 +178,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_STATUSES =
|
||||
"CREATE TABLE statuses"
|
||||
+ " (messageId _HASH NOT NULL,"
|
||||
+ " (messageId HASH NOT NULL,"
|
||||
+ " contactId INT NOT NULL,"
|
||||
+ " ack BOOLEAN NOT NULL,"
|
||||
+ " seen BOOLEAN NOT NULL,"
|
||||
@@ -195,20 +195,20 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final String CREATE_TRANSPORTS =
|
||||
"CREATE TABLE transports"
|
||||
+ " (transportId _STRING NOT NULL,"
|
||||
+ " (transportId VARCHAR NOT NULL,"
|
||||
+ " maxLatency INT NOT NULL,"
|
||||
+ " PRIMARY KEY (transportId))";
|
||||
|
||||
private static final String CREATE_INCOMING_KEYS =
|
||||
"CREATE TABLE incomingKeys"
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " transportId _STRING NOT NULL,"
|
||||
+ " rotationPeriod BIGINT NOT NULL,"
|
||||
+ " tagKey _SECRET NOT NULL,"
|
||||
+ " headerKey _SECRET NOT NULL,"
|
||||
+ " transportId VARCHAR NOT NULL,"
|
||||
+ " period BIGINT NOT NULL,"
|
||||
+ " tagKey SECRET NOT NULL,"
|
||||
+ " headerKey SECRET NOT NULL,"
|
||||
+ " base BIGINT NOT NULL,"
|
||||
+ " bitmap _BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, transportId, rotationPeriod),"
|
||||
+ " bitmap BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, transportId, period),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
+ " REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
@@ -219,10 +219,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
private static final String CREATE_OUTGOING_KEYS =
|
||||
"CREATE TABLE outgoingKeys"
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " transportId _STRING NOT NULL,"
|
||||
+ " rotationPeriod BIGINT NOT NULL,"
|
||||
+ " tagKey _SECRET NOT NULL,"
|
||||
+ " headerKey _SECRET NOT NULL,"
|
||||
+ " transportId VARCHAR NOT NULL,"
|
||||
+ " period BIGINT NOT NULL,"
|
||||
+ " tagKey SECRET NOT NULL,"
|
||||
+ " headerKey SECRET NOT NULL,"
|
||||
+ " stream BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, transportId),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
@@ -260,8 +260,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
Logger.getLogger(JdbcDatabase.class.getName());
|
||||
|
||||
// Different database libraries use different names for certain types
|
||||
private final String hashType, secretType, binaryType;
|
||||
private final String counterType, stringType;
|
||||
private final String hashType, binaryType, counterType, secretType;
|
||||
private final Clock clock;
|
||||
|
||||
// Locking: connectionsLock
|
||||
@@ -276,13 +275,12 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
private final Lock connectionsLock = new ReentrantLock();
|
||||
private final Condition connectionsChanged = connectionsLock.newCondition();
|
||||
|
||||
JdbcDatabase(String hashType, String secretType, String binaryType,
|
||||
String counterType, String stringType, Clock clock) {
|
||||
JdbcDatabase(String hashType, String binaryType, String counterType,
|
||||
String secretType, Clock clock) {
|
||||
this.hashType = hashType;
|
||||
this.secretType = secretType;
|
||||
this.binaryType = binaryType;
|
||||
this.counterType = counterType;
|
||||
this.stringType = stringType;
|
||||
this.secretType = secretType;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@@ -385,17 +383,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
private String insertTypeNames(String s) {
|
||||
s = s.replaceAll("_HASH", hashType);
|
||||
s = s.replaceAll("_SECRET", secretType);
|
||||
s = s.replaceAll("_BINARY", binaryType);
|
||||
s = s.replaceAll("_COUNTER", counterType);
|
||||
s = s.replaceAll("_STRING", stringType);
|
||||
s = s.replaceAll("HASH", hashType);
|
||||
s = s.replaceAll("BINARY", binaryType);
|
||||
s = s.replaceAll("COUNTER", counterType);
|
||||
s = s.replaceAll("SECRET", secretType);
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection startTransaction() throws DbException {
|
||||
Connection txn;
|
||||
Connection txn = null;
|
||||
connectionsLock.lock();
|
||||
try {
|
||||
if (closed) throw new DbClosedException();
|
||||
@@ -503,8 +500,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
try {
|
||||
// Create a contact row
|
||||
String sql = "INSERT INTO contacts"
|
||||
+ " (authorId, name, publicKey, localAuthorId,"
|
||||
+ " verified, active)"
|
||||
+ " (authorId, name, publicKey, localAuthorId, verified, active)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, remote.getId().getBytes());
|
||||
@@ -723,7 +719,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
try {
|
||||
// Store the incoming keys
|
||||
String sql = "INSERT INTO incomingKeys (contactId, transportId,"
|
||||
+ " rotationPeriod, tagKey, headerKey, base, bitmap)"
|
||||
+ " period, tagKey, headerKey, base, bitmap)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
@@ -758,8 +754,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (rows != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
// Store the outgoing keys
|
||||
sql = "INSERT INTO outgoingKeys (contactId, transportId,"
|
||||
+ " rotationPeriod, tagKey, headerKey, stream)"
|
||||
sql = "INSERT INTO outgoingKeys (contactId, transportId, period,"
|
||||
+ " tagKey, headerKey, stream)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
@@ -1339,7 +1335,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " JOIN messageMetadata AS md"
|
||||
+ " ON m.messageId = md.messageId"
|
||||
+ " WHERE state = ? AND groupId = ?"
|
||||
+ " AND metaKey = ? AND value = ?";
|
||||
+ " AND key = ? AND value = ?";
|
||||
for (Entry<String, byte[]> e : query.entrySet()) {
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, DELIVERED.getValue());
|
||||
@@ -1371,7 +1367,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT m.messageId, metaKey, value"
|
||||
String sql = "SELECT m.messageId, key, value"
|
||||
+ " FROM messages AS m"
|
||||
+ " JOIN messageMetadata AS md"
|
||||
+ " ON m.messageId = md.messageId"
|
||||
@@ -1421,7 +1417,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT metaKey, value FROM groupMetadata"
|
||||
String sql = "SELECT key, value FROM groupMetadata"
|
||||
+ " WHERE groupId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getBytes());
|
||||
@@ -1444,7 +1440,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT metaKey, value FROM messageMetadata AS md"
|
||||
String sql = "SELECT key, value FROM messageMetadata AS md"
|
||||
+ " JOIN messages AS m"
|
||||
+ " ON m.messageId = md.messageId"
|
||||
+ " WHERE m.state = ? AND md.messageId = ?";
|
||||
@@ -1470,7 +1466,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT metaKey, value FROM messageMetadata AS md"
|
||||
String sql = "SELECT key, value FROM messageMetadata AS md"
|
||||
+ " JOIN messages AS m"
|
||||
+ " ON m.messageId = md.messageId"
|
||||
+ " WHERE (m.state = ? OR m.state = ?)"
|
||||
@@ -1908,8 +1904,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT settingKey, value FROM settings"
|
||||
+ " WHERE namespace = ?";
|
||||
String sql = "SELECT key, value FROM settings WHERE namespace = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, namespace);
|
||||
rs = ps.executeQuery();
|
||||
@@ -1932,11 +1927,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
// Retrieve the incoming keys
|
||||
String sql = "SELECT rotationPeriod, tagKey, headerKey,"
|
||||
+ " base, bitmap"
|
||||
String sql = "SELECT period, tagKey, headerKey, base, bitmap"
|
||||
+ " FROM incomingKeys"
|
||||
+ " WHERE transportId = ?"
|
||||
+ " ORDER BY contactId, rotationPeriod";
|
||||
+ " ORDER BY contactId, period";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, t.getString());
|
||||
rs = ps.executeQuery();
|
||||
@@ -1953,10 +1947,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
rs.close();
|
||||
ps.close();
|
||||
// Retrieve the outgoing keys in the same order
|
||||
sql = "SELECT contactId, rotationPeriod, tagKey, headerKey, stream"
|
||||
sql = "SELECT contactId, period, tagKey, headerKey, stream"
|
||||
+ " FROM outgoingKeys"
|
||||
+ " WHERE transportId = ?"
|
||||
+ " ORDER BY contactId, rotationPeriod";
|
||||
+ " ORDER BY contactId, period";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, t.getString());
|
||||
rs = ps.executeQuery();
|
||||
@@ -1993,8 +1987,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE outgoingKeys SET stream = stream + 1"
|
||||
+ " WHERE contactId = ? AND transportId = ?"
|
||||
+ " AND rotationPeriod = ?";
|
||||
+ " WHERE contactId = ? AND transportId = ? AND period = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setString(2, t.getString());
|
||||
@@ -2088,7 +2081,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
// Delete any keys that are being removed
|
||||
if (!removed.isEmpty()) {
|
||||
String sql = "DELETE FROM " + tableName
|
||||
+ " WHERE " + columnName + " = ? AND metaKey = ?";
|
||||
+ " WHERE " + columnName + " = ? AND key = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
for (String key : removed) {
|
||||
@@ -2107,7 +2100,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (retained.isEmpty()) return;
|
||||
// Update any keys that already exist
|
||||
String sql = "UPDATE " + tableName + " SET value = ?"
|
||||
+ " WHERE " + columnName + " = ? AND metaKey = ?";
|
||||
+ " WHERE " + columnName + " = ? AND key = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(2, id);
|
||||
for (Entry<String, byte[]> e : retained.entrySet()) {
|
||||
@@ -2124,7 +2117,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
// Insert any keys that don't already exist
|
||||
sql = "INSERT INTO " + tableName
|
||||
+ " (" + columnName + ", metaKey, value)"
|
||||
+ " (" + columnName + ", key, value)"
|
||||
+ " VALUES (?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
@@ -2156,7 +2149,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
try {
|
||||
// Update any settings that already exist
|
||||
String sql = "UPDATE settings SET value = ?"
|
||||
+ " WHERE namespace = ? AND settingKey = ?";
|
||||
+ " WHERE namespace = ? AND key = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
for (Entry<String, String> e : s.entrySet()) {
|
||||
ps.setString(1, e.getValue());
|
||||
@@ -2171,7 +2164,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (rows > 1) throw new DbStateException();
|
||||
}
|
||||
// Insert any settings that don't already exist
|
||||
sql = "INSERT INTO settings (namespace, settingKey, value)"
|
||||
sql = "INSERT INTO settings (namespace, key, value)"
|
||||
+ " VALUES (?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
int updateIndex = 0, inserted = 0;
|
||||
@@ -2535,8 +2528,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE incomingKeys SET base = ?, bitmap = ?"
|
||||
+ " WHERE contactId = ? AND transportId = ?"
|
||||
+ " AND rotationPeriod = ?";
|
||||
+ " WHERE contactId = ? AND transportId = ? AND period = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, base);
|
||||
ps.setBytes(2, bitmap);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||
@@ -46,7 +46,7 @@ class KeyAgreementConnector {
|
||||
|
||||
private final Callbacks callbacks;
|
||||
private final Clock clock;
|
||||
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||
private final CryptoComponent crypto;
|
||||
private final PluginManager pluginManager;
|
||||
private final CompletionService<KeyAgreementConnection> connect;
|
||||
|
||||
@@ -58,11 +58,11 @@ class KeyAgreementConnector {
|
||||
private volatile boolean alice = false;
|
||||
|
||||
KeyAgreementConnector(Callbacks callbacks, Clock clock,
|
||||
KeyAgreementCrypto keyAgreementCrypto, PluginManager pluginManager,
|
||||
CryptoComponent crypto, PluginManager pluginManager,
|
||||
Executor ioExecutor) {
|
||||
this.callbacks = callbacks;
|
||||
this.clock = clock;
|
||||
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||
this.crypto = crypto;
|
||||
this.pluginManager = pluginManager;
|
||||
connect = new ExecutorCompletionService<>(ioExecutor);
|
||||
}
|
||||
@@ -70,8 +70,8 @@ class KeyAgreementConnector {
|
||||
public Payload listen(KeyPair localKeyPair) {
|
||||
LOG.info("Starting BQP listeners");
|
||||
// Derive commitment
|
||||
byte[] commitment = keyAgreementCrypto.deriveKeyCommitment(
|
||||
localKeyPair.getPublic());
|
||||
byte[] commitment = crypto.deriveKeyCommitment(
|
||||
localKeyPair.getPublic().getEncoded());
|
||||
// Start all listeners and collect their descriptors
|
||||
List<TransportDescriptor> descriptors = new ArrayList<>();
|
||||
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -13,9 +22,13 @@ import dagger.Provides;
|
||||
public class KeyAgreementModule {
|
||||
|
||||
@Provides
|
||||
KeyAgreementTask provideKeyAgreementTask(
|
||||
KeyAgreementTaskImpl keyAgreementTask) {
|
||||
return keyAgreementTask;
|
||||
@Singleton
|
||||
KeyAgreementTaskFactory provideKeyAgreementTaskFactory(Clock clock,
|
||||
CryptoComponent crypto, EventBus eventBus,
|
||||
@IoExecutor Executor ioExecutor, PayloadEncoder payloadEncoder,
|
||||
PluginManager pluginManager) {
|
||||
return new KeyAgreementTaskFactoryImpl(clock, crypto, eventBus,
|
||||
ioExecutor, payloadEncoder, pluginManager);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
@@ -14,10 +11,6 @@ import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
||||
|
||||
/**
|
||||
* Implementation of the BQP protocol.
|
||||
* <p/>
|
||||
@@ -64,7 +57,6 @@ class KeyAgreementProtocol {
|
||||
|
||||
private final Callbacks callbacks;
|
||||
private final CryptoComponent crypto;
|
||||
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||
private final PayloadEncoder payloadEncoder;
|
||||
private final KeyAgreementTransport transport;
|
||||
private final Payload theirPayload, ourPayload;
|
||||
@@ -72,13 +64,11 @@ class KeyAgreementProtocol {
|
||||
private final boolean alice;
|
||||
|
||||
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
|
||||
KeyAgreementCrypto keyAgreementCrypto,
|
||||
PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
|
||||
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
|
||||
boolean alice) {
|
||||
this.callbacks = callbacks;
|
||||
this.crypto = crypto;
|
||||
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||
this.payloadEncoder = payloadEncoder;
|
||||
this.transport = transport;
|
||||
this.theirPayload = theirPayload;
|
||||
@@ -96,7 +86,7 @@ class KeyAgreementProtocol {
|
||||
*/
|
||||
SecretKey perform() throws AbortException, IOException {
|
||||
try {
|
||||
PublicKey theirPublicKey;
|
||||
byte[] theirPublicKey;
|
||||
if (alice) {
|
||||
sendKey();
|
||||
// Alice waits here until Bob obtains her payload.
|
||||
@@ -114,7 +104,7 @@ class KeyAgreementProtocol {
|
||||
receiveConfirm(s, theirPublicKey);
|
||||
sendConfirm(s, theirPublicKey);
|
||||
}
|
||||
return crypto.deriveKey(MASTER_SECRET_LABEL, s);
|
||||
return crypto.deriveMasterSecret(s);
|
||||
} catch (AbortException e) {
|
||||
sendAbort(e.getCause() != null);
|
||||
throw e;
|
||||
@@ -125,41 +115,27 @@ class KeyAgreementProtocol {
|
||||
transport.sendKey(ourKeyPair.getPublic().getEncoded());
|
||||
}
|
||||
|
||||
private PublicKey receiveKey() throws AbortException {
|
||||
byte[] publicKeyBytes = transport.receiveKey();
|
||||
private byte[] receiveKey() throws AbortException {
|
||||
byte[] publicKey = transport.receiveKey();
|
||||
callbacks.initialRecordReceived();
|
||||
KeyParser keyParser = crypto.getAgreementKeyParser();
|
||||
try {
|
||||
PublicKey publicKey = keyParser.parsePublicKey(publicKeyBytes);
|
||||
byte[] expected = keyAgreementCrypto.deriveKeyCommitment(publicKey);
|
||||
if (!Arrays.equals(expected, theirPayload.getCommitment()))
|
||||
throw new AbortException();
|
||||
return publicKey;
|
||||
} catch (GeneralSecurityException e) {
|
||||
byte[] expected = crypto.deriveKeyCommitment(publicKey);
|
||||
if (!Arrays.equals(expected, theirPayload.getCommitment()))
|
||||
throw new AbortException();
|
||||
}
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
private SecretKey deriveSharedSecret(PublicKey theirPublicKey)
|
||||
private SecretKey deriveSharedSecret(byte[] theirPublicKey)
|
||||
throws AbortException {
|
||||
try {
|
||||
byte[] ourPublicKeyBytes = ourKeyPair.getPublic().getEncoded();
|
||||
byte[] theirPublicKeyBytes = theirPublicKey.getEncoded();
|
||||
byte[][] inputs = {
|
||||
new byte[] {PROTOCOL_VERSION},
|
||||
alice ? ourPublicKeyBytes : theirPublicKeyBytes,
|
||||
alice ? theirPublicKeyBytes : ourPublicKeyBytes
|
||||
};
|
||||
return crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||
theirPublicKey, ourKeyPair, inputs);
|
||||
return crypto.deriveSharedSecret(theirPublicKey, ourKeyPair, alice);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new AbortException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendConfirm(SecretKey s, PublicKey theirPublicKey)
|
||||
private void sendConfirm(SecretKey s, byte[] theirPublicKey)
|
||||
throws IOException {
|
||||
byte[] confirm = keyAgreementCrypto.deriveConfirmationRecord(s,
|
||||
byte[] confirm = crypto.deriveConfirmationRecord(s,
|
||||
payloadEncoder.encode(theirPayload),
|
||||
payloadEncoder.encode(ourPayload),
|
||||
theirPublicKey, ourKeyPair,
|
||||
@@ -167,10 +143,10 @@ class KeyAgreementProtocol {
|
||||
transport.sendConfirm(confirm);
|
||||
}
|
||||
|
||||
private void receiveConfirm(SecretKey s, PublicKey theirPublicKey)
|
||||
private void receiveConfirm(SecretKey s, byte[] theirPublicKey)
|
||||
throws AbortException {
|
||||
byte[] confirm = transport.receiveConfirm();
|
||||
byte[] expected = keyAgreementCrypto.deriveConfirmationRecord(s,
|
||||
byte[] expected = crypto.deriveConfirmationRecord(s,
|
||||
payloadEncoder.encode(theirPayload),
|
||||
payloadEncoder.encode(ourPayload),
|
||||
theirPublicKey, ourKeyPair,
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class KeyAgreementTaskFactoryImpl implements KeyAgreementTaskFactory {
|
||||
|
||||
private final Clock clock;
|
||||
private final CryptoComponent crypto;
|
||||
private final EventBus eventBus;
|
||||
private final Executor ioExecutor;
|
||||
private final PayloadEncoder payloadEncoder;
|
||||
private final PluginManager pluginManager;
|
||||
|
||||
@Inject
|
||||
KeyAgreementTaskFactoryImpl(Clock clock, CryptoComponent crypto,
|
||||
EventBus eventBus, @IoExecutor Executor ioExecutor,
|
||||
PayloadEncoder payloadEncoder, PluginManager pluginManager) {
|
||||
this.clock = clock;
|
||||
this.crypto = crypto;
|
||||
this.eventBus = eventBus;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.payloadEncoder = payloadEncoder;
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyAgreementTask createTask() {
|
||||
return new KeyAgreementTaskImpl(clock, crypto, eventBus, payloadEncoder,
|
||||
pluginManager, ioExecutor);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
@@ -15,7 +14,6 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent
|
||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
|
||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
|
||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
@@ -25,8 +23,6 @@ import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -39,7 +35,6 @@ class KeyAgreementTaskImpl extends Thread implements
|
||||
Logger.getLogger(KeyAgreementTaskImpl.class.getName());
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||
private final EventBus eventBus;
|
||||
private final PayloadEncoder payloadEncoder;
|
||||
private final KeyPair localKeyPair;
|
||||
@@ -48,17 +43,14 @@ class KeyAgreementTaskImpl extends Thread implements
|
||||
private Payload localPayload;
|
||||
private Payload remotePayload;
|
||||
|
||||
@Inject
|
||||
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
|
||||
KeyAgreementCrypto keyAgreementCrypto, EventBus eventBus,
|
||||
PayloadEncoder payloadEncoder, PluginManager pluginManager,
|
||||
@IoExecutor Executor ioExecutor) {
|
||||
EventBus eventBus, PayloadEncoder payloadEncoder,
|
||||
PluginManager pluginManager, Executor ioExecutor) {
|
||||
this.crypto = crypto;
|
||||
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||
this.eventBus = eventBus;
|
||||
this.payloadEncoder = payloadEncoder;
|
||||
localKeyPair = crypto.generateAgreementKeyPair();
|
||||
connector = new KeyAgreementConnector(this, clock, keyAgreementCrypto,
|
||||
connector = new KeyAgreementConnector(this, clock, crypto,
|
||||
pluginManager, ioExecutor);
|
||||
}
|
||||
|
||||
@@ -108,8 +100,8 @@ class KeyAgreementTaskImpl extends Thread implements
|
||||
// Run BQP protocol over the connection
|
||||
LOG.info("Starting BQP protocol");
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
|
||||
keyAgreementCrypto, payloadEncoder, transport, remotePayload,
|
||||
localPayload, localKeyPair, alice);
|
||||
payloadEncoder, transport, remotePayload, localPayload,
|
||||
localKeyPair, alice);
|
||||
try {
|
||||
SecretKey master = protocol.perform();
|
||||
KeyAgreementResult result =
|
||||
|
||||
@@ -58,8 +58,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
this.metadataParser = metadataParser;
|
||||
this.contactGroupFactory = contactGroupFactory;
|
||||
this.clock = clock;
|
||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
|
||||
CLIENT_VERSION);
|
||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,7 +129,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
public Map<TransportId, TransportProperties> getLocalProperties()
|
||||
throws DbException {
|
||||
Map<TransportId, TransportProperties> local;
|
||||
Transaction txn = db.startTransaction(true);
|
||||
// TODO: Transaction can be read-only when code is simplified
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
local = getLocalProperties(txn);
|
||||
db.commitTransaction(txn);
|
||||
@@ -165,7 +165,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
throws DbException {
|
||||
try {
|
||||
TransportProperties p = null;
|
||||
Transaction txn = db.startTransaction(true);
|
||||
// TODO: Transaction can be read-only when code is simplified
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
// Find the latest local update
|
||||
LatestUpdate latest = findLatest(txn, localGroup.getId(), t,
|
||||
@@ -191,7 +192,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
public Map<ContactId, TransportProperties> getRemoteProperties(
|
||||
TransportId t) throws DbException {
|
||||
Map<ContactId, TransportProperties> remote = new HashMap<>();
|
||||
Transaction txn = db.startTransaction(true);
|
||||
// TODO: Transaction can be read-only when code is simplified
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn))
|
||||
remote.put(c.getId(), getRemoteProperties(txn, c, t));
|
||||
@@ -225,7 +227,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
public TransportProperties getRemoteProperties(ContactId c, TransportId t)
|
||||
throws DbException {
|
||||
TransportProperties p;
|
||||
Transaction txn = db.startTransaction(true);
|
||||
// TODO: Transaction can be read-only when code is simplified
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
p = getRemoteProperties(txn, db.getContact(txn, c), t);
|
||||
db.commitTransaction(txn);
|
||||
@@ -288,8 +291,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
}
|
||||
|
||||
private Group getContactGroup(Contact c) {
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, c);
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID, c);
|
||||
}
|
||||
|
||||
private void storeMessage(Transaction txn, GroupId g, TransportId t,
|
||||
@@ -316,6 +318,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
|
||||
private Map<TransportId, LatestUpdate> findLatestLocal(Transaction txn)
|
||||
throws DbException, FormatException {
|
||||
// TODO: This can be simplified before 1.0
|
||||
Map<TransportId, LatestUpdate> latestUpdates = new HashMap<>();
|
||||
Map<MessageId, BdfDictionary> metadata = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, localGroup.getId());
|
||||
@@ -323,7 +326,17 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
BdfDictionary meta = e.getValue();
|
||||
TransportId t = new TransportId(meta.getString("transportId"));
|
||||
long version = meta.getLong("version");
|
||||
latestUpdates.put(t, new LatestUpdate(e.getKey(), version));
|
||||
LatestUpdate latest = latestUpdates.get(t);
|
||||
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;
|
||||
}
|
||||
@@ -331,16 +344,38 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
@Nullable
|
||||
private LatestUpdate findLatest(Transaction txn, GroupId g, TransportId t,
|
||||
boolean local) throws DbException, FormatException {
|
||||
// TODO: This can be simplified before 1.0
|
||||
LatestUpdate latest = null;
|
||||
Map<MessageId, BdfDictionary> metadata =
|
||||
clientHelper.getMessageMetadataAsDictionary(txn, g);
|
||||
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
|
||||
BdfDictionary meta = e.getValue();
|
||||
if (meta.getString("transportId").equals(t.getString())
|
||||
&& meta.getBoolean("local") == local) {
|
||||
return new LatestUpdate(e.getKey(), meta.getLong("version"));
|
||||
long version = 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 null;
|
||||
return latest;
|
||||
}
|
||||
|
||||
private TransportProperties parseProperties(BdfList message)
|
||||
|
||||
@@ -6,16 +6,11 @@ import org.briarproject.bramble.api.sync.ClientId;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.GroupFactory;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.sync.GroupId.LABEL;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class GroupFactoryImpl implements GroupFactory {
|
||||
@@ -28,12 +23,9 @@ class GroupFactoryImpl implements GroupFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createGroup(ClientId c, int clientVersion, byte[] descriptor) {
|
||||
byte[] clientVersionBytes = new byte[INT_32_BYTES];
|
||||
ByteUtils.writeUint32(clientVersion, clientVersionBytes, 0);
|
||||
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
|
||||
StringUtils.toUtf8(c.getString()), clientVersionBytes,
|
||||
descriptor);
|
||||
public Group createGroup(ClientId c, byte[] descriptor) {
|
||||
byte[] hash = crypto.hash(GroupId.LABEL,
|
||||
StringUtils.toUtf8(c.getString()), descriptor);
|
||||
return new Group(new GroupId(hash), c, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,8 @@ import org.briarproject.bramble.util.ByteUtils;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.sync.MessageId.LABEL;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -34,9 +32,9 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
throw new IllegalArgumentException();
|
||||
byte[] timeBytes = new byte[ByteUtils.INT_64_BYTES];
|
||||
ByteUtils.writeUint64(timestamp, timeBytes, 0);
|
||||
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
|
||||
g.getBytes(), timeBytes, body);
|
||||
MessageId id = new MessageId(hash);
|
||||
byte[] idHash =
|
||||
crypto.hash(MessageId.LABEL, g.getBytes(), timeBytes, body);
|
||||
MessageId id = new MessageId(idHash);
|
||||
byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length];
|
||||
System.arraycopy(g.getBytes(), 0, raw, 0, UniqueId.LENGTH);
|
||||
ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
@@ -20,18 +20,17 @@ class TransportKeyManagerFactoryImpl implements
|
||||
TransportKeyManagerFactory {
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final TransportCrypto transportCrypto;
|
||||
private final CryptoComponent crypto;
|
||||
private final Executor dbExecutor;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
TransportKeyManagerFactoryImpl(DatabaseComponent db,
|
||||
TransportCrypto transportCrypto,
|
||||
TransportKeyManagerFactoryImpl(DatabaseComponent db, CryptoComponent crypto,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
@Scheduler ScheduledExecutorService scheduler, Clock clock) {
|
||||
this.db = db;
|
||||
this.transportCrypto = transportCrypto;
|
||||
this.crypto = crypto;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.scheduler = scheduler;
|
||||
this.clock = clock;
|
||||
@@ -40,8 +39,8 @@ class TransportKeyManagerFactoryImpl implements
|
||||
@Override
|
||||
public TransportKeyManager createTransportKeyManager(
|
||||
TransportId transportId, long maxLatency) {
|
||||
return new TransportKeyManagerImpl(db, transportCrypto, dbExecutor,
|
||||
scheduler, clock, transportId, maxLatency);
|
||||
return new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler,
|
||||
clock, transportId, maxLatency);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
@@ -41,7 +41,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
Logger.getLogger(TransportKeyManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final TransportCrypto transportCrypto;
|
||||
private final CryptoComponent crypto;
|
||||
private final Executor dbExecutor;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Clock clock;
|
||||
@@ -54,12 +54,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
private final Map<ContactId, MutableOutgoingKeys> outContexts;
|
||||
private final Map<ContactId, MutableTransportKeys> keys;
|
||||
|
||||
TransportKeyManagerImpl(DatabaseComponent db,
|
||||
TransportCrypto transportCrypto, Executor dbExecutor,
|
||||
@Scheduler ScheduledExecutorService scheduler, Clock clock,
|
||||
TransportId transportId, long maxLatency) {
|
||||
TransportKeyManagerImpl(DatabaseComponent db, CryptoComponent crypto,
|
||||
Executor dbExecutor, @Scheduler ScheduledExecutorService scheduler,
|
||||
Clock clock, TransportId transportId, long maxLatency) {
|
||||
this.db = db;
|
||||
this.transportCrypto = transportCrypto;
|
||||
this.crypto = crypto;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.scheduler = scheduler;
|
||||
this.clock = clock;
|
||||
@@ -100,8 +99,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
|
||||
ContactId c = e.getKey();
|
||||
TransportKeys k = e.getValue();
|
||||
TransportKeys k1 =
|
||||
transportCrypto.rotateTransportKeys(k, rotationPeriod);
|
||||
TransportKeys k1 = crypto.rotateTransportKeys(k, rotationPeriod);
|
||||
if (k1.getRotationPeriod() > k.getRotationPeriod())
|
||||
rotationResult.rotated.put(c, k1);
|
||||
rotationResult.current.put(c, k1);
|
||||
@@ -129,7 +127,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
||||
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||
crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
inContexts.put(new Bytes(tag), tagCtx);
|
||||
}
|
||||
@@ -164,11 +162,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
// Work out what rotation period the timestamp belongs to
|
||||
long rotationPeriod = timestamp / rotationPeriodLength;
|
||||
// Derive the transport keys
|
||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, rotationPeriod, alice);
|
||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
||||
rotationPeriod, alice);
|
||||
// Rotate the keys to the current rotation period if necessary
|
||||
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
|
||||
k = transportCrypto.rotateTransportKeys(k, rotationPeriod);
|
||||
k = crypto.rotateTransportKeys(k, rotationPeriod);
|
||||
// Initialise mutable state for the contact
|
||||
addKeys(c, new MutableTransportKeys(k));
|
||||
// Write the keys back to the DB
|
||||
@@ -236,8 +234,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
// Add tags for any stream numbers added to the window
|
||||
for (long streamNumber : change.getAdded()) {
|
||||
byte[] addTag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
|
||||
PROTOCOL_VERSION, streamNumber);
|
||||
crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
inContexts.put(new Bytes(addTag), new TagContext(
|
||||
tagCtx.contactId, inKeys, streamNumber));
|
||||
}
|
||||
@@ -245,7 +243,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
for (long streamNumber : change.getRemoved()) {
|
||||
if (streamNumber == tagCtx.streamNumber) continue;
|
||||
byte[] removeTag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(removeTag, inKeys.getTagKey(),
|
||||
crypto.encodeTag(removeTag, inKeys.getTagKey(),
|
||||
PROTOCOL_VERSION, streamNumber);
|
||||
inContexts.remove(new Bytes(removeTag));
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
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,19 +1,16 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import net.i2p.crypto.eddsa.EdDSASecurityProvider;
|
||||
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
||||
|
||||
import org.spongycastle.asn1.sec.SECNamedCurves;
|
||||
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||
import org.spongycastle.asn1.x9.X9ECParameters;
|
||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.spongycastle.crypto.BasicAgreement;
|
||||
import org.spongycastle.crypto.Digest;
|
||||
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
|
||||
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
||||
import org.spongycastle.crypto.params.ECDomainParameters;
|
||||
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.spongycastle.crypto.params.ParametersWithRandom;
|
||||
import org.spongycastle.crypto.signers.DSADigestSigner;
|
||||
import org.spongycastle.crypto.signers.DSAKCalculator;
|
||||
@@ -22,23 +19,14 @@ import org.spongycastle.crypto.signers.HMacDSAKCalculator;
|
||||
import org.spongycastle.math.ec.ECCurve;
|
||||
import org.spongycastle.math.ec.ECPoint;
|
||||
import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
|
||||
import org.whispersystems.curve25519.Curve25519;
|
||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Signature;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
|
||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
||||
|
||||
// Not a JUnit test
|
||||
public class EllipticCurvePerformanceTest {
|
||||
|
||||
@@ -49,9 +37,8 @@ public class EllipticCurvePerformanceTest {
|
||||
"secp256k1", "secp256r1", "secp384r1", "secp521r1");
|
||||
private static final List<String> BRAINPOOL_NAMES = Arrays.asList(
|
||||
"brainpoolp256r1", "brainpoolp384r1", "brainpoolp512r1");
|
||||
private static final Provider ED_PROVIDER = new EdDSASecurityProvider();
|
||||
|
||||
public static void main(String[] args) throws GeneralSecurityException {
|
||||
public static void main(String[] args) {
|
||||
for (String name : SEC_NAMES) {
|
||||
ECDomainParameters params =
|
||||
convertParams(SECNamedCurves.getByName(name));
|
||||
@@ -64,32 +51,43 @@ public class EllipticCurvePerformanceTest {
|
||||
runTest(name + " default", params);
|
||||
runTest(name + " constant", constantTime(params));
|
||||
}
|
||||
runTest("ours", PARAMETERS);
|
||||
runCurve25519Test();
|
||||
runEd25519Test();
|
||||
runTest("ours", EllipticCurveConstants.PARAMETERS);
|
||||
}
|
||||
|
||||
private static void runTest(String name, ECDomainParameters params) {
|
||||
// Generate two key pairs using the given parameters
|
||||
ECKeyGenerationParameters generatorParams =
|
||||
new ECKeyGenerationParameters(params, random);
|
||||
ECKeyPairGenerator generator = new ECKeyPairGenerator();
|
||||
generator.init(new ECKeyGenerationParameters(params, random));
|
||||
generator.init(generatorParams);
|
||||
AsymmetricCipherKeyPair keyPair1 = generator.generateKeyPair();
|
||||
ECPublicKeyParameters public1 =
|
||||
(ECPublicKeyParameters) keyPair1.getPublic();
|
||||
ECPrivateKeyParameters private1 =
|
||||
(ECPrivateKeyParameters) keyPair1.getPrivate();
|
||||
AsymmetricCipherKeyPair keyPair2 = generator.generateKeyPair();
|
||||
// Time some ECDH and ECDHC key agreements
|
||||
long agreementMedian = runAgreementTest(keyPair1, keyPair2, false);
|
||||
long agreementWithCofactorMedian =
|
||||
runAgreementTest(keyPair1, keyPair2, true);
|
||||
// Time some signatures
|
||||
ECPublicKeyParameters public2 =
|
||||
(ECPublicKeyParameters) keyPair2.getPublic();
|
||||
// Time some ECDH key agreements
|
||||
List<Long> samples = new ArrayList<>();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
|
||||
long start = System.nanoTime();
|
||||
agreement.init(private1);
|
||||
agreement.calculateAgreement(public2);
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
long agreementMedian = median(samples);
|
||||
// Time some signatures
|
||||
List<byte[]> signatures = new ArrayList<>();
|
||||
samples.clear();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
Digest digest = new Blake2sDigest();
|
||||
DSAKCalculator calculator = new HMacDSAKCalculator(digest);
|
||||
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
||||
calculator), digest);
|
||||
long start = System.nanoTime();
|
||||
signer.init(true,
|
||||
new ParametersWithRandom(keyPair1.getPrivate(), random));
|
||||
signer.init(true, new ParametersWithRandom(private1, random));
|
||||
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||
signatures.add(signer.generateSignature());
|
||||
samples.add(System.nanoTime() - start);
|
||||
@@ -103,83 +101,17 @@ public class EllipticCurvePerformanceTest {
|
||||
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
||||
calculator), digest);
|
||||
long start = System.nanoTime();
|
||||
signer.init(false, keyPair1.getPublic());
|
||||
signer.init(false, public1);
|
||||
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||
if (!signer.verifySignature(signatures.get(i)))
|
||||
throw new AssertionError();
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
long verificationMedian = median(samples);
|
||||
System.out.println(String.format("%s: %,d %,d %,d %,d", name,
|
||||
agreementMedian, agreementWithCofactorMedian,
|
||||
signatureMedian, verificationMedian));
|
||||
}
|
||||
|
||||
private static long runAgreementTest(AsymmetricCipherKeyPair keyPair1,
|
||||
AsymmetricCipherKeyPair keyPair2, boolean withCofactor) {
|
||||
List<Long> samples = new ArrayList<>();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
BasicAgreement agreement = createAgreement(withCofactor);
|
||||
long start = System.nanoTime();
|
||||
agreement.init(keyPair1.getPrivate());
|
||||
agreement.calculateAgreement(keyPair2.getPublic());
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
return median(samples);
|
||||
}
|
||||
|
||||
private static BasicAgreement createAgreement(boolean withCofactor) {
|
||||
if (withCofactor) return new ECDHCBasicAgreement();
|
||||
else return new ECDHBasicAgreement();
|
||||
}
|
||||
|
||||
private static void runCurve25519Test() {
|
||||
Curve25519 curve25519 = Curve25519.getInstance("java");
|
||||
Curve25519KeyPair keyPair1 = curve25519.generateKeyPair();
|
||||
Curve25519KeyPair keyPair2 = curve25519.generateKeyPair();
|
||||
// Time some key agreements
|
||||
List<Long> samples = new ArrayList<>();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
long start = System.nanoTime();
|
||||
curve25519.calculateAgreement(keyPair1.getPublicKey(),
|
||||
keyPair2.getPrivateKey());
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
long agreementMedian = median(samples);
|
||||
System.out.println(String.format("Curve25519: %,d - - -",
|
||||
agreementMedian));
|
||||
}
|
||||
|
||||
private static void runEd25519Test() throws GeneralSecurityException {
|
||||
KeyPair keyPair = new KeyPairGenerator().generateKeyPair();
|
||||
// Time some signatures
|
||||
List<Long> samples = new ArrayList<>();
|
||||
List<byte[]> signatures = new ArrayList<>();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
Signature signature =
|
||||
Signature.getInstance(SIGNATURE_ALGORITHM, ED_PROVIDER);
|
||||
long start = System.nanoTime();
|
||||
signature.initSign(keyPair.getPrivate(), random);
|
||||
signature.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||
signatures.add(signature.sign());
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
long signatureMedian = median(samples);
|
||||
// Time some signature verifications
|
||||
samples.clear();
|
||||
for (int i = 0; i < SAMPLES; i++) {
|
||||
Signature signature =
|
||||
Signature.getInstance(SIGNATURE_ALGORITHM, ED_PROVIDER);
|
||||
long start = System.nanoTime();
|
||||
signature.initVerify(keyPair.getPublic());
|
||||
signature.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||
if (!signature.verify(signatures.get(i)))
|
||||
throw new AssertionError();
|
||||
samples.add(System.nanoTime() - start);
|
||||
}
|
||||
long verificationMedian = median(samples);
|
||||
System.out.println(String.format("Ed25519: - - %,d %,d",
|
||||
signatureMedian, verificationMedian));
|
||||
System.out.println(name + ": "
|
||||
+ agreementMedian + " "
|
||||
+ signatureMedian + " "
|
||||
+ verificationMedian);
|
||||
}
|
||||
|
||||
private static long median(List<Long> list) {
|
||||
|
||||
@@ -3,32 +3,40 @@ package org.briarproject.bramble.crypto;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
public class KeyAgreementTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testDeriveSharedSecret() throws Exception {
|
||||
CryptoComponent crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
public void testDeriveMasterSecret() throws Exception {
|
||||
SecureRandomProvider
|
||||
secureRandomProvider = new TestSecureRandomProvider();
|
||||
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||
byte[] aPub = aPair.getPublic().getEncoded();
|
||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||
Random random = new Random();
|
||||
byte[][] inputs = new byte[random.nextInt(10) + 1][];
|
||||
for (int i = 0; i < inputs.length; i++)
|
||||
inputs[i] = getRandomBytes(random.nextInt(256));
|
||||
SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||
bPair.getPublic(), aPair, inputs);
|
||||
SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||
aPair.getPublic(), bPair, inputs);
|
||||
byte[] bPub = bPair.getPublic().getEncoded();
|
||||
SecretKey aMaster = crypto.deriveMasterSecret(aPub, bPair, true);
|
||||
SecretKey bMaster = crypto.deriveMasterSecret(bPub, aPair, false);
|
||||
assertArrayEquals(aMaster.getBytes(), bMaster.getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeriveSharedSecret() throws Exception {
|
||||
SecureRandomProvider
|
||||
secureRandomProvider = new TestSecureRandomProvider();
|
||||
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||
byte[] aPub = aPair.getPublic().getEncoded();
|
||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||
byte[] bPub = bPair.getPublic().getEncoded();
|
||||
SecretKey aShared = crypto.deriveSharedSecret(bPub, aPair, true);
|
||||
SecretKey bShared = crypto.deriveSharedSecret(aPub, bPair, false);
|
||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package org.briarproject.bramble.crypto;
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -16,34 +16,35 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class KeyDerivationTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponent crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
private final TransportCrypto transportCrypto =
|
||||
new TransportCryptoImpl(crypto);
|
||||
private final TransportId transportId = new TransportId("id");
|
||||
private final SecretKey master = getSecretKey();
|
||||
private final CryptoComponent crypto;
|
||||
private final SecretKey master;
|
||||
|
||||
public KeyDerivationTest() {
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
master = TestUtils.getSecretKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeysAreDistinct() {
|
||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
assertAllDifferent(k);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrentKeysMatchCurrentKeysOfContact() {
|
||||
// Start in rotation period 123
|
||||
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, false);
|
||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
||||
123, false);
|
||||
// Alice's incoming keys should equal Bob's outgoing keys
|
||||
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||
@@ -55,8 +56,8 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
|
||||
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
|
||||
// Rotate into the future
|
||||
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||
kB = transportCrypto.rotateTransportKeys(kB, 456);
|
||||
kA = crypto.rotateTransportKeys(kA, 456);
|
||||
kB = crypto.rotateTransportKeys(kB, 456);
|
||||
// Alice's incoming keys should equal Bob's outgoing keys
|
||||
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
|
||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||
@@ -72,23 +73,22 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testPreviousKeysMatchPreviousKeysOfContact() {
|
||||
// Start in rotation period 123
|
||||
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, false);
|
||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
||||
123, false);
|
||||
// Compare Alice's previous keys in period 456 with Bob's current keys
|
||||
// in period 455
|
||||
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||
kB = transportCrypto.rotateTransportKeys(kB, 455);
|
||||
kA = crypto.rotateTransportKeys(kA, 456);
|
||||
kB = crypto.rotateTransportKeys(kB, 455);
|
||||
// Alice's previous incoming keys should equal Bob's outgoing keys
|
||||
assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(),
|
||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||
assertArrayEquals(
|
||||
kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
|
||||
assertArrayEquals(kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
|
||||
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
|
||||
// Compare Alice's current keys in period 456 with Bob's previous keys
|
||||
// in period 457
|
||||
kB = transportCrypto.rotateTransportKeys(kB, 457);
|
||||
kB = crypto.rotateTransportKeys(kB, 457);
|
||||
// Alice's outgoing keys should equal Bob's previous incoming keys
|
||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
||||
kB.getPreviousIncomingKeys().getTagKey().getBytes());
|
||||
@@ -99,14 +99,14 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testNextKeysMatchNextKeysOfContact() {
|
||||
// Start in rotation period 123
|
||||
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, false);
|
||||
TransportKeys kA = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
TransportKeys kB = crypto.deriveTransportKeys(transportId, master,
|
||||
123, false);
|
||||
// Compare Alice's current keys in period 456 with Bob's next keys in
|
||||
// period 455
|
||||
kA = transportCrypto.rotateTransportKeys(kA, 456);
|
||||
kB = transportCrypto.rotateTransportKeys(kB, 455);
|
||||
kA = crypto.rotateTransportKeys(kA, 456);
|
||||
kB = crypto.rotateTransportKeys(kB, 455);
|
||||
// Alice's outgoing keys should equal Bob's next incoming keys
|
||||
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
|
||||
kB.getNextIncomingKeys().getTagKey().getBytes());
|
||||
@@ -114,7 +114,7 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
kB.getNextIncomingKeys().getHeaderKey().getBytes());
|
||||
// Compare Alice's next keys in period 456 with Bob's current keys
|
||||
// in period 457
|
||||
kB = transportCrypto.rotateTransportKeys(kB, 457);
|
||||
kB = crypto.rotateTransportKeys(kB, 457);
|
||||
// Alice's next incoming keys should equal Bob's outgoing keys
|
||||
assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(),
|
||||
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
|
||||
@@ -124,12 +124,12 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testMasterKeyAffectsOutput() {
|
||||
SecretKey master1 = getSecretKey();
|
||||
SecretKey master1 = TestUtils.getSecretKey();
|
||||
assertFalse(Arrays.equals(master.getBytes(), master1.getBytes()));
|
||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
|
||||
master1, 123, true);
|
||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
TransportKeys k1 = crypto.deriveTransportKeys(transportId, master1,
|
||||
123, true);
|
||||
assertAllDifferent(k, k1);
|
||||
}
|
||||
|
||||
@@ -137,10 +137,10 @@ public class KeyDerivationTest extends BrambleTestCase {
|
||||
public void testTransportIdAffectsOutput() {
|
||||
TransportId transportId1 = new TransportId("id1");
|
||||
assertFalse(transportId.getString().equals(transportId1.getString()));
|
||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||
master, 123, true);
|
||||
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
|
||||
master, 123, true);
|
||||
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
|
||||
123, true);
|
||||
TransportKeys k1 = crypto.deriveTransportKeys(transportId1, master,
|
||||
123, true);
|
||||
assertAllDifferent(k, k1);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,49 +4,42 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class MacTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponent crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
private final SecretKey key1 = getSecretKey(), key2 = getSecretKey();
|
||||
private final String label1 = getRandomString(123);
|
||||
private final String label2 = getRandomString(123);
|
||||
private final byte[] input1 = getRandomBytes(123);
|
||||
private final byte[] input2 = getRandomBytes(234);
|
||||
private final byte[] input3 = new byte[0];
|
||||
private final SecretKey k = TestUtils.getSecretKey();
|
||||
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
||||
private final byte[] inputBytes1 = TestUtils.getRandomBytes(234);
|
||||
private final byte[] inputBytes2 = new byte[0];
|
||||
|
||||
public MacTest() {
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdenticalKeysAndInputsProduceIdenticalMacs() {
|
||||
// Calculate the MAC twice - the results should be identical
|
||||
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||
byte[] mac1 = crypto.mac(label1, key1, input1, input2, input3);
|
||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
||||
byte[] mac1 = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
||||
assertArrayEquals(mac, mac1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentLabelsProduceDifferentMacs() {
|
||||
// Calculate the MAC with each label - the results should be different
|
||||
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||
byte[] mac1 = crypto.mac(label2, key1, input1, input2, input3);
|
||||
assertFalse(Arrays.equals(mac, mac1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentKeysProduceDifferentMacs() {
|
||||
// Generate second random key
|
||||
SecretKey k1 = TestUtils.getSecretKey();
|
||||
// Calculate the MAC with each key - the results should be different
|
||||
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||
byte[] mac1 = crypto.mac(label1, key2, input1, input2, input3);
|
||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
||||
byte[] mac1 = crypto.mac(k1, inputBytes, inputBytes1, inputBytes2);
|
||||
assertFalse(Arrays.equals(mac, mac1));
|
||||
}
|
||||
|
||||
@@ -54,8 +47,8 @@ public class MacTest extends BrambleTestCase {
|
||||
public void testDifferentInputsProduceDifferentMacs() {
|
||||
// Calculate the MAC with the inputs in different orders - the results
|
||||
// should be different
|
||||
byte[] mac = crypto.mac(label1, key1, input1, input2, input3);
|
||||
byte[] mac1 = crypto.mac(label1, key1, input3, input2, input1);
|
||||
byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
|
||||
byte[] mac1 = crypto.mac(k, inputBytes2, inputBytes1, inputBytes);
|
||||
assertFalse(Arrays.equals(mac, mac1));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,32 +8,23 @@ import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public abstract class SignatureTest extends BrambleTestCase {
|
||||
public class SignatureTest extends BrambleTestCase {
|
||||
|
||||
protected final CryptoComponent crypto;
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
private final byte[] publicKey, privateKey;
|
||||
private final String label = StringUtils.getRandomString(42);
|
||||
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
||||
|
||||
protected abstract KeyPair generateKeyPair();
|
||||
|
||||
protected abstract byte[] sign(String label, byte[] toSign,
|
||||
byte[] privateKey) throws GeneralSecurityException;
|
||||
|
||||
protected abstract boolean verify(String label, byte[] signedData,
|
||||
byte[] publicKey, byte[] signature) throws GeneralSecurityException;
|
||||
|
||||
SignatureTest() {
|
||||
public SignatureTest() {
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
KeyPair k = generateKeyPair();
|
||||
KeyPair k = crypto.generateSignatureKeyPair();
|
||||
publicKey = k.getPublic().getEncoded();
|
||||
privateKey = k.getPrivate().getEncoded();
|
||||
}
|
||||
@@ -42,19 +33,19 @@ public abstract class SignatureTest extends BrambleTestCase {
|
||||
public void testIdenticalKeysAndInputsProduceIdenticalSignatures()
|
||||
throws Exception {
|
||||
// Calculate the Signature twice - the results should be identical
|
||||
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = sign(label, inputBytes, privateKey);
|
||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = crypto.sign(label, inputBytes, privateKey);
|
||||
assertArrayEquals(sig1, sig2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentKeysProduceDifferentSignatures() throws Exception {
|
||||
// Generate second private key
|
||||
KeyPair k2 = generateKeyPair();
|
||||
KeyPair k2 = crypto.generateSignatureKeyPair();
|
||||
byte[] privateKey2 = k2.getPrivate().getEncoded();
|
||||
// Calculate the signature with each key
|
||||
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = sign(label, inputBytes, privateKey2);
|
||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = crypto.sign(label, inputBytes, privateKey2);
|
||||
assertFalse(Arrays.equals(sig1, sig2));
|
||||
}
|
||||
|
||||
@@ -65,8 +56,8 @@ public abstract class SignatureTest extends BrambleTestCase {
|
||||
byte[] inputBytes2 = TestUtils.getRandomBytes(123);
|
||||
// Calculate the signature with different inputs
|
||||
// the results should be different
|
||||
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = sign(label, inputBytes2, privateKey);
|
||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = crypto.sign(label, inputBytes2, privateKey);
|
||||
assertFalse(Arrays.equals(sig1, sig2));
|
||||
}
|
||||
|
||||
@@ -77,25 +68,25 @@ public abstract class SignatureTest extends BrambleTestCase {
|
||||
String label2 = StringUtils.getRandomString(42);
|
||||
// Calculate the signature with different inputs
|
||||
// the results should be different
|
||||
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = sign(label2, inputBytes, privateKey);
|
||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = crypto.sign(label2, inputBytes, privateKey);
|
||||
assertFalse(Arrays.equals(sig1, sig2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignatureVerification() throws Exception {
|
||||
byte[] sig = sign(label, inputBytes, privateKey);
|
||||
assertTrue(verify(label, inputBytes, publicKey, sig));
|
||||
byte[] sig = crypto.sign(label, inputBytes, privateKey);
|
||||
assertTrue(crypto.verify(label, inputBytes, publicKey, sig));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentKeyFailsVerification() throws Exception {
|
||||
// Generate second private key
|
||||
KeyPair k2 = generateKeyPair();
|
||||
KeyPair k2 = crypto.generateSignatureKeyPair();
|
||||
byte[] privateKey2 = k2.getPrivate().getEncoded();
|
||||
// calculate the signature with different key, should fail to verify
|
||||
byte[] sig = sign(label, inputBytes, privateKey2);
|
||||
assertFalse(verify(label, inputBytes, publicKey, sig));
|
||||
byte[] sig = crypto.sign(label, inputBytes, privateKey2);
|
||||
assertFalse(crypto.verify(label, inputBytes, publicKey, sig));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -103,8 +94,8 @@ public abstract class SignatureTest extends BrambleTestCase {
|
||||
// Generate a second input
|
||||
byte[] inputBytes2 = TestUtils.getRandomBytes(123);
|
||||
// calculate the signature with different input, should fail to verify
|
||||
byte[] sig = sign(label, inputBytes, privateKey);
|
||||
assertFalse(verify(label, inputBytes2, publicKey, sig));
|
||||
byte[] sig = crypto.sign(label, inputBytes, privateKey);
|
||||
assertFalse(crypto.verify(label, inputBytes2, publicKey, sig));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,8 +103,8 @@ public abstract class SignatureTest extends BrambleTestCase {
|
||||
// Generate a second label
|
||||
String label2 = StringUtils.getRandomString(42);
|
||||
// calculate the signature with different label, should fail to verify
|
||||
byte[] sig = sign(label, inputBytes, privateKey);
|
||||
assertFalse(verify(label2, inputBytes, publicKey, sig));
|
||||
byte[] sig = crypto.sign(label, inputBytes, privateKey);
|
||||
assertFalse(crypto.verify(label2, inputBytes, publicKey, sig));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@ package org.briarproject.bramble.crypto;
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
@@ -13,25 +14,25 @@ import java.util.Set;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
|
||||
public class TagEncodingTest extends BrambleMockTestCase {
|
||||
public class TagEncodingTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
|
||||
private final TransportCrypto transportCrypto =
|
||||
new TransportCryptoImpl(crypto);
|
||||
private final SecretKey tagKey = getSecretKey();
|
||||
private final CryptoComponent crypto;
|
||||
private final SecretKey tagKey;
|
||||
private final long streamNumber = 1234567890;
|
||||
|
||||
public TagEncodingTest() {
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
tagKey = TestUtils.getSecretKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyAffectsTag() throws Exception {
|
||||
Set<Bytes> set = new HashSet<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
SecretKey tagKey = getSecretKey();
|
||||
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
SecretKey tagKey = TestUtils.getSecretKey();
|
||||
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber);
|
||||
assertTrue(set.add(new Bytes(tag)));
|
||||
}
|
||||
}
|
||||
@@ -41,8 +42,7 @@ public class TagEncodingTest extends BrambleMockTestCase {
|
||||
Set<Bytes> set = new HashSet<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i,
|
||||
streamNumber);
|
||||
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber);
|
||||
assertTrue(set.add(new Bytes(tag)));
|
||||
}
|
||||
}
|
||||
@@ -52,8 +52,7 @@ public class TagEncodingTest extends BrambleMockTestCase {
|
||||
Set<Bytes> set = new HashSet<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(tag, tagKey, PROTOCOL_VERSION,
|
||||
streamNumber + i);
|
||||
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i);
|
||||
assertTrue(set.add(new Bytes(tag)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,380 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.sql.Types.BINARY;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public abstract class BasicDatabaseTest extends BrambleTestCase {
|
||||
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File db = new File(testDir, "db");
|
||||
|
||||
protected abstract String getBinaryType();
|
||||
|
||||
protected abstract String getDriverName();
|
||||
|
||||
protected abstract Connection openConnection(File db, boolean encrypt)
|
||||
throws SQLException;
|
||||
|
||||
protected abstract void shutdownDatabase(File db, boolean encrypt)
|
||||
throws SQLException;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testDir.mkdirs();
|
||||
Class.forName(getDriverName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertUpdateAndDelete() throws Exception {
|
||||
Connection connection = openConnection(db, false);
|
||||
try {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Generate an ID and two names
|
||||
byte[] id = TestUtils.getRandomId();
|
||||
String oldName = StringUtils.getRandomString(50);
|
||||
String newName = StringUtils.getRandomString(50);
|
||||
// Insert the ID and old name into the table
|
||||
insertRow(connection, id, oldName);
|
||||
// Check that the old name can be retrieved using the ID
|
||||
assertTrue(rowExists(connection, id));
|
||||
assertEquals(oldName, getName(connection, id));
|
||||
// Update the name
|
||||
updateRow(connection, id, newName);
|
||||
// Check that the new name can be retrieved using the ID
|
||||
assertTrue(rowExists(connection, id));
|
||||
assertEquals(newName, getName(connection, id));
|
||||
// Delete the row from the table
|
||||
assertTrue(deleteRow(connection, id));
|
||||
// Check that the row no longer exists
|
||||
assertFalse(rowExists(connection, id));
|
||||
// Deleting the row again should have no effect
|
||||
assertFalse(deleteRow(connection, id));
|
||||
} finally {
|
||||
connection.close();
|
||||
shutdownDatabase(db, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchInsertUpdateAndDelete() throws Exception {
|
||||
Connection connection = openConnection(db, false);
|
||||
try {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Generate some IDs and two sets of names
|
||||
byte[][] ids = new byte[BATCH_SIZE][];
|
||||
String[] oldNames = new String[BATCH_SIZE];
|
||||
String[] newNames = new String[BATCH_SIZE];
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
ids[i] = TestUtils.getRandomId();
|
||||
oldNames[i] = StringUtils.getRandomString(50);
|
||||
newNames[i] = StringUtils.getRandomString(50);
|
||||
}
|
||||
// Insert the IDs and old names into the table as a batch
|
||||
insertBatch(connection, ids, oldNames);
|
||||
// Update the names as a batch
|
||||
updateBatch(connection, ids, newNames);
|
||||
// Check that the new names can be retrieved using the IDs
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
assertTrue(rowExists(connection, ids[i]));
|
||||
assertEquals(newNames[i], getName(connection, ids[i]));
|
||||
}
|
||||
// Delete the rows as a batch
|
||||
boolean[] deleted = deleteBatch(connection, ids);
|
||||
// Check that the rows no longer exist
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
assertTrue(deleted[i]);
|
||||
assertFalse(rowExists(connection, ids[i]));
|
||||
}
|
||||
// Deleting the rows again should have no effect
|
||||
deleted = deleteBatch(connection, ids);
|
||||
for (int i = 0; i < BATCH_SIZE; i++) assertFalse(deleted[i]);
|
||||
} finally {
|
||||
connection.close();
|
||||
shutdownDatabase(db, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortOrder() throws Exception {
|
||||
byte[] first = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
byte[] second = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 127
|
||||
};
|
||||
byte[] third = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, (byte) 255
|
||||
};
|
||||
Connection connection = openConnection(db, false);
|
||||
try {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Insert the rows
|
||||
insertRow(connection, first, "first");
|
||||
insertRow(connection, second, "second");
|
||||
insertRow(connection, third, "third");
|
||||
insertRow(connection, null, "null");
|
||||
// Check the ordering of the < operator: null is not comparable
|
||||
assertNull(getPredecessor(connection, first));
|
||||
assertEquals("first", getPredecessor(connection, second));
|
||||
assertEquals("second", getPredecessor(connection, third));
|
||||
assertNull(getPredecessor(connection, null));
|
||||
// Check the ordering of ORDER BY: nulls come first
|
||||
List<String> names = getNames(connection);
|
||||
assertEquals(4, names.size());
|
||||
assertEquals("null", names.get(0));
|
||||
assertEquals("first", names.get(1));
|
||||
assertEquals("second", names.get(2));
|
||||
assertEquals("third", names.get(3));
|
||||
} finally {
|
||||
connection.close();
|
||||
shutdownDatabase(db, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataIsFoundWithoutEncryption() throws Exception {
|
||||
testEncryption(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataIsNotFoundWithEncryption() throws Exception {
|
||||
testEncryption(true);
|
||||
}
|
||||
|
||||
private void testEncryption(boolean encrypt) throws Exception {
|
||||
byte[] sequence = new byte[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
|
||||
Connection connection = openConnection(db, encrypt);
|
||||
try {
|
||||
createTable(connection);
|
||||
insertRow(connection, sequence, "abcdefg");
|
||||
} finally {
|
||||
connection.close();
|
||||
shutdownDatabase(db, encrypt);
|
||||
}
|
||||
try {
|
||||
if (findSequence(testDir, sequence) == encrypt) fail();
|
||||
} finally {
|
||||
shutdownDatabase(db, encrypt);
|
||||
}
|
||||
}
|
||||
|
||||
private void createTable(Connection connection) throws SQLException {
|
||||
Statement s = connection.createStatement();
|
||||
String sql = "CREATE TABLE foo (uniqueId " + getBinaryType() + ","
|
||||
+ " name VARCHAR(100) NOT NULL)";
|
||||
s.executeUpdate(sql);
|
||||
s.close();
|
||||
}
|
||||
|
||||
private void insertRow(Connection connection, byte[] id, String name)
|
||||
throws SQLException {
|
||||
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
ps.setString(2, name);
|
||||
int affected = ps.executeUpdate();
|
||||
assertEquals(1, affected);
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private boolean rowExists(Connection connection, byte[] id)
|
||||
throws SQLException {
|
||||
assertNotNull(id);
|
||||
String sql = "SELECT * FROM foo WHERE uniqueID = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
boolean found = rs.next();
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return found;
|
||||
}
|
||||
|
||||
private String getName(Connection connection, byte[] id)
|
||||
throws SQLException {
|
||||
assertNotNull(id);
|
||||
String sql = "SELECT name FROM foo WHERE uniqueID = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
assertTrue(rs.next());
|
||||
String name = rs.getString(1);
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return name;
|
||||
}
|
||||
|
||||
private void updateRow(Connection connection, byte[] id, String name)
|
||||
throws SQLException {
|
||||
String sql = "UPDATE foo SET name = ? WHERE uniqueId = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(2, BINARY);
|
||||
else ps.setBytes(2, id);
|
||||
ps.setString(1, name);
|
||||
assertEquals(1, ps.executeUpdate());
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private boolean deleteRow(Connection connection, byte[] id)
|
||||
throws SQLException {
|
||||
String sql = "DELETE FROM foo WHERE uniqueId = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
int affected = ps.executeUpdate();
|
||||
ps.close();
|
||||
return affected == 1;
|
||||
}
|
||||
|
||||
private void insertBatch(Connection connection, byte[][] ids,
|
||||
String[] names) throws SQLException {
|
||||
assertEquals(ids.length, names.length);
|
||||
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
if (ids[i] == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, ids[i]);
|
||||
ps.setString(2, names[i]);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
for (int affected : batchAffected) assertEquals(1, affected);
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private void updateBatch(Connection connection, byte[][] ids,
|
||||
String[] names) throws SQLException {
|
||||
assertEquals(ids.length, names.length);
|
||||
String sql = "UPDATE foo SET name = ? WHERE uniqueId = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
if (ids[i] == null) ps.setNull(2, BINARY);
|
||||
else ps.setBytes(2, ids[i]);
|
||||
ps.setString(1, names[i]);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
for (int affected : batchAffected) assertEquals(1, affected);
|
||||
ps.close();
|
||||
}
|
||||
|
||||
private boolean[] deleteBatch(Connection connection, byte[][] ids)
|
||||
throws SQLException {
|
||||
String sql = "DELETE FROM foo WHERE uniqueId = ?";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (byte[] id : ids) {
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
boolean[] ret = new boolean[ids.length];
|
||||
for (int i = 0; i < batchAffected.length; i++)
|
||||
ret[i] = batchAffected[i] == 1;
|
||||
ps.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
private String getPredecessor(Connection connection, byte[] id)
|
||||
throws SQLException {
|
||||
String sql = "SELECT name FROM foo WHERE uniqueId < ?"
|
||||
+ " ORDER BY uniqueId DESC";
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ps.setMaxRows(1);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
String name = rs.next() ? rs.getString(1) : null;
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return name;
|
||||
}
|
||||
|
||||
private List<String> getNames(Connection connection) throws SQLException {
|
||||
String sql = "SELECT name FROM foo ORDER BY uniqueId NULLS FIRST";
|
||||
List<String> names = new ArrayList<>();
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) names.add(rs.getString(1));
|
||||
rs.close();
|
||||
ps.close();
|
||||
return names;
|
||||
}
|
||||
|
||||
private boolean findSequence(File f, byte[] sequence) throws IOException {
|
||||
if (f.isDirectory()) {
|
||||
File[] children = f.listFiles();
|
||||
if (children != null)
|
||||
for (File child : children)
|
||||
if (findSequence(child, sequence)) return true;
|
||||
return false;
|
||||
} else if (f.isFile()) {
|
||||
FileInputStream in = new FileInputStream(f);
|
||||
try {
|
||||
int offset = 0;
|
||||
while (true) {
|
||||
int read = in.read();
|
||||
if (read == -1) return false;
|
||||
if (((byte) read) == sequence[offset]) {
|
||||
offset++;
|
||||
if (offset == sequence.length) return true;
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,345 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BasicH2Test extends BasicDatabaseTest {
|
||||
import static java.sql.Types.BINARY;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
private final SecretKey key = TestUtils.getSecretKey();
|
||||
public class BasicH2Test extends BrambleTestCase {
|
||||
|
||||
@Override
|
||||
protected String getBinaryType() {
|
||||
return "BINARY(32)";
|
||||
private static final String CREATE_TABLE =
|
||||
"CREATE TABLE foo (uniqueId BINARY(32), name VARCHAR NOT NULL)";
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File db = new File(testDir, "db");
|
||||
private final String url = "jdbc:h2:" + db.getAbsolutePath();
|
||||
|
||||
private Connection connection = null;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testDir.mkdirs();
|
||||
Class.forName("org.h2.Driver");
|
||||
connection = DriverManager.getConnection(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDriverName() {
|
||||
return "org.h2.Driver";
|
||||
@Test
|
||||
public void testInsertUpdateAndDelete() throws Exception {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Generate an ID and two names
|
||||
byte[] id = TestUtils.getRandomId();
|
||||
String oldName = StringUtils.getRandomString(50);
|
||||
String newName = StringUtils.getRandomString(50);
|
||||
// Insert the ID and old name into the table
|
||||
insertRow(id, oldName);
|
||||
// Check that the old name can be retrieved using the ID
|
||||
assertTrue(rowExists(id));
|
||||
assertEquals(oldName, getName(id));
|
||||
// Update the name
|
||||
updateRow(id, newName);
|
||||
// Check that the new name can be retrieved using the ID
|
||||
assertTrue(rowExists(id));
|
||||
assertEquals(newName, getName(id));
|
||||
// Delete the row from the table
|
||||
assertTrue(deleteRow(id));
|
||||
// Check that the row no longer exists
|
||||
assertFalse(rowExists(id));
|
||||
// Deleting the row again should have no effect
|
||||
assertFalse(deleteRow(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection openConnection(File db, boolean encrypt)
|
||||
throws SQLException {
|
||||
String url = "jdbc:h2:split:" + db.getAbsolutePath();
|
||||
Properties props = new Properties();
|
||||
props.setProperty("user", "user");
|
||||
if (encrypt) {
|
||||
url += ";CIPHER=AES";
|
||||
String hex = StringUtils.toHexString(key.getBytes());
|
||||
props.setProperty("password", hex + " password");
|
||||
@Test
|
||||
public void testBatchInsertUpdateAndDelete() throws Exception {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Generate some IDs and two sets of names
|
||||
byte[][] ids = new byte[BATCH_SIZE][];
|
||||
String[] oldNames = new String[BATCH_SIZE];
|
||||
String[] newNames = new String[BATCH_SIZE];
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
ids[i] = TestUtils.getRandomId();
|
||||
oldNames[i] = StringUtils.getRandomString(50);
|
||||
newNames[i] = StringUtils.getRandomString(50);
|
||||
}
|
||||
return DriverManager.getConnection(url, props);
|
||||
// Insert the IDs and old names into the table as a batch
|
||||
insertBatch(ids, oldNames);
|
||||
// Update the names as a batch
|
||||
updateBatch(ids, newNames);
|
||||
// Check that the new names can be retrieved using the IDs
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
assertTrue(rowExists(ids[i]));
|
||||
assertEquals(newNames[i], getName(ids[i]));
|
||||
}
|
||||
// Delete the rows as a batch
|
||||
boolean[] deleted = deleteBatch(ids);
|
||||
// Check that the rows no longer exist
|
||||
for (int i = 0; i < BATCH_SIZE; i++) {
|
||||
assertTrue(deleted[i]);
|
||||
assertFalse(rowExists(ids[i]));
|
||||
}
|
||||
// Deleting the rows again should have no effect
|
||||
deleted = deleteBatch(ids);
|
||||
for (int i = 0; i < BATCH_SIZE; i++) assertFalse(deleted[i]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdownDatabase(File db, boolean encrypt)
|
||||
throws SQLException {
|
||||
// The DB is closed automatically when the connection is closed
|
||||
@Test
|
||||
public void testSortOrder() throws Exception {
|
||||
byte[] first = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
byte[] second = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 127
|
||||
};
|
||||
byte[] third = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, (byte) 255
|
||||
};
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Insert the rows
|
||||
insertRow(first, "first");
|
||||
insertRow(second, "second");
|
||||
insertRow(third, "third");
|
||||
insertRow(null, "null");
|
||||
// Check the ordering of the < operator: the null ID is not comparable
|
||||
assertNull(getPredecessor(first));
|
||||
assertEquals("first", getPredecessor(second));
|
||||
assertEquals("second", getPredecessor(third));
|
||||
assertNull(getPredecessor(null));
|
||||
// Check the ordering of ORDER BY: nulls come first
|
||||
List<String> names = getNames();
|
||||
assertEquals(4, names.size());
|
||||
assertEquals("null", names.get(0));
|
||||
assertEquals("first", names.get(1));
|
||||
assertEquals("second", names.get(2));
|
||||
assertEquals("third", names.get(3));
|
||||
}
|
||||
|
||||
private void createTable(Connection connection) throws SQLException {
|
||||
try {
|
||||
Statement s = connection.createStatement();
|
||||
s.executeUpdate(CREATE_TABLE);
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void insertRow(byte[] id, String name) throws SQLException {
|
||||
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
ps.setString(2, name);
|
||||
int affected = ps.executeUpdate();
|
||||
assertEquals(1, affected);
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean rowExists(byte[] id) throws SQLException {
|
||||
assertNotNull(id);
|
||||
String sql = "SELECT NULL FROM foo WHERE uniqueID = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
boolean found = rs.next();
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return found;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private String getName(byte[] id) throws SQLException {
|
||||
assertNotNull(id);
|
||||
String sql = "SELECT name FROM foo WHERE uniqueID = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
assertTrue(rs.next());
|
||||
String name = rs.getString(1);
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return name;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRow(byte[] id, String name) throws SQLException {
|
||||
String sql = "UPDATE foo SET name = ? WHERE uniqueId = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(2, BINARY);
|
||||
else ps.setBytes(2, id);
|
||||
ps.setString(1, name);
|
||||
assertEquals(1, ps.executeUpdate());
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deleteRow(byte[] id) throws SQLException {
|
||||
String sql = "DELETE FROM foo WHERE uniqueId = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
int affected = ps.executeUpdate();
|
||||
ps.close();
|
||||
return affected == 1;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void insertBatch(byte[][] ids, String[] names) throws SQLException {
|
||||
assertEquals(ids.length, names.length);
|
||||
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
if (ids[i] == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, ids[i]);
|
||||
ps.setString(2, names[i]);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
for (int affected : batchAffected) assertEquals(1, affected);
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBatch(byte[][] ids, String[] names) throws SQLException {
|
||||
assertEquals(ids.length, names.length);
|
||||
String sql = "UPDATE foo SET name = ? WHERE uniqueId = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
if (ids[i] == null) ps.setNull(2, BINARY);
|
||||
else ps.setBytes(2, ids[i]);
|
||||
ps.setString(1, names[i]);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
for (int affected : batchAffected) assertEquals(1, affected);
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean[] deleteBatch(byte[][] ids) throws SQLException {
|
||||
String sql = "DELETE FROM foo WHERE uniqueId = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
for (byte[] id : ids) {
|
||||
if (id == null) ps.setNull(1, BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
assertEquals(ids.length, batchAffected.length);
|
||||
boolean[] ret = new boolean[ids.length];
|
||||
for (int i = 0; i < batchAffected.length; i++)
|
||||
ret[i] = batchAffected[i] == 1;
|
||||
ps.close();
|
||||
return ret;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private String getPredecessor(byte[] id) throws SQLException {
|
||||
String sql = "SELECT name FROM foo WHERE uniqueId < ?"
|
||||
+ " ORDER BY uniqueId DESC LIMIT ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ps.setInt(2, 1);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
String name = rs.next() ? rs.getString(1) : null;
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return name;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getNames() throws SQLException {
|
||||
String sql = "SELECT name FROM foo ORDER BY uniqueId";
|
||||
List<String> names = new ArrayList<>();
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) names.add(rs.getString(1));
|
||||
rs.close();
|
||||
ps.close();
|
||||
return names;
|
||||
} catch (SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
if (connection != null) connection.close();
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class BasicHyperSqlTest extends BasicDatabaseTest {
|
||||
|
||||
private final SecretKey key = TestUtils.getSecretKey();
|
||||
|
||||
@Override
|
||||
protected String getBinaryType() {
|
||||
return "BINARY(32)";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDriverName() {
|
||||
return "org.hsqldb.jdbc.JDBCDriver";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection openConnection(File db, boolean encrypt)
|
||||
throws SQLException {
|
||||
String url = "jdbc:hsqldb:file:" + db.getAbsolutePath() +
|
||||
";sql.enforce_size=false;allow_empty_batch=true";
|
||||
if (encrypt) {
|
||||
String hex = StringUtils.toHexString(key.getBytes());
|
||||
url += ";encrypt_lobs=true;crypt_type=AES;crypt_key=" + hex;
|
||||
}
|
||||
return DriverManager.getConnection(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdownDatabase(File db, boolean encrypt)
|
||||
throws SQLException {
|
||||
Connection c = openConnection(db, encrypt);
|
||||
Statement s = c.createStatement();
|
||||
s.executeQuery("SHUTDOWN");
|
||||
s.close();
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
public class HyperSqlDatabaseTest extends JdbcDatabaseTest {
|
||||
|
||||
public HyperSqlDatabaseTest() throws Exception {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdbcDatabase createDatabase(DatabaseConfig config, Clock clock) {
|
||||
return new HyperSqlDatabase(config, clock);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
public class TransactionIsolationTest extends BrambleTestCase {
|
||||
|
||||
private static final String DROP_TABLE = "DROP TABLE foo IF EXISTS";
|
||||
private static final String CREATE_TABLE = "CREATE TABLE foo"
|
||||
@@ -34,9 +34,9 @@ public class H2TransactionIsolationTest extends BrambleTestCase {
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File db = new File(testDir, "db");
|
||||
private final String withMvcc = "jdbc:h2:split:" + db.getAbsolutePath()
|
||||
private final String withMvcc = "jdbc:h2:" + db.getAbsolutePath()
|
||||
+ ";MV_STORE=TRUE;MVCC=TRUE";
|
||||
private final String withoutMvcc = "jdbc:h2:split:" + db.getAbsolutePath()
|
||||
private final String withoutMvcc = "jdbc:h2:" + db.getAbsolutePath()
|
||||
+ ";MV_STORE=FALSE;MVCC=FALSE;LOCK_MODE=1";
|
||||
|
||||
@Before
|
||||
@@ -1,14 +1,13 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.auto.Mock;
|
||||
import org.jmock.integration.junit4.JUnitRuleMockery;
|
||||
@@ -17,11 +16,6 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
@@ -34,33 +28,34 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
setImposteriser(ClassImposteriser.INSTANCE);
|
||||
}};
|
||||
|
||||
private final PublicKey alicePubKey =
|
||||
context.mock(PublicKey.class, "alice");
|
||||
private final byte[] alicePubKeyBytes = getRandomBytes(32);
|
||||
private final byte[] aliceCommit = getRandomBytes(COMMIT_LENGTH);
|
||||
private final byte[] alicePayload = getRandomBytes(COMMIT_LENGTH + 8);
|
||||
private final byte[] aliceConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||
private static final byte[] ALICE_PUBKEY = TestUtils.getRandomBytes(32);
|
||||
private static final byte[] ALICE_COMMIT =
|
||||
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
||||
private static final byte[] ALICE_PAYLOAD =
|
||||
TestUtils.getRandomBytes(COMMIT_LENGTH + 8);
|
||||
|
||||
private final PublicKey bobPubKey = context.mock(PublicKey.class, "bob");
|
||||
private final byte[] bobPubKeyBytes = getRandomBytes(32);
|
||||
private final byte[] bobCommit = getRandomBytes(COMMIT_LENGTH);
|
||||
private final byte[] bobPayload = getRandomBytes(COMMIT_LENGTH + 19);
|
||||
private final byte[] bobConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||
private static final byte[] BOB_PUBKEY = TestUtils.getRandomBytes(32);
|
||||
private static final byte[] BOB_COMMIT =
|
||||
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
||||
private static final byte[] BOB_PAYLOAD =
|
||||
TestUtils.getRandomBytes(COMMIT_LENGTH + 19);
|
||||
|
||||
private final PublicKey badPubKey = context.mock(PublicKey.class, "bad");
|
||||
private final byte[] badPubKeyBytes = getRandomBytes(32);
|
||||
private final byte[] badCommit = getRandomBytes(COMMIT_LENGTH);
|
||||
private final byte[] badConfirm = getRandomBytes(SecretKey.LENGTH);
|
||||
private static final byte[] ALICE_CONFIRM =
|
||||
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
||||
private static final byte[] BOB_CONFIRM =
|
||||
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
||||
|
||||
private static final byte[] BAD_PUBKEY = TestUtils.getRandomBytes(32);
|
||||
private static final byte[] BAD_COMMIT =
|
||||
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
||||
private static final byte[] BAD_CONFIRM =
|
||||
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
||||
|
||||
@Mock
|
||||
KeyAgreementProtocol.Callbacks callbacks;
|
||||
@Mock
|
||||
CryptoComponent crypto;
|
||||
@Mock
|
||||
KeyAgreementCrypto keyAgreementCrypto;
|
||||
@Mock
|
||||
KeyParser keyParser;
|
||||
@Mock
|
||||
PayloadEncoder payloadEncoder;
|
||||
@Mock
|
||||
KeyAgreementTransport transport;
|
||||
@@ -70,72 +65,60 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testAliceProtocol() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(bobCommit, null);
|
||||
Payload ourPayload = new Payload(aliceCommit, null);
|
||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
SecretKey sharedSecret = getSecretKey();
|
||||
SecretKey masterSecret = getSecretKey();
|
||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
||||
SecretKey masterSecret = TestUtils.getSecretKey();
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, true);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(payloadEncoder).encode(ourPayload);
|
||||
will(returnValue(alicePayload));
|
||||
will(returnValue(ALICE_PAYLOAD));
|
||||
allowing(payloadEncoder).encode(theirPayload);
|
||||
will(returnValue(bobPayload));
|
||||
will(returnValue(BOB_PAYLOAD));
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
allowing(alicePubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
allowing(bobPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
will(returnValue(ALICE_PUBKEY));
|
||||
|
||||
// Alice sends her public key
|
||||
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
||||
|
||||
// Alice receives Bob's public key
|
||||
oneOf(callbacks).connectionWaiting();
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
will(returnValue(BOB_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
|
||||
will(returnValue(bobPubKey));
|
||||
|
||||
// Alice verifies Bob's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(bobPubKey);
|
||||
will(returnValue(bobCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
|
||||
will(returnValue(BOB_COMMIT));
|
||||
|
||||
// Alice computes shared secret
|
||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||
alicePubKeyBytes, bobPubKeyBytes);
|
||||
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
|
||||
will(returnValue(sharedSecret));
|
||||
|
||||
// Alice sends her confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||
true, true);
|
||||
will(returnValue(aliceConfirm));
|
||||
oneOf(transport).sendConfirm(aliceConfirm);
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
|
||||
will(returnValue(ALICE_CONFIRM));
|
||||
oneOf(transport).sendConfirm(ALICE_CONFIRM);
|
||||
|
||||
// Alice receives Bob's confirmation record
|
||||
oneOf(transport).receiveConfirm();
|
||||
will(returnValue(bobConfirm));
|
||||
will(returnValue(BOB_CONFIRM));
|
||||
|
||||
// Alice verifies Bob's confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||
true, false);
|
||||
will(returnValue(bobConfirm));
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
|
||||
will(returnValue(BOB_CONFIRM));
|
||||
|
||||
// Alice computes master secret
|
||||
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||
oneOf(crypto).deriveMasterSecret(sharedSecret);
|
||||
will(returnValue(masterSecret));
|
||||
}});
|
||||
|
||||
@@ -146,71 +129,59 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testBobProtocol() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(aliceCommit, null);
|
||||
Payload ourPayload = new Payload(bobCommit, null);
|
||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
SecretKey sharedSecret = getSecretKey();
|
||||
SecretKey masterSecret = getSecretKey();
|
||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
||||
SecretKey masterSecret = TestUtils.getSecretKey();
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, false);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(payloadEncoder).encode(ourPayload);
|
||||
will(returnValue(bobPayload));
|
||||
will(returnValue(BOB_PAYLOAD));
|
||||
allowing(payloadEncoder).encode(theirPayload);
|
||||
will(returnValue(alicePayload));
|
||||
will(returnValue(ALICE_PAYLOAD));
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
allowing(alicePubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
allowing(bobPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
will(returnValue(BOB_PUBKEY));
|
||||
|
||||
// Bob receives Alice's public key
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
will(returnValue(ALICE_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
|
||||
will(returnValue(alicePubKey));
|
||||
|
||||
// Bob verifies Alice's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(alicePubKey);
|
||||
will(returnValue(aliceCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
|
||||
will(returnValue(ALICE_COMMIT));
|
||||
|
||||
// Bob sends his public key
|
||||
oneOf(transport).sendKey(bobPubKeyBytes);
|
||||
oneOf(transport).sendKey(BOB_PUBKEY);
|
||||
|
||||
// Bob computes shared secret
|
||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||
alicePubKeyBytes, bobPubKeyBytes);
|
||||
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
|
||||
will(returnValue(sharedSecret));
|
||||
|
||||
// Bob receives Alices's confirmation record
|
||||
oneOf(transport).receiveConfirm();
|
||||
will(returnValue(aliceConfirm));
|
||||
will(returnValue(ALICE_CONFIRM));
|
||||
|
||||
// Bob verifies Alice's confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||
false, true);
|
||||
will(returnValue(aliceConfirm));
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
|
||||
will(returnValue(ALICE_CONFIRM));
|
||||
|
||||
// Bob sends his confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||
false, false);
|
||||
will(returnValue(bobConfirm));
|
||||
oneOf(transport).sendConfirm(bobConfirm);
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
|
||||
will(returnValue(BOB_CONFIRM));
|
||||
oneOf(transport).sendConfirm(BOB_CONFIRM);
|
||||
|
||||
// Bob computes master secret
|
||||
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||
oneOf(crypto).deriveMasterSecret(sharedSecret);
|
||||
will(returnValue(masterSecret));
|
||||
}});
|
||||
|
||||
@@ -221,44 +192,38 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test(expected = AbortException.class)
|
||||
public void testAliceProtocolAbortOnBadKey() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(bobCommit, null);
|
||||
Payload ourPayload = new Payload(aliceCommit, null);
|
||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, true);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
will(returnValue(ALICE_PUBKEY));
|
||||
|
||||
// Alice sends her public key
|
||||
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
||||
|
||||
// Alice receives a bad public key
|
||||
oneOf(callbacks).connectionWaiting();
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(badPubKeyBytes));
|
||||
will(returnValue(BAD_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
|
||||
will(returnValue(badPubKey));
|
||||
|
||||
// Alice verifies Bob's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(badPubKey);
|
||||
will(returnValue(badCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
|
||||
will(returnValue(BAD_COMMIT));
|
||||
|
||||
// Alice aborts
|
||||
oneOf(transport).sendAbort(false);
|
||||
|
||||
// Alice never computes shared secret
|
||||
never(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, badPubKey,
|
||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||
alicePubKeyBytes, bobPubKeyBytes);
|
||||
never(crypto).deriveSharedSecret(BAD_PUBKEY, ourKeyPair, true);
|
||||
}});
|
||||
|
||||
// execute
|
||||
@@ -268,38 +233,34 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test(expected = AbortException.class)
|
||||
public void testBobProtocolAbortOnBadKey() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(aliceCommit, null);
|
||||
Payload ourPayload = new Payload(bobCommit, null);
|
||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, false);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
will(returnValue(BOB_PUBKEY));
|
||||
|
||||
// Bob receives a bad public key
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(badPubKeyBytes));
|
||||
will(returnValue(BAD_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(badPubKeyBytes);
|
||||
will(returnValue(badPubKey));
|
||||
|
||||
// Bob verifies Alice's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(badPubKey);
|
||||
will(returnValue(badCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
|
||||
will(returnValue(BAD_COMMIT));
|
||||
|
||||
// Bob aborts
|
||||
oneOf(transport).sendAbort(false);
|
||||
|
||||
// Bob never sends his public key
|
||||
never(transport).sendKey(bobPubKeyBytes);
|
||||
never(transport).sendKey(BOB_PUBKEY);
|
||||
}});
|
||||
|
||||
// execute
|
||||
@@ -309,72 +270,62 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test(expected = AbortException.class)
|
||||
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(bobCommit, null);
|
||||
Payload ourPayload = new Payload(aliceCommit, null);
|
||||
Payload theirPayload = new Payload(BOB_COMMIT, null);
|
||||
Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
SecretKey sharedSecret = getSecretKey();
|
||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, true);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, true);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(payloadEncoder).encode(ourPayload);
|
||||
will(returnValue(alicePayload));
|
||||
will(returnValue(ALICE_PAYLOAD));
|
||||
allowing(payloadEncoder).encode(theirPayload);
|
||||
will(returnValue(bobPayload));
|
||||
will(returnValue(BOB_PAYLOAD));
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
allowing(bobPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
will(returnValue(ALICE_PUBKEY));
|
||||
|
||||
// Alice sends her public key
|
||||
oneOf(transport).sendKey(alicePubKeyBytes);
|
||||
oneOf(transport).sendKey(ALICE_PUBKEY);
|
||||
|
||||
// Alice receives Bob's public key
|
||||
oneOf(callbacks).connectionWaiting();
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
will(returnValue(BOB_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(bobPubKeyBytes);
|
||||
will(returnValue(bobPubKey));
|
||||
|
||||
// Alice verifies Bob's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(bobPubKey);
|
||||
will(returnValue(bobCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
|
||||
will(returnValue(BOB_COMMIT));
|
||||
|
||||
// Alice computes shared secret
|
||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, bobPubKey,
|
||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||
alicePubKeyBytes, bobPubKeyBytes);
|
||||
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
|
||||
will(returnValue(sharedSecret));
|
||||
|
||||
// Alice sends her confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||
true, true);
|
||||
will(returnValue(aliceConfirm));
|
||||
oneOf(transport).sendConfirm(aliceConfirm);
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
|
||||
will(returnValue(ALICE_CONFIRM));
|
||||
oneOf(transport).sendConfirm(ALICE_CONFIRM);
|
||||
|
||||
// Alice receives a bad confirmation record
|
||||
oneOf(transport).receiveConfirm();
|
||||
will(returnValue(badConfirm));
|
||||
will(returnValue(BAD_CONFIRM));
|
||||
|
||||
// Alice verifies Bob's confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
bobPayload, alicePayload, bobPubKey, ourKeyPair,
|
||||
true, false);
|
||||
will(returnValue(bobConfirm));
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
|
||||
will(returnValue(BOB_CONFIRM));
|
||||
|
||||
// Alice aborts
|
||||
oneOf(transport).sendAbort(false);
|
||||
|
||||
// Alice never computes master secret
|
||||
never(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
||||
never(crypto).deriveMasterSecret(sharedSecret);
|
||||
}});
|
||||
|
||||
// execute
|
||||
@@ -384,66 +335,56 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
||||
@Test(expected = AbortException.class)
|
||||
public void testBobProtocolAbortOnBadConfirm() throws Exception {
|
||||
// set up
|
||||
Payload theirPayload = new Payload(aliceCommit, null);
|
||||
Payload ourPayload = new Payload(bobCommit, null);
|
||||
Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
||||
Payload ourPayload = new Payload(BOB_COMMIT, null);
|
||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||
SecretKey sharedSecret = getSecretKey();
|
||||
SecretKey sharedSecret = TestUtils.getSecretKey();
|
||||
|
||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||
theirPayload, ourPayload, ourKeyPair, false);
|
||||
KeyAgreementProtocol protocol =
|
||||
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||
transport, theirPayload, ourPayload, ourKeyPair, false);
|
||||
|
||||
// expectations
|
||||
context.checking(new Expectations() {{
|
||||
// Helpers
|
||||
allowing(payloadEncoder).encode(ourPayload);
|
||||
will(returnValue(bobPayload));
|
||||
will(returnValue(BOB_PAYLOAD));
|
||||
allowing(payloadEncoder).encode(theirPayload);
|
||||
will(returnValue(alicePayload));
|
||||
will(returnValue(ALICE_PAYLOAD));
|
||||
allowing(ourPubKey).getEncoded();
|
||||
will(returnValue(bobPubKeyBytes));
|
||||
allowing(crypto).getAgreementKeyParser();
|
||||
will(returnValue(keyParser));
|
||||
allowing(alicePubKey).getEncoded();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
will(returnValue(BOB_PUBKEY));
|
||||
|
||||
// Bob receives Alice's public key
|
||||
oneOf(transport).receiveKey();
|
||||
will(returnValue(alicePubKeyBytes));
|
||||
will(returnValue(ALICE_PUBKEY));
|
||||
oneOf(callbacks).initialRecordReceived();
|
||||
oneOf(keyParser).parsePublicKey(alicePubKeyBytes);
|
||||
will(returnValue(alicePubKey));
|
||||
|
||||
// Bob verifies Alice's public key
|
||||
oneOf(keyAgreementCrypto).deriveKeyCommitment(alicePubKey);
|
||||
will(returnValue(aliceCommit));
|
||||
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
|
||||
will(returnValue(ALICE_COMMIT));
|
||||
|
||||
// Bob sends his public key
|
||||
oneOf(transport).sendKey(bobPubKeyBytes);
|
||||
oneOf(transport).sendKey(BOB_PUBKEY);
|
||||
|
||||
// Bob computes shared secret
|
||||
oneOf(crypto).deriveSharedSecret(SHARED_SECRET_LABEL, alicePubKey,
|
||||
ourKeyPair, new byte[] {PROTOCOL_VERSION},
|
||||
alicePubKeyBytes, bobPubKeyBytes);
|
||||
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
|
||||
will(returnValue(sharedSecret));
|
||||
|
||||
// Bob receives a bad confirmation record
|
||||
oneOf(transport).receiveConfirm();
|
||||
will(returnValue(badConfirm));
|
||||
will(returnValue(BAD_CONFIRM));
|
||||
|
||||
// Bob verifies Alice's confirmation record
|
||||
oneOf(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||
false, true);
|
||||
will(returnValue(aliceConfirm));
|
||||
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
|
||||
will(returnValue(ALICE_CONFIRM));
|
||||
|
||||
// Bob aborts
|
||||
oneOf(transport).sendAbort(false);
|
||||
|
||||
// Bob never sends his confirmation record
|
||||
never(keyAgreementCrypto).deriveConfirmationRecord(sharedSecret,
|
||||
alicePayload, bobPayload, alicePubKey, ourKeyPair,
|
||||
false, false);
|
||||
never(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
|
||||
}});
|
||||
|
||||
// execute
|
||||
|
||||
@@ -150,14 +150,17 @@ public class LanTcpPluginTest extends BrambleTestCase {
|
||||
int port = ss.getLocalPort();
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicBoolean error = new AtomicBoolean(false);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
ss.accept();
|
||||
latch.countDown();
|
||||
} catch (IOException e) {
|
||||
error.set(true);
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ss.accept();
|
||||
latch.countDown();
|
||||
} catch (IOException e) {
|
||||
error.set(true);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}.start();
|
||||
// Tell the plugin about the port
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put("ipPorts", addrString + ":" + port);
|
||||
@@ -240,14 +243,17 @@ public class LanTcpPluginTest extends BrambleTestCase {
|
||||
ss.bind(new InetSocketAddress(addrString, 0), 10);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicBoolean error = new AtomicBoolean(false);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
ss.accept();
|
||||
latch.countDown();
|
||||
} catch (IOException e) {
|
||||
error.set(true);
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ss.accept();
|
||||
latch.countDown();
|
||||
} catch (IOException e) {
|
||||
error.set(true);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}.start();
|
||||
// Tell the plugin about the port
|
||||
BdfList descriptor = new BdfList();
|
||||
descriptor.add(TRANSPORT_ID_LAN);
|
||||
|
||||
@@ -34,7 +34,6 @@ import java.util.Map;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID;
|
||||
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_VERSION;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
@@ -79,8 +78,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private TransportPropertyManagerImpl createInstance() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID,
|
||||
CLIENT_VERSION);
|
||||
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID);
|
||||
will(returnValue(localGroup));
|
||||
}});
|
||||
return new TransportPropertyManagerImpl(db, clientHelper,
|
||||
@@ -100,14 +98,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(contacts));
|
||||
// The first contact's group has already been set up
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact1);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact1);
|
||||
will(returnValue(contactGroup1));
|
||||
oneOf(db).containsGroup(txn, contactGroup1.getId());
|
||||
will(returnValue(true));
|
||||
// The second contact's group hasn't been set up
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact2);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact2);
|
||||
will(returnValue(contactGroup2));
|
||||
oneOf(db).containsGroup(txn, contactGroup2.getId());
|
||||
will(returnValue(false));
|
||||
@@ -134,8 +130,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// Create the group and share it with the contact
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(db).containsGroup(txn, contactGroup.getId());
|
||||
will(returnValue(false));
|
||||
@@ -161,8 +156,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
Group contactGroup = getGroup();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(db).removeGroup(txn, contactGroup);
|
||||
}});
|
||||
@@ -221,7 +215,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
long timestamp = 123456789;
|
||||
Message message = getMessage(contactGroupId, timestamp);
|
||||
Metadata meta = new Metadata();
|
||||
// Version 4 is being delivered
|
||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||
new BdfEntry("transportId", "foo"),
|
||||
new BdfEntry("version", 4),
|
||||
@@ -229,7 +222,19 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
);
|
||||
Map<MessageId, BdfDictionary> messageMetadata =
|
||||
new LinkedHashMap<>();
|
||||
// An older remote update for the same transport should be deleted
|
||||
// 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)
|
||||
));
|
||||
MessageId fooVersion3 = new MessageId(getRandomId());
|
||||
messageMetadata.put(fooVersion3, BdfDictionary.of(
|
||||
new BdfEntry("transportId", "foo"),
|
||||
@@ -243,7 +248,11 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
// The previous update (version 3) should be deleted
|
||||
// Versions 1-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).deleteMessageMetadata(txn, fooVersion3);
|
||||
}});
|
||||
@@ -259,7 +268,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
long timestamp = 123456789;
|
||||
Message message = getMessage(contactGroupId, timestamp);
|
||||
Metadata meta = new Metadata();
|
||||
// Version 3 is being delivered
|
||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||
new BdfEntry("transportId", "foo"),
|
||||
new BdfEntry("version", 3),
|
||||
@@ -267,6 +275,19 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
);
|
||||
Map<MessageId, BdfDictionary> messageMetadata =
|
||||
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
|
||||
MessageId fooVersion4 = new MessageId(getRandomId());
|
||||
messageMetadata.put(fooVersion4, BdfDictionary.of(
|
||||
@@ -281,6 +302,11 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroupId);
|
||||
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
|
||||
oneOf(db).deleteMessage(txn, message.getId());
|
||||
oneOf(db).deleteMessageMetadata(txn, message.getId());
|
||||
@@ -303,8 +329,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).getContact(txn, contact.getId());
|
||||
will(returnValue(contact));
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
||||
will(returnValue(contactGroup));
|
||||
}});
|
||||
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
|
||||
@@ -318,7 +343,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
public void testReturnsLatestLocalProperties() throws Exception {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
expectGetLocalProperties(txn);
|
||||
|
||||
@@ -332,7 +357,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testReturnsEmptyPropertiesIfNoLocalPropertiesAreFound()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Map<MessageId, BdfDictionary> messageMetadata =
|
||||
new LinkedHashMap<>();
|
||||
// A local update for another transport should be ignored
|
||||
@@ -344,7 +369,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
));
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).startTransaction(true);
|
||||
oneOf(db).startTransaction(false);
|
||||
will(returnValue(txn));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
@@ -359,7 +384,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
public void testReturnsLocalProperties() throws Exception {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Map<MessageId, BdfDictionary> messageMetadata =
|
||||
new LinkedHashMap<>();
|
||||
// A local update for another transport should be ignored
|
||||
@@ -379,7 +404,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).startTransaction(true);
|
||||
oneOf(db).startTransaction(false);
|
||||
will(returnValue(txn));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
@@ -398,7 +423,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testReturnsRemotePropertiesOrEmptyProperties()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact1 = getContact(false);
|
||||
Contact contact2 = getContact(true);
|
||||
Contact contact3 = getContact(true);
|
||||
@@ -432,21 +457,19 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).startTransaction(true);
|
||||
oneOf(db).startTransaction(false);
|
||||
will(returnValue(txn));
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(contacts));
|
||||
// First contact: skipped because not active
|
||||
// Second contact: no updates
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact2);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact2);
|
||||
will(returnValue(contactGroup2));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup2.getId());
|
||||
will(returnValue(Collections.emptyMap()));
|
||||
// Third contact: returns an update
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact3);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact3);
|
||||
will(returnValue(contactGroup3));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup3.getId());
|
||||
@@ -516,8 +539,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
// Store the new properties in each contact's group, version 1
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(Collections.singletonList(contact)));
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
@@ -569,8 +591,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
// Store the merged properties in each contact's group, version 2
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(Collections.singletonList(contact)));
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, contact);
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
@@ -617,14 +638,28 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
return new Message(messageId, g, timestamp, raw);
|
||||
}
|
||||
|
||||
private void expectGetLocalProperties(Transaction txn) throws Exception {
|
||||
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||
// The latest update for transport "foo" should be returned
|
||||
private void expectGetLocalProperties(Transaction txn)
|
||||
throws Exception {
|
||||
Map<MessageId, BdfDictionary> messageMetadata =
|
||||
new LinkedHashMap<>();
|
||||
// The only update for transport "foo" should be returned
|
||||
MessageId fooVersion999 = new MessageId(getRandomId());
|
||||
messageMetadata.put(fooVersion999, BdfDictionary.of(
|
||||
new BdfEntry("transportId", "foo"),
|
||||
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
|
||||
MessageId barVersion3 = new MessageId(getRandomId());
|
||||
messageMetadata.put(barVersion3, BdfDictionary.of(
|
||||
@@ -639,6 +674,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(db).removeMessage(txn, barVersion1);
|
||||
oneOf(db).removeMessage(txn, barVersion2);
|
||||
// Retrieve and parse the latest local properties
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooVersion999);
|
||||
will(returnValue(fooUpdate));
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.briarproject.bramble.sync;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.sync.Ack;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
@@ -22,6 +22,7 @@ import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -36,7 +37,6 @@ import javax.inject.Inject;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -57,7 +57,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
||||
@Inject
|
||||
RecordWriterFactory recordWriterFactory;
|
||||
@Inject
|
||||
TransportCrypto transportCrypto;
|
||||
CryptoComponent crypto;
|
||||
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
@@ -79,11 +79,9 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
||||
headerKey = TestUtils.getSecretKey();
|
||||
streamNumber = 123;
|
||||
// Create a group
|
||||
ClientId clientId = new ClientId(getRandomString(123));
|
||||
int clientVersion = 1234567890;
|
||||
ClientId clientId = new ClientId(StringUtils.getRandomString(5));
|
||||
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
|
||||
Group group = groupFactory.createGroup(clientId, clientVersion,
|
||||
descriptor);
|
||||
Group group = groupFactory.createGroup(clientId, descriptor);
|
||||
// Add two messages to the group
|
||||
long timestamp = System.currentTimeMillis();
|
||||
byte[] body = "Hello world".getBytes("UTF-8");
|
||||
@@ -119,8 +117,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
||||
private void read(byte[] connectionData) throws Exception {
|
||||
// Calculate the expected tag
|
||||
byte[] expectedTag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
|
||||
|
||||
// Read the tag
|
||||
InputStream in = new ByteArrayInputStream(connectionData);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
@@ -11,11 +11,12 @@ import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||
import org.briarproject.bramble.api.transport.StreamContext;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.RunAction;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.hamcrest.Description;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.jmock.api.Action;
|
||||
import org.jmock.api.Invocation;
|
||||
import org.junit.Test;
|
||||
@@ -40,15 +41,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
private final TransportCrypto transportCrypto =
|
||||
context.mock(TransportCrypto.class);
|
||||
private final Executor dbExecutor = context.mock(Executor.class);
|
||||
private final ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
private final Clock clock = context.mock(Clock.class);
|
||||
public class TransportKeyManagerImplTest extends BrambleTestCase {
|
||||
|
||||
private final TransportId transportId = new TransportId("id");
|
||||
private final long maxLatency = 30 * 1000; // 30 seconds
|
||||
@@ -62,6 +55,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
public void testKeysAreRotatedAtStartup() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
Map<ContactId, TransportKeys> loaded = new LinkedHashMap<>();
|
||||
TransportKeys shouldRotate = createTransportKeys(900, 0);
|
||||
TransportKeys shouldNotRotate = createTransportKeys(1000, 0);
|
||||
@@ -78,15 +79,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).getTransportKeys(txn, transportId);
|
||||
will(returnValue(loaded));
|
||||
// Rotate the transport keys
|
||||
oneOf(transportCrypto).rotateTransportKeys(shouldRotate, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(shouldRotate, 1000);
|
||||
will(returnValue(rotated));
|
||||
oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(shouldNotRotate, 1000);
|
||||
will(returnValue(shouldNotRotate));
|
||||
// Encode the tags (3 sets per contact)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(6).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Save the keys that were rotated
|
||||
@@ -97,124 +97,161 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
with(rotationPeriodLength - 1), with(MILLISECONDS));
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
transportKeyManager.start(txn);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeysAreRotatedWhenAddingContact() throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
boolean alice = true;
|
||||
TransportKeys transportKeys = createTransportKeys(999, 0);
|
||||
TransportKeys rotated = createTransportKeys(1000, 0);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
999, alice);
|
||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 999,
|
||||
alice);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (1 ms after start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000 + 1));
|
||||
// Rotate the transport keys
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(rotated));
|
||||
// Encode the tags (3 sets)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(3).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, rotated);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
// The timestamp is 1 ms before the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000 - 1;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutgoingStreamContextIsNullIfContactIsNotFound()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutgoingStreamContextIsNullIfStreamCounterIsExhausted()
|
||||
throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
boolean alice = true;
|
||||
// The stream counter has been exhausted
|
||||
TransportKeys transportKeys = createTransportKeys(1000,
|
||||
MAX_32_BIT_UNSIGNED + 1);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice);
|
||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
||||
alice);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000));
|
||||
// Encode the tags (3 sets)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(3).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutgoingStreamCounterIsIncremented() throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
boolean alice = true;
|
||||
// The stream counter can be used one more time before being exhausted
|
||||
TransportKeys transportKeys = createTransportKeys(1000,
|
||||
MAX_32_BIT_UNSIGNED);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice);
|
||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
||||
alice);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000));
|
||||
// Encode the tags (3 sets)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(3).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||
@@ -222,9 +259,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
@@ -240,76 +277,94 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
|
||||
// The second request should return null, the counter is exhausted
|
||||
assertNull(transportKeyManager.getStreamContext(txn, contactId));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncomingStreamContextIsNullIfTagIsNotFound()
|
||||
throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
boolean alice = true;
|
||||
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice);
|
||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
||||
alice);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000));
|
||||
// Encode the tags (3 sets)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(3).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
alice);
|
||||
assertNull(transportKeyManager.getStreamContext(txn,
|
||||
new byte[TAG_LENGTH]));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTagIsNotRecognisedTwice() throws Exception {
|
||||
boolean alice = random.nextBoolean();
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
boolean alice = true;
|
||||
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||
// Keep a copy of the tags
|
||||
List<byte[]> tags = new ArrayList<>();
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
|
||||
1000, alice);
|
||||
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
|
||||
alice);
|
||||
will(returnValue(transportKeys));
|
||||
// Get the current time (the start of rotation period 1000)
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1000));
|
||||
// Encode the tags (3 sets)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(3).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction(tags));
|
||||
}
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Save the keys
|
||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||
// Encode a new tag after sliding the window
|
||||
oneOf(transportCrypto).encodeTag(with(any(byte[].class)),
|
||||
oneOf(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION),
|
||||
with((long) REORDERING_WINDOW_SIZE));
|
||||
will(new EncodeTagAction(tags));
|
||||
@@ -318,9 +373,9 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
1, new byte[REORDERING_WINDOW_SIZE / 8]);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
// The timestamp is at the start of rotation period 1000
|
||||
long timestamp = rotationPeriodLength * 1000;
|
||||
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
|
||||
@@ -340,10 +395,20 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
|
||||
// The second request should return null, the tag has already been used
|
||||
assertNull(transportKeyManager.getStreamContext(txn, tag));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeysAreRotatedToCurrentPeriod() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
Executor dbExecutor = context.mock(Executor.class);
|
||||
ScheduledExecutorService scheduler =
|
||||
context.mock(ScheduledExecutorService.class);
|
||||
Clock clock = context.mock(Clock.class);
|
||||
|
||||
TransportKeys transportKeys = createTransportKeys(1000, 0);
|
||||
Map<ContactId, TransportKeys> loaded =
|
||||
Collections.singletonMap(contactId, transportKeys);
|
||||
@@ -359,13 +424,12 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).getTransportKeys(txn, transportId);
|
||||
will(returnValue(loaded));
|
||||
// Rotate the transport keys (the keys are unaffected)
|
||||
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
|
||||
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
|
||||
will(returnValue(transportKeys));
|
||||
// Encode the tags (3 sets)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(3).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Schedule key rotation at the start of the next rotation period
|
||||
@@ -381,14 +445,13 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(rotationPeriodLength * 1001));
|
||||
// Rotate the transport keys
|
||||
oneOf(transportCrypto).rotateTransportKeys(
|
||||
with(any(TransportKeys.class)), with(1001L));
|
||||
oneOf(crypto).rotateTransportKeys(with(any(TransportKeys.class)),
|
||||
with(1001L));
|
||||
will(returnValue(rotated));
|
||||
// Encode the tags (3 sets)
|
||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||
exactly(3).of(transportCrypto).encodeTag(
|
||||
with(any(byte[].class)), with(tagKey),
|
||||
with(PROTOCOL_VERSION), with(i));
|
||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||
will(new EncodeTagAction());
|
||||
}
|
||||
// Save the keys that were rotated
|
||||
@@ -402,10 +465,12 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).endTransaction(txn1);
|
||||
}});
|
||||
|
||||
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
|
||||
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
|
||||
maxLatency);
|
||||
TransportKeyManager
|
||||
transportKeyManager = new TransportKeyManagerImpl(db,
|
||||
crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
|
||||
transportKeyManager.start(txn);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
private TransportKeys createTransportKeys(long rotationPeriod,
|
||||
|
||||
@@ -36,7 +36,6 @@ dependencyVerification {
|
||||
'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',
|
||||
'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:4.4.0:jna-4.4.0.jar:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
|
||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||
|
||||
@@ -108,7 +108,6 @@ dependencyVerification {
|
||||
'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: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: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',
|
||||
@@ -186,19 +185,19 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 1700
|
||||
versionName "0.17.0"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
resValue "string", "app_package", "org.briarproject.briar.android"
|
||||
resValue "string", "app_name", "Briar"
|
||||
versionCode 1616
|
||||
versionName "0.16.16"
|
||||
applicationId "org.briarproject.briar.beta"
|
||||
resValue "string", "app_package", "org.briarproject.briar.beta"
|
||||
resValue "string", "app_name", "Briar Beta"
|
||||
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
resValue "string", "app_package", "org.briarproject.briar.android.debug"
|
||||
resValue "string", "app_name", "Briar Debug"
|
||||
resValue "string", "app_package", "org.briarproject.briar.beta.debug"
|
||||
resValue "string", "app_name", "Briar Beta Debug"
|
||||
shrinkResources false
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
@@ -125,7 +125,7 @@ public interface AndroidComponent
|
||||
|
||||
ContactExchangeTask contactExchangeTask();
|
||||
|
||||
KeyAgreementTask keyAgreementTask();
|
||||
KeyAgreementTaskFactory keyAgreementTaskFactory();
|
||||
|
||||
PayloadEncoder payloadEncoder();
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ package org.briarproject.briar.android;
|
||||
*/
|
||||
public interface BriarApplication {
|
||||
|
||||
// This build expires on 31 December 2018
|
||||
long EXPIRY_DATE = 1546214400 * 1000L;
|
||||
// This build expires on 30 April 2018
|
||||
long EXPIRY_DATE = 1525046400 * 1000L;
|
||||
|
||||
AndroidComponent getApplicationComponent();
|
||||
|
||||
|
||||
@@ -124,21 +124,24 @@ public class BriarService extends Service {
|
||||
b.setPriority(PRIORITY_MIN);
|
||||
startForeground(ONGOING_NOTIFICATION_ID, b.build());
|
||||
// Start the services in a background thread
|
||||
new Thread(() -> {
|
||||
String nickname = databaseConfig.getLocalAuthorName();
|
||||
StartResult result = lifecycleManager.startServices(nickname);
|
||||
if (result == SUCCESS) {
|
||||
started = true;
|
||||
} else if (result == ALREADY_RUNNING) {
|
||||
LOG.info("Already running");
|
||||
stopSelf();
|
||||
} else {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.warning("Startup failed: " + result);
|
||||
showStartupFailureNotification(result);
|
||||
stopSelf();
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
String nickname = databaseConfig.getLocalAuthorName();
|
||||
StartResult result = lifecycleManager.startServices(nickname);
|
||||
if (result == SUCCESS) {
|
||||
started = true;
|
||||
} else if (result == ALREADY_RUNNING) {
|
||||
LOG.info("Already running");
|
||||
stopSelf();
|
||||
} else {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.warning("Startup failed: " + result);
|
||||
showStartupFailureNotification(result);
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}.start();
|
||||
}
|
||||
|
||||
private void showStartupFailureNotification(StartResult result) {
|
||||
@@ -184,9 +187,12 @@ public class BriarService extends Service {
|
||||
LOG.info("Destroyed");
|
||||
stopForeground(true);
|
||||
// Stop the services in a background thread
|
||||
new Thread(() -> {
|
||||
if (started) lifecycleManager.stopServices();
|
||||
}).start();
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (started) lifecycleManager.stopServices();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,7 +18,7 @@ public interface TestingConstants {
|
||||
* Whether this is a beta build. This should be set to false for final
|
||||
* release builds.
|
||||
*/
|
||||
boolean IS_BETA_BUILD = false;
|
||||
boolean IS_BETA_BUILD = true;
|
||||
|
||||
/**
|
||||
* Default log level. Disable logging for final release builds.
|
||||
|
||||
@@ -123,22 +123,25 @@ public class BriarControllerImpl implements BriarController {
|
||||
|
||||
@Override
|
||||
public void signOut(ResultHandler<Void> eventHandler) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
// Wait for the service to finish starting up
|
||||
IBinder binder = serviceConnection.waitForBinder();
|
||||
BriarService service =
|
||||
((BriarService.BriarBinder) binder).getService();
|
||||
service.waitForStartup();
|
||||
// Shut down the service and wait for it to shut down
|
||||
LOG.info("Shutting down service");
|
||||
service.shutdown();
|
||||
service.waitForShutdown();
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warning("Interrupted while waiting for service");
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Wait for the service to finish starting up
|
||||
IBinder binder = serviceConnection.waitForBinder();
|
||||
BriarService service =
|
||||
((BriarService.BriarBinder) binder).getService();
|
||||
service.waitForStartup();
|
||||
// Shut down the service and wait for it to shut down
|
||||
LOG.info("Shutting down service");
|
||||
service.shutdown();
|
||||
service.waitForShutdown();
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warning("Interrupted while waiting for service");
|
||||
}
|
||||
eventHandler.onResult(null);
|
||||
}
|
||||
eventHandler.onResult(null);
|
||||
}).start();
|
||||
}.start();
|
||||
}
|
||||
|
||||
private void unbindService() {
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.google.zxing.Result;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
@@ -47,7 +48,6 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
||||
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
|
||||
@@ -68,7 +68,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
||||
private static final Logger LOG = Logger.getLogger(TAG);
|
||||
|
||||
@Inject
|
||||
Provider<KeyAgreementTask> keyAgreementTaskProvider;
|
||||
KeyAgreementTaskFactory keyAgreementTaskFactory;
|
||||
@Inject
|
||||
PayloadEncoder payloadEncoder;
|
||||
@Inject
|
||||
@@ -187,7 +187,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
||||
@UiThread
|
||||
private void startListening() {
|
||||
KeyAgreementTask oldTask = task;
|
||||
KeyAgreementTask newTask = keyAgreementTaskProvider.get();
|
||||
KeyAgreementTask newTask = keyAgreementTaskFactory.createTask();
|
||||
task = newTask;
|
||||
ioExecutor.execute(() -> {
|
||||
if (oldTask != null) oldTask.stopListening();
|
||||
|
||||
@@ -29,8 +29,6 @@ import javax.inject.Inject;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
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.navdrawer.NavDrawerController.ExpiryWarning.NO;
|
||||
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.SHOW;
|
||||
@@ -46,7 +44,7 @@ public class NavDrawerControllerImpl extends DbControllerImpl
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(NavDrawerControllerImpl.class.getName());
|
||||
private static final String EXPIRY_DATE_WARNING = "expiryDateWarning";
|
||||
private static final String EXPIRY_SHOW_UPDATE = "expiryShowUpdate";
|
||||
private static final String EXPIRY_SHOW_UPDATE = "expiryShowUpdateAgain";
|
||||
|
||||
private final PluginManager pluginManager;
|
||||
private final SettingsManager settingsManager;
|
||||
@@ -108,10 +106,6 @@ public class NavDrawerControllerImpl extends DbControllerImpl
|
||||
|
||||
@Override
|
||||
public void showExpiryWarning(ResultHandler<ExpiryWarning> handler) {
|
||||
if (!IS_DEBUG_BUILD && !IS_BETA_BUILD) {
|
||||
handler.onResult(NO);
|
||||
return;
|
||||
}
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
Settings settings =
|
||||
|
||||
@@ -28,6 +28,7 @@ import javax.annotation.Nullable;
|
||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
||||
import static android.view.KeyEvent.KEYCODE_BACK;
|
||||
import static android.view.KeyEvent.KEYCODE_ENTER;
|
||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||
|
||||
@UiThread
|
||||
@@ -82,16 +83,22 @@ public class TextInputView extends KeyboardAwareLinearLayout
|
||||
hideEmojiDrawer();
|
||||
return true;
|
||||
}
|
||||
if (keyCode == KEYCODE_ENTER && event.isCtrlPressed()) {
|
||||
trySendMessage();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
ui.sendButton.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onSendClick(ui.editText.getText().toString());
|
||||
}
|
||||
});
|
||||
ui.sendButton.setOnClickListener(v -> trySendMessage());
|
||||
ui.emojiDrawer.setEmojiEventListener(this);
|
||||
}
|
||||
|
||||
private void trySendMessage() {
|
||||
if (listener != null) {
|
||||
listener.onSendClick(ui.editText.getText().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
if (visibility == GONE && isKeyboardOpen()) {
|
||||
|
||||
@@ -39,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_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">
|
||||
<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 test version of Briar. Your account will expire in %d days and cannot be renewed.</item>
|
||||
<item quantity="one">This is a beta 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>
|
||||
</plurals>
|
||||
<string name="expiry_update">The testing expiry date has been extended. Your account will now expire in %d days.</string>
|
||||
<string name="expiry_update">The beta 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>
|
||||
|
||||
<!-- Navigation Drawer -->
|
||||
|
||||
@@ -17,15 +17,10 @@ import javax.annotation.Nullable;
|
||||
public interface BlogManager {
|
||||
|
||||
/**
|
||||
* The unique ID of the blog client.
|
||||
* Unique ID of the blog client.
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog");
|
||||
|
||||
/**
|
||||
* The current version of the blog client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Adds the given {@link Blog).}
|
||||
*/
|
||||
|
||||
@@ -17,8 +17,8 @@ import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
|
||||
@NotNullByDefault
|
||||
public interface BlogPostFactory {
|
||||
|
||||
String SIGNING_LABEL_POST = CLIENT_ID.getString() + "/POST";
|
||||
String SIGNING_LABEL_COMMENT = CLIENT_ID.getString() + "/COMMENT";
|
||||
String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
|
||||
String SIGNING_LABEL_COMMENT = CLIENT_ID + "/COMMENT";
|
||||
|
||||
BlogPost createBlogPost(GroupId groupId, long timestamp,
|
||||
@Nullable MessageId parent, LocalAuthor author, String body)
|
||||
|
||||
@@ -5,13 +5,6 @@ import org.briarproject.briar.api.sharing.SharingManager;
|
||||
|
||||
public interface BlogSharingManager extends SharingManager<Blog> {
|
||||
|
||||
/**
|
||||
* The unique ID of the blog sharing client.
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog.sharing");
|
||||
|
||||
/**
|
||||
* The current version of the blog sharing client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
}
|
||||
|
||||
@@ -15,11 +15,6 @@ public interface FeedManager {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.feed");
|
||||
|
||||
/**
|
||||
* The current version of the RSS feed client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Adds an RSS feed as a new dedicated blog.
|
||||
*/
|
||||
|
||||
@@ -22,11 +22,6 @@ public interface ForumManager {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum");
|
||||
|
||||
/**
|
||||
* The current version of the forum client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Subscribes to a forum.
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
|
||||
@NotNullByDefault
|
||||
public interface ForumPostFactory {
|
||||
|
||||
String SIGNING_LABEL_POST = CLIENT_ID.getString() + "/POST";
|
||||
String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
|
||||
|
||||
@CryptoExecutor
|
||||
ForumPost createPost(GroupId groupId, long timestamp,
|
||||
|
||||
@@ -5,13 +5,6 @@ import org.briarproject.briar.api.sharing.SharingManager;
|
||||
|
||||
public interface ForumSharingManager extends SharingManager<Forum> {
|
||||
|
||||
/**
|
||||
* The unique ID of the forum sharing client.
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum.sharing");
|
||||
|
||||
/**
|
||||
* The current version of the forum sharing client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
}
|
||||
|
||||
@@ -86,44 +86,4 @@ public interface IntroductionConstants {
|
||||
int TASK_ACTIVATE_CONTACT = 1;
|
||||
int TASK_ABORT = 2;
|
||||
|
||||
/**
|
||||
* Label for deriving the shared secret.
|
||||
*/
|
||||
String SHARED_SECRET_LABEL =
|
||||
"org.briarproject.briar.introduction/SHARED_SECRET";
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's key binding nonce from the shared secret.
|
||||
*/
|
||||
String ALICE_NONCE_LABEL =
|
||||
"org.briarproject.briar.introduction/ALICE_NONCE";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's key binding nonce from the shared secret.
|
||||
*/
|
||||
String BOB_NONCE_LABEL =
|
||||
"org.briarproject.briar.introduction/BOB_NONCE";
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's MAC key from the shared secret.
|
||||
*/
|
||||
String ALICE_MAC_KEY_LABEL =
|
||||
"org.briarproject.briar.introduction/ALICE_MAC_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving Bob's MAC key from the shared secret.
|
||||
*/
|
||||
String BOB_MAC_KEY_LABEL =
|
||||
"org.briarproject.briar.introduction/BOB_MAC_KEY";
|
||||
|
||||
/**
|
||||
* Label for signing the introduction response.
|
||||
*/
|
||||
String SIGNING_LABEL =
|
||||
"org.briarproject.briar.introduction/RESPONSE_SIGNATURE";
|
||||
|
||||
/**
|
||||
* Label for MACing the introduction response.
|
||||
*/
|
||||
String MAC_LABEL = "org.briarproject.briar.introduction/RESPONSE_MAC";
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ public interface IntroductionManager extends ConversationClient {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.introduction");
|
||||
|
||||
/**
|
||||
* The current version of the introduction client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Sends two initial introduction messages.
|
||||
*/
|
||||
|
||||
@@ -18,11 +18,6 @@ public interface MessagingManager extends ConversationClient {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.messaging");
|
||||
|
||||
/**
|
||||
* The current version of the messaging client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Stores a local private message.
|
||||
*/
|
||||
|
||||
@@ -13,8 +13,8 @@ import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT
|
||||
@NotNullByDefault
|
||||
public interface GroupMessageFactory {
|
||||
|
||||
String SIGNING_LABEL_JOIN = CLIENT_ID.getString() + "/JOIN";
|
||||
String SIGNING_LABEL_POST = CLIENT_ID.getString() + "/POST";
|
||||
String SIGNING_LABEL_JOIN = CLIENT_ID + "/JOIN";
|
||||
String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
|
||||
|
||||
/**
|
||||
* Creates a join announcement message for the creator of a group.
|
||||
|
||||
@@ -21,11 +21,6 @@ public interface PrivateGroupManager {
|
||||
*/
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.privategroup");
|
||||
|
||||
/**
|
||||
* The current version of the private group client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Adds a new private group and joins it.
|
||||
*
|
||||
|
||||
@@ -12,7 +12,7 @@ import static org.briarproject.briar.api.privategroup.invitation.GroupInvitation
|
||||
@NotNullByDefault
|
||||
public interface GroupInvitationFactory {
|
||||
|
||||
String SIGNING_LABEL_INVITE = CLIENT_ID.getString() + "/INVITE";
|
||||
String SIGNING_LABEL_INVITE = CLIENT_ID + "/INVITE";
|
||||
|
||||
/**
|
||||
* Returns a signature to include when inviting a member to join a private
|
||||
|
||||
@@ -25,11 +25,6 @@ public interface GroupInvitationManager extends ConversationClient {
|
||||
ClientId CLIENT_ID =
|
||||
new ClientId("org.briarproject.briar.privategroup.invitation");
|
||||
|
||||
/**
|
||||
* The current version of the private group invitation client.
|
||||
*/
|
||||
int CLIENT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Sends an invitation to share the given private group with the given
|
||||
* contact, including an optional message.
|
||||
|
||||
@@ -45,7 +45,6 @@ dependencyVerification {
|
||||
'com.squareup.okio:okio:1.13.0:okio-1.13.0.jar:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||
'net.jodah:concurrentunit:0.4.2:concurrentunit-0.4.2.jar:5583078e1acf91734939e985bc9e7ee947b0e93a8eef679da6bb07bbeb47ced3',
|
||||
'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',
|
||||
|
||||
@@ -16,8 +16,6 @@ import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -54,8 +52,8 @@ class BlogFactoryImpl implements BlogFactory {
|
||||
rssFeed
|
||||
);
|
||||
byte[] descriptor = clientHelper.toByteArray(blog);
|
||||
Group g = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
|
||||
descriptor);
|
||||
Group g = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
return new Blog(g, a, rssFeed);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -45,8 +45,6 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED;
|
||||
import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE;
|
||||
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_COMMENT_LENGTH;
|
||||
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
|
||||
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_VERSION;
|
||||
import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_COMMENT;
|
||||
import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_POST;
|
||||
import static org.briarproject.briar.api.blog.MessageType.COMMENT;
|
||||
@@ -201,8 +199,8 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
|
||||
|
||||
// Get and Validate the Wrapped Message
|
||||
Group wGroup = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
|
||||
descriptor);
|
||||
Group wGroup = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
Blog wBlog = blogFactory.parseBlog(wGroup);
|
||||
BdfList wBodyList = BdfList.of(POST.getInt(), content, signature);
|
||||
byte[] wBody = clientHelper.toByteArray(wBodyList);
|
||||
@@ -264,8 +262,8 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
MessageId parentId = new MessageId(parentIdBytes);
|
||||
|
||||
// Get and Validate the Wrapped Comment
|
||||
Group wGroup = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
|
||||
descriptor);
|
||||
Group wGroup = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
BdfList wBodyList = BdfList.of(COMMENT.getInt(), comment, pOriginalId,
|
||||
oldId, signature);
|
||||
byte[] wBody = clientHelper.toByteArray(wBodyList);
|
||||
|
||||
@@ -12,10 +12,8 @@ import org.briarproject.briar.api.client.QueueMessageFactory;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.sync.MessageId.LABEL;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||
import static org.briarproject.briar.api.client.QueueMessage.MAX_QUEUE_MESSAGE_BODY_LENGTH;
|
||||
import static org.briarproject.briar.api.client.QueueMessage.QUEUE_MESSAGE_HEADER_LENGTH;
|
||||
@@ -47,9 +45,9 @@ class QueueMessageFactoryImpl implements QueueMessageFactory {
|
||||
byte[] bodyBytes = new byte[body.length + INT_64_BYTES];
|
||||
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, bodyBytes, 0,
|
||||
body.length + INT_64_BYTES);
|
||||
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
|
||||
groupId.getBytes(), timeBytes, bodyBytes);
|
||||
MessageId id = new MessageId(hash);
|
||||
MessageId id = new MessageId(
|
||||
crypto.hash(MessageId.LABEL, groupId.getBytes(), timeBytes,
|
||||
bodyBytes));
|
||||
return new QueueMessage(id, groupId, timestamp, queuePosition, raw);
|
||||
}
|
||||
|
||||
|
||||
@@ -496,7 +496,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener,
|
||||
}
|
||||
|
||||
private Group getLocalGroup() {
|
||||
return contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION);
|
||||
return contactGroupFactory.createLocalGroup(CLIENT_ID);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import javax.inject.Inject;
|
||||
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH;
|
||||
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
|
||||
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -53,8 +52,7 @@ class ForumFactoryImpl implements ForumFactory {
|
||||
try {
|
||||
BdfList forum = BdfList.of(name, salt);
|
||||
byte[] descriptor = clientHelper.toByteArray(forum);
|
||||
Group g = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
|
||||
descriptor);
|
||||
Group g = groupFactory.createGroup(CLIENT_ID, descriptor);
|
||||
return new Forum(g, name, salt);
|
||||
} catch (FormatException e) {
|
||||
throw new AssertionError(e);
|
||||
|
||||
@@ -50,11 +50,7 @@ import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.briar.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ACCEPT;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ADDED_CONTACT_ID;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ALICE_MAC_KEY_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ALICE_NONCE_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ANSWERED;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.BOB_MAC_KEY_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.BOB_NONCE_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.EXISTS;
|
||||
@@ -64,7 +60,6 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.INTR
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_KEY;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_ID;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.NAME;
|
||||
@@ -81,9 +76,7 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.REMO
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.SHARED_SECRET_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNATURE;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNING_LABEL;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.STATE;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TASK;
|
||||
@@ -96,7 +89,7 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ABORT;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ACK;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -105,6 +98,8 @@ class IntroduceeManager {
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(IntroduceeManager.class.getName());
|
||||
|
||||
static final String SIGNING_LABEL_RESPONSE = CLIENT_ID + "/RESPONSE";
|
||||
|
||||
private final MessageSender messageSender;
|
||||
private final DatabaseComponent db;
|
||||
private final ClientHelper clientHelper;
|
||||
@@ -292,7 +287,8 @@ class IntroduceeManager {
|
||||
|
||||
@Nullable
|
||||
private BdfDictionary performTasks(Transaction txn,
|
||||
BdfDictionary localState) throws FormatException, DbException {
|
||||
BdfDictionary localState)
|
||||
throws FormatException, DbException {
|
||||
|
||||
if (!localState.containsKey(TASK) || localState.get(TASK) == NULL_VALUE)
|
||||
return null;
|
||||
@@ -309,21 +305,22 @@ class IntroduceeManager {
|
||||
}
|
||||
|
||||
// figure out who takes which role by comparing public keys
|
||||
byte[] ourPublicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY);
|
||||
byte[] theirPublicKeyBytes = localState.getRaw(E_PUBLIC_KEY);
|
||||
int comp = Bytes.COMPARATOR.compare(new Bytes(ourPublicKeyBytes),
|
||||
new Bytes(theirPublicKeyBytes));
|
||||
byte[] publicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY);
|
||||
byte[] theirEphemeralKey = localState.getRaw(E_PUBLIC_KEY);
|
||||
int comp = Bytes.COMPARATOR.compare(new Bytes(publicKeyBytes),
|
||||
new Bytes(theirEphemeralKey));
|
||||
boolean alice = comp < 0;
|
||||
|
||||
// get our local author
|
||||
LocalAuthor author = identityManager.getLocalAuthor(txn);
|
||||
|
||||
SecretKey secretKey;
|
||||
byte[] ourPrivateKeyBytes = localState.getRaw(OUR_PRIVATE_KEY);
|
||||
byte[] privateKeyBytes = localState.getRaw(OUR_PRIVATE_KEY);
|
||||
try {
|
||||
// derive secret master key
|
||||
secretKey = deriveSecretKey(ourPublicKeyBytes,
|
||||
ourPrivateKeyBytes, alice, theirPublicKeyBytes);
|
||||
secretKey =
|
||||
deriveSecretKey(publicKeyBytes, privateKeyBytes, alice,
|
||||
theirEphemeralKey);
|
||||
// derive MAC keys and nonces, sign our nonce and calculate MAC
|
||||
deriveMacKeysAndNonces(localState, author, secretKey, alice);
|
||||
} catch (GeneralSecurityException e) {
|
||||
@@ -412,41 +409,34 @@ class IntroduceeManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
private SecretKey deriveSecretKey(byte[] ourPublicKeyBytes,
|
||||
byte[] ourPrivateKeyBytes, boolean alice,
|
||||
byte[] theirPublicKeyBytes) throws GeneralSecurityException {
|
||||
private SecretKey deriveSecretKey(byte[] publicKeyBytes,
|
||||
byte[] privateKeyBytes, boolean alice, byte[] theirPublicKey)
|
||||
throws GeneralSecurityException {
|
||||
// parse the local ephemeral key pair
|
||||
KeyParser keyParser = cryptoComponent.getAgreementKeyParser();
|
||||
PublicKey ourPublicKey;
|
||||
PrivateKey ourPrivateKey;
|
||||
PublicKey publicKey;
|
||||
PrivateKey privateKey;
|
||||
try {
|
||||
ourPublicKey = keyParser.parsePublicKey(ourPublicKeyBytes);
|
||||
ourPrivateKey = keyParser.parsePrivateKey(ourPrivateKeyBytes);
|
||||
publicKey = keyParser.parsePublicKey(publicKeyBytes);
|
||||
privateKey = keyParser.parsePrivateKey(privateKeyBytes);
|
||||
} catch (GeneralSecurityException e) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
throw new RuntimeException("Our own ephemeral key is invalid");
|
||||
}
|
||||
KeyPair ourKeyPair = new KeyPair(ourPublicKey, ourPrivateKey);
|
||||
PublicKey theirPublicKey =
|
||||
keyParser.parsePublicKey(theirPublicKeyBytes);
|
||||
KeyPair keyPair = new KeyPair(publicKey, privateKey);
|
||||
|
||||
// The shared secret is derived from the local ephemeral key pair
|
||||
// The master secret is derived from the local ephemeral key pair
|
||||
// and the remote ephemeral public key
|
||||
byte[][] inputs = {
|
||||
new byte[] {CLIENT_VERSION},
|
||||
alice ? ourPublicKeyBytes : theirPublicKeyBytes,
|
||||
alice ? theirPublicKeyBytes : ourPublicKeyBytes
|
||||
};
|
||||
return cryptoComponent.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||
theirPublicKey, ourKeyPair, inputs);
|
||||
return cryptoComponent
|
||||
.deriveMasterSecret(theirPublicKey, keyPair, alice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives nonces, signs our nonce and calculates MAC
|
||||
* <p>
|
||||
* Derives two nonces and two MAC keys from the shared secret key.
|
||||
* Derives two nonces and two mac keys from the secret master key.
|
||||
* The other introducee's nonce and MAC key are added to the localState.
|
||||
* <p>
|
||||
* Our nonce is signed with the local author's long-term private key.
|
||||
@@ -457,24 +447,21 @@ class IntroduceeManager {
|
||||
private void deriveMacKeysAndNonces(BdfDictionary localState,
|
||||
LocalAuthor author, SecretKey secretKey, boolean alice)
|
||||
throws FormatException, GeneralSecurityException {
|
||||
// Derive two nonces and two MAC keys from the shared secret key
|
||||
String ourNonceLabel = alice ? ALICE_NONCE_LABEL : BOB_NONCE_LABEL;
|
||||
String theirNonceLabel = alice ? BOB_NONCE_LABEL : ALICE_NONCE_LABEL;
|
||||
byte[] ourNonce = cryptoComponent.mac(ourNonceLabel, secretKey);
|
||||
byte[] theirNonce = cryptoComponent.mac(theirNonceLabel, secretKey);
|
||||
String ourKeyLabel = alice ? ALICE_MAC_KEY_LABEL : BOB_MAC_KEY_LABEL;
|
||||
String theirKeyLabel = alice ? BOB_MAC_KEY_LABEL : ALICE_MAC_KEY_LABEL;
|
||||
SecretKey ourMacKey = cryptoComponent.deriveKey(ourKeyLabel, secretKey);
|
||||
SecretKey theirMacKey =
|
||||
cryptoComponent.deriveKey(theirKeyLabel, secretKey);
|
||||
// Derive two nonces and a MAC key from the secret master key
|
||||
byte[] ourNonce =
|
||||
cryptoComponent.deriveSignatureNonce(secretKey, alice);
|
||||
byte[] theirNonce =
|
||||
cryptoComponent.deriveSignatureNonce(secretKey, !alice);
|
||||
SecretKey macKey = cryptoComponent.deriveMacKey(secretKey, alice);
|
||||
SecretKey theirMacKey = cryptoComponent.deriveMacKey(secretKey, !alice);
|
||||
|
||||
// Save the other nonce and MAC key for the verification
|
||||
localState.put(NONCE, theirNonce);
|
||||
localState.put(MAC_KEY, theirMacKey.getBytes());
|
||||
|
||||
// Sign our nonce with our long-term identity public key
|
||||
byte[] sig = cryptoComponent.sign(SIGNING_LABEL, ourNonce,
|
||||
author.getPrivateKey());
|
||||
byte[] sig = cryptoComponent
|
||||
.sign(SIGNING_LABEL_RESPONSE, ourNonce, author.getPrivateKey());
|
||||
|
||||
// Calculate a MAC over identity public key, ephemeral public key,
|
||||
// transport properties and timestamp.
|
||||
@@ -484,7 +471,7 @@ class IntroduceeManager {
|
||||
BdfList toMacList = BdfList.of(author.getPublicKey(),
|
||||
publicKeyBytes, tp, ourTime);
|
||||
byte[] toMac = clientHelper.toByteArray(toMacList);
|
||||
byte[] mac = cryptoComponent.mac(MAC_LABEL, ourMacKey, toMac);
|
||||
byte[] mac = cryptoComponent.mac(macKey, toMac);
|
||||
|
||||
// Add MAC and signature to localState, so it can be included in ACK
|
||||
localState.put(OUR_MAC, mac);
|
||||
@@ -498,7 +485,7 @@ class IntroduceeManager {
|
||||
byte[] key = localState.getRaw(PUBLIC_KEY);
|
||||
|
||||
// Verify the signature
|
||||
if (!cryptoComponent.verify(SIGNING_LABEL, nonce, key, sig)) {
|
||||
if (!cryptoComponent.verify(SIGNING_LABEL_RESPONSE, nonce, key, sig)) {
|
||||
LOG.warning("Invalid nonce signature in ACK");
|
||||
throw new GeneralSecurityException();
|
||||
}
|
||||
@@ -518,7 +505,7 @@ class IntroduceeManager {
|
||||
long timestamp = localState.getLong(TIME);
|
||||
BdfList toMacList = BdfList.of(pubKey, ePubKey, tp, timestamp);
|
||||
byte[] toMac = clientHelper.toByteArray(toMacList);
|
||||
byte[] calculatedMac = cryptoComponent.mac(MAC_LABEL, macKey, toMac);
|
||||
byte[] calculatedMac = cryptoComponent.mac(macKey, toMac);
|
||||
if (!Arrays.equals(mac, calculatedMac)) {
|
||||
LOG.warning("Received ACK with invalid MAC");
|
||||
throw new GeneralSecurityException();
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.briarproject.bramble.api.sync.Group;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION;
|
||||
|
||||
class IntroductionGroupFactory {
|
||||
|
||||
@@ -17,16 +16,14 @@ class IntroductionGroupFactory {
|
||||
@Inject
|
||||
IntroductionGroupFactory(ContactGroupFactory contactGroupFactory) {
|
||||
this.contactGroupFactory = contactGroupFactory;
|
||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
|
||||
CLIENT_VERSION);
|
||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID);
|
||||
}
|
||||
|
||||
Group createIntroductionGroup(Contact c) {
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, c);
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID, c);
|
||||
}
|
||||
|
||||
Group createLocalGroup() {
|
||||
public Group createLocalGroup() {
|
||||
return localGroup;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,8 +79,7 @@ class MessagingManagerImpl extends ConversationClientImpl
|
||||
|
||||
@Override
|
||||
public Group getContactGroup(Contact c) {
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, c);
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,7 +20,6 @@ import javax.inject.Inject;
|
||||
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.GROUP_SALT_LENGTH;
|
||||
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
|
||||
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -65,8 +64,7 @@ class PrivateGroupFactoryImpl implements PrivateGroupFactory {
|
||||
salt
|
||||
);
|
||||
byte[] descriptor = clientHelper.toByteArray(group);
|
||||
Group g = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
|
||||
descriptor);
|
||||
Group g = groupFactory.createGroup(CLIENT_ID, descriptor);
|
||||
return new PrivateGroup(g, name, author, salt);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -17,7 +17,6 @@ import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.CLIENT_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -53,7 +52,7 @@ class GroupInvitationFactoryImpl implements GroupInvitationFactory {
|
||||
public BdfList createInviteToken(AuthorId creatorId, AuthorId memberId,
|
||||
GroupId privateGroupId, long timestamp) {
|
||||
Group contactGroup = contactGroupFactory.createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, creatorId, memberId);
|
||||
creatorId, memberId);
|
||||
return BdfList.of(
|
||||
timestamp,
|
||||
contactGroup.getId(),
|
||||
|
||||
@@ -132,8 +132,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
|
||||
@Override
|
||||
public Group getContactGroup(Contact c) {
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID,
|
||||
CLIENT_VERSION, c);
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -51,11 +51,6 @@ class BlogSharingManagerImpl extends SharingManagerImpl<Blog>
|
||||
return CLIENT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getClientVersion() {
|
||||
return CLIENT_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called during each startup for each existing Contact.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user