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