mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 19:59:05 +01:00
First stage of key rotation refactoring. Some tests are failing.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package net.sf.briar.crypto;
|
||||
|
||||
import static net.sf.briar.api.plugins.InvitationConstants.CODE_BITS;
|
||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
@@ -48,18 +49,23 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
private static final int GCM_MAC_LENGTH = 16; // 128 bits
|
||||
|
||||
// Labels for key derivation
|
||||
private static final byte[] TAG = { 'T', 'A', 'G' };
|
||||
private static final byte[] FRAME = { 'F', 'R', 'A', 'M', 'E' };
|
||||
private static final byte[] A_TAG = { 'A', '_', 'T', 'A', 'G', '\0' };
|
||||
private static final byte[] B_TAG = { 'B', '_', 'T', 'A', 'G', '\0' };
|
||||
private static final byte[] A_FRAME_A =
|
||||
{ 'A', '_', 'F', 'R', 'A', 'M', 'E', '_', 'A', '\0' };
|
||||
private static final byte[] A_FRAME_B =
|
||||
{ 'A', '_', 'F', 'R', 'A', 'M', 'E', '_', 'B', '\0' };
|
||||
private static final byte[] B_FRAME_A =
|
||||
{ 'B', '_', 'F', 'R', 'A', 'M', 'E', '_', 'A', '\0' };
|
||||
private static final byte[] B_FRAME_B =
|
||||
{ 'B', '_', 'F', 'R', 'A', 'M', 'E', '_', 'B', '\0' };
|
||||
// Labels for secret derivation
|
||||
private static final byte[] FIRST = { 'F', 'I', 'R', 'S', 'T' };
|
||||
private static final byte[] NEXT = { 'N', 'E', 'X', 'T' };
|
||||
private static final byte[] FIRST = { 'F', 'I', 'R', 'S', 'T', '\0' };
|
||||
private static final byte[] ROTATE = { 'R', 'O', 'T', 'A', 'T', 'E', '\0' };
|
||||
// Label for confirmation code derivation
|
||||
private static final byte[] CODE = { 'C', 'O', 'D', 'E' };
|
||||
// Context strings for key and confirmation code derivation
|
||||
private static final byte[] INITIATOR = { 'I' };
|
||||
private static final byte[] RESPONDER = { 'R' };
|
||||
private static final byte[] CODE = { 'C', 'O', 'D', 'E', '\0' };
|
||||
// Blank plaintext for key derivation
|
||||
private static final byte[] KEY_DERIVATION_INPUT =
|
||||
private static final byte[] KEY_DERIVATION_BLANK_PLAINTEXT =
|
||||
new byte[SECRET_KEY_BYTES];
|
||||
|
||||
private final KeyParser agreementKeyParser, signatureKeyParser;
|
||||
@@ -87,43 +93,46 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
secureRandom = new SecureRandom();
|
||||
}
|
||||
|
||||
public ErasableKey deriveTagKey(byte[] secret, boolean initiator) {
|
||||
if(initiator) return deriveKey(secret, TAG, INITIATOR);
|
||||
else return deriveKey(secret, TAG, RESPONDER);
|
||||
public ErasableKey deriveTagKey(byte[] secret, boolean alice) {
|
||||
if(alice) return deriveKey(secret, A_TAG, 0L);
|
||||
else return deriveKey(secret, B_TAG, 0L);
|
||||
}
|
||||
|
||||
public ErasableKey deriveFrameKey(byte[] secret, boolean initiator) {
|
||||
if(initiator) return deriveKey(secret, FRAME, INITIATOR);
|
||||
else return deriveKey(secret, FRAME, RESPONDER);
|
||||
public ErasableKey deriveFrameKey(byte[] secret, long connection,
|
||||
boolean alice, boolean initiator) {
|
||||
if(alice) {
|
||||
if(initiator) return deriveKey(secret, A_FRAME_A, connection);
|
||||
else return deriveKey(secret, A_FRAME_B, connection);
|
||||
} else {
|
||||
if(initiator) return deriveKey(secret, B_FRAME_A, connection);
|
||||
else return deriveKey(secret, B_FRAME_B, connection);
|
||||
}
|
||||
}
|
||||
|
||||
private ErasableKey deriveKey(byte[] secret, byte[] label, byte[] context) {
|
||||
private ErasableKey deriveKey(byte[] secret, byte[] label, long context) {
|
||||
byte[] key = counterModeKdf(secret, label, context);
|
||||
return new ErasableKeyImpl(key, SECRET_KEY_ALGO);
|
||||
}
|
||||
|
||||
// Key derivation function based on a block cipher in CTR mode - see
|
||||
// NIST SP 800-108, section 5.1
|
||||
private byte[] counterModeKdf(byte[] secret, byte[] label, byte[] context) {
|
||||
private byte[] counterModeKdf(byte[] secret, byte[] label, long context) {
|
||||
// The secret must be usable as a key
|
||||
if(secret.length != SECRET_KEY_BYTES)
|
||||
throw new IllegalArgumentException();
|
||||
// The label and context must leave a byte free for the counter
|
||||
if(label.length + context.length + 2 >= KEY_DERIVATION_IV_BYTES)
|
||||
if(label.length + 4 >= KEY_DERIVATION_IV_BYTES)
|
||||
throw new IllegalArgumentException();
|
||||
// The IV contains the length-prefixed label and context
|
||||
byte[] ivBytes = new byte[KEY_DERIVATION_IV_BYTES];
|
||||
ByteUtils.writeUint8(label.length, ivBytes, 0);
|
||||
System.arraycopy(label, 0, ivBytes, 1, label.length);
|
||||
ByteUtils.writeUint8(context.length, ivBytes, label.length + 1);
|
||||
System.arraycopy(context, 0, ivBytes, label.length + 2, context.length);
|
||||
System.arraycopy(label, 0, ivBytes, 0, label.length);
|
||||
ByteUtils.writeUint32(context, ivBytes, label.length);
|
||||
// Use the secret and the IV to encrypt a blank plaintext
|
||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||
ErasableKey key = new ErasableKeyImpl(secret, SECRET_KEY_ALGO);
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(KEY_DERIVATION_ALGO, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] output = cipher.doFinal(KEY_DERIVATION_INPUT);
|
||||
byte[] output = cipher.doFinal(KEY_DERIVATION_BLANK_PLAINTEXT);
|
||||
assert output.length == SECRET_KEY_BYTES;
|
||||
return output;
|
||||
} catch(GeneralSecurityException e) {
|
||||
@@ -131,27 +140,22 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[][] deriveInitialSecrets(byte[] ourPublicKey,
|
||||
byte[] theirPublicKey, PrivateKey ourPrivateKey, int invitationCode,
|
||||
boolean initiator) {
|
||||
public byte[] deriveInitialSecret(byte[] ourPublicKey,
|
||||
byte[] theirPublicKey, PrivateKey ourPrivateKey, boolean alice) {
|
||||
try {
|
||||
PublicKey theirPublic = agreementKeyParser.parsePublicKey(
|
||||
theirPublicKey);
|
||||
MessageDigest messageDigest = getMessageDigest();
|
||||
byte[] ourHash = messageDigest.digest(ourPublicKey);
|
||||
byte[] theirHash = messageDigest.digest(theirPublicKey);
|
||||
// The initiator and responder info are hashes of the public keys
|
||||
byte[] initiatorInfo, responderInfo;
|
||||
if(initiator) {
|
||||
initiatorInfo = ourHash;
|
||||
responderInfo = theirHash;
|
||||
byte[] aliceInfo, bobInfo;
|
||||
if(alice) {
|
||||
aliceInfo = ourHash;
|
||||
bobInfo = theirHash;
|
||||
} else {
|
||||
initiatorInfo = theirHash;
|
||||
responderInfo = ourHash;
|
||||
aliceInfo = theirHash;
|
||||
bobInfo = ourHash;
|
||||
}
|
||||
// The public info is the invitation code as a uint32
|
||||
byte[] publicInfo = new byte[4];
|
||||
ByteUtils.writeUint32(invitationCode, publicInfo, 0);
|
||||
// The raw secret comes from the key agreement algorithm
|
||||
KeyAgreement keyAgreement = KeyAgreement.getInstance(AGREEMENT_ALGO,
|
||||
PROVIDER);
|
||||
@@ -160,17 +164,12 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
byte[] rawSecret = keyAgreement.generateSecret();
|
||||
// Derive the cooked secret from the raw secret using the
|
||||
// concatenation KDF
|
||||
byte[] cookedSecret = concatenationKdf(rawSecret, FIRST,
|
||||
initiatorInfo, responderInfo, publicInfo);
|
||||
byte[] cookedSecret = concatenationKdf(rawSecret, FIRST, aliceInfo,
|
||||
bobInfo);
|
||||
ByteUtils.erase(rawSecret);
|
||||
// Derive the incoming and outgoing secrets from the cooked secret
|
||||
// using the CTR mode KDF
|
||||
byte[][] secrets = new byte[2][];
|
||||
secrets[0] = counterModeKdf(cookedSecret, FIRST, INITIATOR);
|
||||
secrets[1] = counterModeKdf(cookedSecret, FIRST, RESPONDER);
|
||||
ByteUtils.erase(cookedSecret);
|
||||
return secrets;
|
||||
return cookedSecret;
|
||||
} catch(GeneralSecurityException e) {
|
||||
// FIXME: Throw instead of returning null?
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -178,7 +177,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
// Key derivation function based on a hash function - see NIST SP 800-56A,
|
||||
// section 5.8
|
||||
private byte[] concatenationKdf(byte[] rawSecret, byte[] label,
|
||||
byte[] initiatorInfo, byte[] responderInfo, byte[] publicInfo) {
|
||||
byte[] initiatorInfo, byte[] responderInfo) {
|
||||
// The output of the hash function must be long enough to use as a key
|
||||
MessageDigest messageDigest = getMessageDigest();
|
||||
if(messageDigest.getDigestLength() < SECRET_KEY_BYTES)
|
||||
@@ -197,9 +196,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
ByteUtils.writeUint8(responderInfo.length, length, 0);
|
||||
messageDigest.update(length);
|
||||
messageDigest.update(responderInfo);
|
||||
ByteUtils.writeUint8(publicInfo.length, length, 0);
|
||||
messageDigest.update(length);
|
||||
messageDigest.update(publicInfo);
|
||||
byte[] hash = messageDigest.digest();
|
||||
// The secret is the first SECRET_KEY_BYTES bytes of the hash
|
||||
byte[] output = new byte[SECRET_KEY_BYTES];
|
||||
@@ -208,22 +204,28 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
return output;
|
||||
}
|
||||
|
||||
public byte[] deriveNextSecret(byte[] secret, int index, long connection) {
|
||||
if(index < 0 || index > ByteUtils.MAX_16_BIT_UNSIGNED)
|
||||
public byte[] deriveNextSecret(byte[] secret, long period) {
|
||||
if(period < 0 || period > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if(connection < 0 || connection > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
byte[] context = new byte[6];
|
||||
ByteUtils.writeUint16(index, context, 0);
|
||||
ByteUtils.writeUint32(connection, context, 2);
|
||||
return counterModeKdf(secret, NEXT, context);
|
||||
return counterModeKdf(secret, ROTATE, period);
|
||||
}
|
||||
|
||||
public int deriveConfirmationCode(byte[] secret) {
|
||||
byte[] output = counterModeKdf(secret, CODE, CODE);
|
||||
int code = ByteUtils.readUint(output, CODE_BITS);
|
||||
ByteUtils.erase(output);
|
||||
return code;
|
||||
public int generateInvitationCode() {
|
||||
int codeBytes = (int) Math.ceil(CODE_BITS / 8.0);
|
||||
byte[] random = new byte[codeBytes];
|
||||
secureRandom.nextBytes(random);
|
||||
return ByteUtils.readUint(random, CODE_BITS);
|
||||
}
|
||||
|
||||
public int[] deriveConfirmationCodes(byte[] secret) {
|
||||
byte[] alice = counterModeKdf(secret, CODE, 0);
|
||||
byte[] bob = counterModeKdf(secret, CODE, 1);
|
||||
int[] codes = new int[2];
|
||||
codes[0] = ByteUtils.readUint(alice, CODE_BITS);
|
||||
codes[1] = ByteUtils.readUint(bob, CODE_BITS);
|
||||
ByteUtils.erase(alice);
|
||||
ByteUtils.erase(bob);
|
||||
return codes;
|
||||
}
|
||||
|
||||
public KeyPair generateAgreementKeyPair() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.sf.briar.db;
|
||||
package net.sf.briar.crypto;
|
||||
|
||||
import net.sf.briar.api.db.DbException;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.sf.briar.db;
|
||||
package net.sf.briar.crypto;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@@ -5,7 +5,9 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.ContactTransport;
|
||||
import net.sf.briar.api.Rating;
|
||||
import net.sf.briar.api.TemporarySecret;
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
@@ -19,9 +21,6 @@ import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionWindow;
|
||||
|
||||
/**
|
||||
* A low-level interface to the database (DatabaseComponent provides a
|
||||
@@ -81,18 +80,19 @@ interface Database<T> {
|
||||
void addBatchToAck(T txn, ContactId c, BatchId b) throws DbException;
|
||||
|
||||
/**
|
||||
* Adds a new contact to the database with the given secrets and returns an
|
||||
* ID for the contact.
|
||||
* Adds a new contact to the database and returns an ID for the contact.
|
||||
* <p>
|
||||
* Any secrets generated by the method are stored in the given collection
|
||||
* and should be erased by the caller once the transaction has been
|
||||
* committed or aborted.
|
||||
* <p>
|
||||
* Locking: contact write, subscription write, transport write,
|
||||
* window write.
|
||||
* Locking: contact write, subscription write, transport write.
|
||||
*/
|
||||
ContactId addContact(T txn, byte[] inSecret, byte[] outSecret,
|
||||
Collection<byte[]> erase) throws DbException;
|
||||
ContactId addContact(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Adds a contact transport to the database.
|
||||
* <p>
|
||||
* Locking: contact read, window write.
|
||||
*/
|
||||
void addContactTransport(T txn, ContactTransport ct)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns false if the given message is already in the database. Otherwise
|
||||
@@ -118,6 +118,15 @@ interface Database<T> {
|
||||
*/
|
||||
boolean addPrivateMessage(T txn, Message m, ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given temporary secrets and deletes any secrets that have
|
||||
* been made obsolete.
|
||||
* <p>
|
||||
* Locking: contact read, window write.
|
||||
*/
|
||||
void addSecrets(T txn, Collection<TemporarySecret> secrets)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Subscribes to the given group.
|
||||
* <p>
|
||||
@@ -132,15 +141,7 @@ interface Database<T> {
|
||||
* Locking: contact read, subscription write.
|
||||
*/
|
||||
void addSubscription(T txn, ContactId c, Group g, long start)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Allocates and returns a local index for the given transport. Returns
|
||||
* null if all indices have been allocated.
|
||||
* <p>
|
||||
* Locking: transport write.
|
||||
*/
|
||||
TransportIndex addTransport(T txn, TransportId t) throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Makes the given group visible to the given contact.
|
||||
@@ -156,6 +157,14 @@ interface Database<T> {
|
||||
*/
|
||||
boolean containsContact(T txn, ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the database contains the given contact transport.
|
||||
* <p>
|
||||
* Locking: contact read, window read.
|
||||
*/
|
||||
boolean containsContactTransport(T txn, ContactId c, TransportId t)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the database contains the given message.
|
||||
* <p>
|
||||
@@ -177,7 +186,7 @@ interface Database<T> {
|
||||
* Locking: subscription read.
|
||||
*/
|
||||
boolean containsSubscription(T txn, GroupId g, long time)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the user is subscribed to the given group, the group is
|
||||
@@ -196,7 +205,7 @@ interface Database<T> {
|
||||
* Locking: contact read, messageStatus read.
|
||||
*/
|
||||
Collection<BatchId> getBatchesToAck(T txn, ContactId c, int maxBatches)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the configuration for the given transport.
|
||||
@@ -205,28 +214,6 @@ interface Database<T> {
|
||||
*/
|
||||
TransportConfig getConfig(T txn, TransportId t) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns an outgoing connection context for the given contact and
|
||||
* transport.
|
||||
* <p>
|
||||
* Any secrets generated by the method are stored in the given collection
|
||||
* and should be erased by the caller once the transaction has been
|
||||
* committed or aborted.
|
||||
* <p>
|
||||
* Locking: contact read, window write.
|
||||
*/
|
||||
ConnectionContext getConnectionContext(T txn, ContactId c, TransportIndex i,
|
||||
Collection<byte[]> erase) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the connection reordering window for the given contact and
|
||||
* transport.
|
||||
* <p>
|
||||
* Locking: contact read, window read.
|
||||
*/
|
||||
ConnectionWindow getConnectionWindow(T txn, ContactId c, TransportIndex i)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of all contacts.
|
||||
* <p>
|
||||
@@ -234,6 +221,13 @@ interface Database<T> {
|
||||
*/
|
||||
Collection<ContactId> getContacts(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all contact transports.
|
||||
* <p>
|
||||
* Locking: contact read, window read.
|
||||
*/
|
||||
Collection<ContactTransport> getContactTransports(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the approximate expiry time of the database.
|
||||
* <p>
|
||||
@@ -259,21 +253,13 @@ interface Database<T> {
|
||||
*/
|
||||
MessageId getGroupMessageParent(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the local index for the given transport, or null if no index
|
||||
* has been allocated.
|
||||
* <p>
|
||||
* Locking: transport read.
|
||||
*/
|
||||
TransportIndex getLocalIndex(T txn, TransportId t) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the local transport properties for the given transport.
|
||||
* <p>
|
||||
* Locking: transport read.
|
||||
*/
|
||||
TransportProperties getLocalProperties(T txn, TransportId t)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all local transports.
|
||||
@@ -310,7 +296,7 @@ interface Database<T> {
|
||||
* Locking: message read, messageFlag read.
|
||||
*/
|
||||
Collection<MessageHeader> getMessageHeaders(T txn, GroupId g)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the message identified by the given ID, in raw format, or null
|
||||
@@ -321,7 +307,7 @@ interface Database<T> {
|
||||
* subscription read.
|
||||
*/
|
||||
byte[] getMessageIfSendable(T txn, ContactId c, MessageId m)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of all messages signed by the given author.
|
||||
@@ -329,7 +315,7 @@ interface Database<T> {
|
||||
* Locking: message read.
|
||||
*/
|
||||
Collection<MessageId> getMessagesByAuthor(T txn, AuthorId a)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the number of children of the message identified by the given
|
||||
@@ -372,15 +358,6 @@ interface Database<T> {
|
||||
*/
|
||||
boolean getRead(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the given contact's index for the given transport, or null if
|
||||
* the contact does not support the transport.
|
||||
* <p>
|
||||
* Locking: contact read, window read.
|
||||
*/
|
||||
TransportIndex getRemoteIndex(T txn, ContactId c, TransportId t)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all remote properties for the given transport.
|
||||
* <p>
|
||||
@@ -404,7 +381,7 @@ interface Database<T> {
|
||||
* subscription read.
|
||||
*/
|
||||
Collection<MessageId> getSendableMessages(T txn, ContactId c, int capacity)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the given message has been starred.
|
||||
@@ -464,7 +441,7 @@ interface Database<T> {
|
||||
* Locking: contact read, subscription read.
|
||||
*/
|
||||
Map<GroupId, GroupId> getVisibleHoles(T txn, ContactId c, long timestamp)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns any subscriptions that are visible to the given contact,
|
||||
@@ -474,7 +451,7 @@ interface Database<T> {
|
||||
* Locking: contact read, subscription read.
|
||||
*/
|
||||
Map<Group, Long> getVisibleSubscriptions(T txn, ContactId c, long timestamp)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if any messages are sendable to the given contact.
|
||||
@@ -483,6 +460,15 @@ interface Database<T> {
|
||||
*/
|
||||
boolean hasSendableMessages(T txn, ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Increments the outgoing connection counter for the given contact
|
||||
* transport in the given rotation period.
|
||||
* <p>
|
||||
* Locking: contact read, window write.
|
||||
*/
|
||||
void incrementConnectionCounter(T txn, ContactId c, TransportId t,
|
||||
long period) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes an outstanding batch that has been acknowledged. Any messages in
|
||||
* the batch that are still considered outstanding (Status.SENT) with
|
||||
@@ -499,7 +485,7 @@ interface Database<T> {
|
||||
* Locking: contact read, messageStatus write.
|
||||
*/
|
||||
void removeBatchesToAck(T txn, ContactId c, Collection<BatchId> sent)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Removes a contact (and all associated state) from the database.
|
||||
@@ -543,7 +529,7 @@ interface Database<T> {
|
||||
* with IDs greater than the first are removed.
|
||||
*/
|
||||
void removeSubscriptions(T txn, ContactId c, GroupId start, GroupId end)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Makes the given group invisible to the given contact.
|
||||
@@ -559,16 +545,16 @@ interface Database<T> {
|
||||
* Locking: transport write.
|
||||
*/
|
||||
void setConfig(T txn, TransportId t, TransportConfig config)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the connection reordering window for the given contact and
|
||||
* transport.
|
||||
* Sets the connection reordering window for the given contact transport in
|
||||
* the given rotation period.
|
||||
* <p>
|
||||
* Locking: contact read, window write.
|
||||
*/
|
||||
void setConnectionWindow(T txn, ContactId c, TransportIndex i,
|
||||
ConnectionWindow w) throws DbException;
|
||||
void setConnectionWindow(T txn, ContactId c, TransportId t, long period,
|
||||
long centre, byte[] bitmap) throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the given contact's database expiry time.
|
||||
@@ -584,7 +570,7 @@ interface Database<T> {
|
||||
* Locking: transport write.
|
||||
*/
|
||||
void setLocalProperties(T txn, TransportId t, TransportProperties p)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the user's rating for the given author.
|
||||
@@ -622,7 +608,7 @@ interface Database<T> {
|
||||
* Locking: contact read, message read, messageStatus write.
|
||||
*/
|
||||
void setStatus(T txn, ContactId c, MessageId m, Status s)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* If the database contains the given message and it belongs to a group
|
||||
@@ -634,7 +620,7 @@ interface Database<T> {
|
||||
* subscription read.
|
||||
*/
|
||||
boolean setStatusSeenIfVisible(T txn, ContactId c, MessageId m)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Records the time of the latest subscription update acknowledged by the
|
||||
@@ -643,7 +629,7 @@ interface Database<T> {
|
||||
* Locking: contact read, subscription write.
|
||||
*/
|
||||
void setSubscriptionsAcked(T txn, ContactId c, long timestamp)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Records the time of the latest subscription update received from the
|
||||
@@ -652,7 +638,7 @@ interface Database<T> {
|
||||
* Locking: contact read, subscription write.
|
||||
*/
|
||||
void setSubscriptionsReceived(T txn, ContactId c, long timestamp)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the transports for the given contact, replacing any existing
|
||||
@@ -677,5 +663,5 @@ interface Database<T> {
|
||||
* Locking: contact read, transport write.
|
||||
*/
|
||||
void setTransportsSent(T txn, ContactId c, long timestamp)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.ContactTransport;
|
||||
import net.sf.briar.api.Rating;
|
||||
import net.sf.briar.api.TemporarySecret;
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.clock.Clock;
|
||||
@@ -30,6 +32,7 @@ import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
import net.sf.briar.api.db.NoSuchContactException;
|
||||
import net.sf.briar.api.db.NoSuchContactTransportException;
|
||||
import net.sf.briar.api.db.Status;
|
||||
import net.sf.briar.api.db.event.BatchReceivedEvent;
|
||||
import net.sf.briar.api.db.event.ContactAddedEvent;
|
||||
@@ -41,7 +44,6 @@ import net.sf.briar.api.db.event.MessagesAddedEvent;
|
||||
import net.sf.briar.api.db.event.RatingChangedEvent;
|
||||
import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
|
||||
import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
|
||||
import net.sf.briar.api.db.event.TransportAddedEvent;
|
||||
import net.sf.briar.api.lifecycle.ShutdownManager;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
import net.sf.briar.api.protocol.AuthorId;
|
||||
@@ -58,10 +60,7 @@ import net.sf.briar.api.protocol.Request;
|
||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionWindow;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
@@ -76,7 +75,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent,
|
||||
DatabaseCleaner.Callback {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(DatabaseComponentImpl.class.getName());
|
||||
Logger.getLogger(DatabaseComponentImpl.class.getName());
|
||||
|
||||
/*
|
||||
* Locks must always be acquired in alphabetical order. See the Database
|
||||
@@ -84,21 +83,21 @@ DatabaseCleaner.Callback {
|
||||
*/
|
||||
|
||||
private final ReentrantReadWriteLock contactLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
new ReentrantReadWriteLock(true);
|
||||
private final ReentrantReadWriteLock messageLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
new ReentrantReadWriteLock(true);
|
||||
private final ReentrantReadWriteLock messageFlagLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
new ReentrantReadWriteLock(true);
|
||||
private final ReentrantReadWriteLock messageStatusLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
new ReentrantReadWriteLock(true);
|
||||
private final ReentrantReadWriteLock ratingLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
new ReentrantReadWriteLock(true);
|
||||
private final ReentrantReadWriteLock subscriptionLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
new ReentrantReadWriteLock(true);
|
||||
private final ReentrantReadWriteLock transportLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
new ReentrantReadWriteLock(true);
|
||||
private final ReentrantReadWriteLock windowLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
new ReentrantReadWriteLock(true);
|
||||
|
||||
private final Database<T> db;
|
||||
private final DatabaseCleaner cleaner;
|
||||
@@ -107,7 +106,7 @@ DatabaseCleaner.Callback {
|
||||
private final Clock clock;
|
||||
|
||||
private final Collection<DatabaseListener> listeners =
|
||||
new CopyOnWriteArrayList<DatabaseListener>();
|
||||
new CopyOnWriteArrayList<DatabaseListener>();
|
||||
|
||||
private final Object spaceLock = new Object();
|
||||
private long bytesStoredSinceLastCheck = 0L; // Locking: spaceLock
|
||||
@@ -172,8 +171,7 @@ DatabaseCleaner.Callback {
|
||||
listeners.remove(d);
|
||||
}
|
||||
|
||||
public ContactId addContact(byte[] inSecret, byte[] outSecret)
|
||||
throws DbException {
|
||||
public ContactId addContact() throws DbException {
|
||||
ContactId c;
|
||||
Collection<byte[]> erase = new ArrayList<byte[]>();
|
||||
contactLock.writeLock().lock();
|
||||
@@ -186,7 +184,7 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
c = db.addContact(txn, inSecret, outSecret, erase);
|
||||
c = db.addContact(txn);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -266,7 +264,7 @@ DatabaseCleaner.Callback {
|
||||
* @param sender may be null for a locally generated message.
|
||||
*/
|
||||
private boolean storeGroupMessage(T txn, Message m, ContactId sender)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
if(m.getGroup() == null) throw new IllegalArgumentException();
|
||||
boolean stored = db.addGroupMessage(txn, m);
|
||||
// Mark the message as seen by the sender
|
||||
@@ -315,7 +313,7 @@ DatabaseCleaner.Callback {
|
||||
* greater than 0, or false if it has changed from greater than 0 to 0.
|
||||
*/
|
||||
private int updateAncestorSendability(T txn, MessageId m, boolean increment)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
int affected = 0;
|
||||
boolean changed = true;
|
||||
while(changed) {
|
||||
@@ -343,17 +341,18 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public void addLocalPrivateMessage(Message m, ContactId c)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
boolean added = false;
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.writeLock().lock();
|
||||
try {
|
||||
messageStatusLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
added = storePrivateMessage(txn, m, c, false);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
@@ -373,6 +372,36 @@ DatabaseCleaner.Callback {
|
||||
if(added) callListeners(new MessagesAddedEvent());
|
||||
}
|
||||
|
||||
public void addSecrets(Collection<TemporarySecret> secrets)
|
||||
throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
windowLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
Collection<TemporarySecret> relevant =
|
||||
new ArrayList<TemporarySecret>();
|
||||
for(TemporarySecret s : secrets) {
|
||||
ContactId c = s.getContactId();
|
||||
TransportId t = s.getTransportId();
|
||||
if(db.containsContactTransport(txn, c, t))
|
||||
relevant.add(s);
|
||||
}
|
||||
if(!secrets.isEmpty()) db.addSecrets(txn, relevant);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
windowLock.writeLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
contactLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the given message is already in the database, returns false.
|
||||
* Otherwise stores the message and marks it as new or seen with respect to
|
||||
@@ -396,52 +425,16 @@ DatabaseCleaner.Callback {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the database contains the given contact.
|
||||
* <p>
|
||||
* Locking: contact read.
|
||||
*/
|
||||
private boolean containsContact(ContactId c) throws DbException {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
boolean contains = db.containsContact(txn, c);
|
||||
db.commitTransaction(txn);
|
||||
return contains;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public TransportIndex addTransport(TransportId t) throws DbException {
|
||||
TransportIndex i;
|
||||
transportLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
i = db.addTransport(txn, t);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
transportLock.writeLock().unlock();
|
||||
}
|
||||
// Call the listeners outside the lock
|
||||
if(i != null) callListeners(new TransportAddedEvent(t));
|
||||
return i;
|
||||
}
|
||||
|
||||
public Ack generateAck(ContactId c, int maxBatches) throws DbException {
|
||||
Collection<BatchId> acked;
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageStatusLock.readLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
acked = db.getBatchesToAck(txn, c, maxBatches);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
@@ -473,14 +466,13 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public RawBatch generateBatch(ContactId c, int capacity)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
Collection<MessageId> ids;
|
||||
List<byte[]> messages = new ArrayList<byte[]>();
|
||||
RawBatch b;
|
||||
// Get some sendable messages from the database
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
messageStatusLock.readLock().lock();
|
||||
@@ -489,6 +481,8 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
ids = db.getSendableMessages(txn, c, capacity);
|
||||
for(MessageId m : ids) {
|
||||
messages.add(db.getMessage(txn, m));
|
||||
@@ -537,7 +531,6 @@ DatabaseCleaner.Callback {
|
||||
// Get some sendable messages from the database
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
messageStatusLock.readLock().lock();
|
||||
@@ -546,6 +539,8 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
Iterator<MessageId> it = requested.iterator();
|
||||
while(it.hasNext()) {
|
||||
MessageId m = it.next();
|
||||
@@ -595,17 +590,18 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public Offer generateOffer(ContactId c, int maxMessages)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
Collection<MessageId> offered;
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
messageStatusLock.readLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
offered = db.getOfferableMessages(txn, c, maxMessages);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
@@ -625,17 +621,18 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public SubscriptionUpdate generateSubscriptionUpdate(ContactId c)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
Map<GroupId, GroupId> holes;
|
||||
Map<Group, Long> subs;
|
||||
long expiry, timestamp;
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
subscriptionLock.readLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
timestamp = clock.currentTimeMillis() - 1;
|
||||
holes = db.getVisibleHoles(txn, c, timestamp);
|
||||
subs = db.getVisibleSubscriptions(txn, c, timestamp);
|
||||
@@ -661,17 +658,18 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public TransportUpdate generateTransportUpdate(ContactId c)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
boolean due;
|
||||
Collection<Transport> transports;
|
||||
long timestamp;
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
transportLock.readLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
// Work out whether an update is due
|
||||
long modified = db.getTransportsModified(txn);
|
||||
long sent = db.getTransportsSent(txn, c);
|
||||
@@ -723,58 +721,6 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionContext getConnectionContext(ContactId c, TransportIndex i)
|
||||
throws DbException {
|
||||
Collection<byte[]> erase = new ArrayList<byte[]>();
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
windowLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
ConnectionContext ctx =
|
||||
db.getConnectionContext(txn, c, i, erase);
|
||||
db.commitTransaction(txn);
|
||||
return ctx;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
windowLock.writeLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
contactLock.readLock().unlock();
|
||||
// Erase the secrets after committing or aborting the transaction
|
||||
for(byte[] b : erase) ByteUtils.erase(b);
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionWindow getConnectionWindow(ContactId c, TransportIndex i)
|
||||
throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
windowLock.readLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
ConnectionWindow w = db.getConnectionWindow(txn, c, i);
|
||||
db.commitTransaction(txn);
|
||||
return w;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
windowLock.readLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
contactLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<ContactId> getContacts() throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
@@ -792,32 +738,39 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public TransportIndex getLocalIndex(TransportId t) throws DbException {
|
||||
transportLock.readLock().lock();
|
||||
public Collection<ContactTransport> getContactTransports()
|
||||
throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
windowLock.readLock().lock();
|
||||
try {
|
||||
TransportIndex i = db.getLocalIndex(txn, t);
|
||||
db.commitTransaction(txn);
|
||||
return i;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
Collection<ContactTransport> contactTransports =
|
||||
db.getContactTransports(txn);
|
||||
db.commitTransaction(txn);
|
||||
return contactTransports;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
windowLock.readLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
transportLock.readLock().unlock();
|
||||
contactLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public TransportProperties getLocalProperties(TransportId t)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
transportLock.readLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
TransportProperties p = db.getLocalProperties(txn, t);
|
||||
TransportProperties properties = db.getLocalProperties(txn, t);
|
||||
db.commitTransaction(txn);
|
||||
return p;
|
||||
return properties;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
@@ -845,7 +798,7 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public Collection<MessageHeader> getMessageHeaders(GroupId g)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
messageFlagLock.readLock().lock();
|
||||
@@ -853,7 +806,7 @@ DatabaseCleaner.Callback {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
Collection<MessageHeader> headers =
|
||||
db.getMessageHeaders(txn, g);
|
||||
db.getMessageHeaders(txn, g);
|
||||
db.commitTransaction(txn);
|
||||
return headers;
|
||||
} catch(DbException e) {
|
||||
@@ -885,30 +838,6 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public TransportIndex getRemoteIndex(ContactId c, TransportId t)
|
||||
throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
transportLock.readLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
TransportIndex i = db.getRemoteIndex(txn, c, t);
|
||||
db.commitTransaction(txn);
|
||||
return i;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
transportLock.readLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
contactLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<ContactId, TransportProperties> getRemoteProperties(
|
||||
TransportId t) throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
@@ -918,7 +847,7 @@ DatabaseCleaner.Callback {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
Map<ContactId, TransportProperties> properties =
|
||||
db.getRemoteProperties(txn, t);
|
||||
db.getRemoteProperties(txn, t);
|
||||
db.commitTransaction(txn);
|
||||
return properties;
|
||||
} catch(DbException e) {
|
||||
@@ -960,7 +889,7 @@ DatabaseCleaner.Callback {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
Map<GroupId, Integer> counts =
|
||||
db.getUnreadMessageCounts(txn);
|
||||
db.getUnreadMessageCounts(txn);
|
||||
db.commitTransaction(txn);
|
||||
return counts;
|
||||
} catch(DbException e) {
|
||||
@@ -1003,7 +932,6 @@ DatabaseCleaner.Callback {
|
||||
public boolean hasSendableMessages(ContactId c) throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
messageStatusLock.readLock().lock();
|
||||
@@ -1012,6 +940,8 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
boolean has = db.hasSendableMessages(txn, c);
|
||||
db.commitTransaction(txn);
|
||||
return has;
|
||||
@@ -1033,17 +963,41 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public void incrementConnectionCounter(ContactId c, TransportId t,
|
||||
long period) throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
windowLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContactTransport(txn, c, t))
|
||||
throw new NoSuchContactTransportException();
|
||||
db.incrementConnectionCounter(txn, c, t, period);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
}
|
||||
} finally {
|
||||
windowLock.writeLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
contactLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void receiveAck(ContactId c, Ack a) throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
messageStatusLock.writeLock().lock();
|
||||
try {
|
||||
Collection<BatchId> acks = a.getBatchIds();
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
Collection<BatchId> acks = a.getBatchIds();
|
||||
// Mark all messages in acked batches as seen
|
||||
for(BatchId b : acks) db.removeAckedBatch(txn, c, b);
|
||||
// Find any lost batches that need to be retransmitted
|
||||
@@ -1069,7 +1023,6 @@ DatabaseCleaner.Callback {
|
||||
boolean anyAdded = false;
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.writeLock().lock();
|
||||
try {
|
||||
messageStatusLock.writeLock().lock();
|
||||
@@ -1078,6 +1031,8 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
anyAdded = storeMessages(txn, c, b.getMessages());
|
||||
db.addBatchToAck(txn, c, b.getId());
|
||||
db.commitTransaction(txn);
|
||||
@@ -1131,7 +1086,6 @@ DatabaseCleaner.Callback {
|
||||
BitSet request;
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
messageStatusLock.writeLock().lock();
|
||||
@@ -1140,6 +1094,8 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
offered = o.getMessageIds();
|
||||
request = new BitSet(offered.size());
|
||||
Iterator<MessageId> it = offered.iterator();
|
||||
@@ -1171,15 +1127,16 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public void receiveSubscriptionUpdate(ContactId c, SubscriptionUpdate s)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
// Update the contact's subscriptions
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
subscriptionLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
Map<GroupId, GroupId> holes = s.getHoles();
|
||||
for(Entry<GroupId, GroupId> e : holes.entrySet()) {
|
||||
GroupId start = e.getKey(), end = e.getValue();
|
||||
@@ -1208,16 +1165,17 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public void receiveTransportUpdate(ContactId c, TransportUpdate t)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
Collection<Transport> transports;
|
||||
// Update the contact's transport properties
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
transportLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
transports = t.getTransports();
|
||||
db.setTransports(txn, c, transports, t.getTimestamp());
|
||||
db.commitTransaction(txn);
|
||||
@@ -1238,7 +1196,6 @@ DatabaseCleaner.Callback {
|
||||
public void removeContact(ContactId c) throws DbException {
|
||||
contactLock.writeLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.writeLock().lock();
|
||||
try {
|
||||
messageFlagLock.writeLock().lock();
|
||||
@@ -1253,6 +1210,8 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
db.removeContact(txn, c);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
@@ -1285,7 +1244,7 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public void setConfig(TransportId t, TransportConfig c)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
transportLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
@@ -1301,16 +1260,17 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public void setConnectionWindow(ContactId c, TransportIndex i,
|
||||
ConnectionWindow w) throws DbException {
|
||||
public void setConnectionWindow(ContactId c, TransportId t, long period,
|
||||
long centre, byte[] bitmap) throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
windowLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
db.setConnectionWindow(txn, c, i, w);
|
||||
if(!db.containsContactTransport(txn, c, t))
|
||||
throw new NoSuchContactTransportException();
|
||||
db.setConnectionWindow(txn, c, t, period, centre, bitmap);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -1324,7 +1284,7 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public void setLocalProperties(TransportId t, TransportProperties p)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
boolean changed = false;
|
||||
transportLock.writeLock().lock();
|
||||
try {
|
||||
@@ -1378,10 +1338,9 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public void setSeen(ContactId c, Collection<MessageId> seen)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
messageStatusLock.writeLock().lock();
|
||||
@@ -1390,6 +1349,8 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
for(MessageId m : seen) {
|
||||
db.setStatusSeenIfVisible(txn, c, m);
|
||||
}
|
||||
@@ -1421,7 +1382,7 @@ DatabaseCleaner.Callback {
|
||||
* from not good to good, or false if it has changed from good to not good.
|
||||
*/
|
||||
private void updateAuthorSendability(T txn, AuthorId a, boolean increment)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
for(MessageId id : db.getMessagesByAuthor(txn, a)) {
|
||||
int sendability = db.getSendability(txn, id);
|
||||
if(increment) {
|
||||
@@ -1438,7 +1399,7 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
public void setVisibility(GroupId g, Collection<ContactId> visible)
|
||||
throws DbException {
|
||||
throws DbException {
|
||||
List<ContactId> affected = new ArrayList<ContactId>();
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
@@ -1619,4 +1580,8 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void rotateKeys() throws DbException {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ import net.sf.briar.api.db.DatabasePassword;
|
||||
import net.sf.briar.api.lifecycle.ShutdownManager;
|
||||
import net.sf.briar.api.protocol.GroupFactory;
|
||||
import net.sf.briar.api.protocol.PacketFactory;
|
||||
import net.sf.briar.api.transport.ConnectionContextFactory;
|
||||
import net.sf.briar.api.transport.ConnectionWindowFactory;
|
||||
import net.sf.briar.util.BoundedExecutor;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
@@ -50,11 +48,8 @@ public class DatabaseModule extends AbstractModule {
|
||||
@Provides
|
||||
Database<Connection> getDatabase(@DatabaseDirectory File dir,
|
||||
@DatabasePassword Password password, @DatabaseMaxSize long maxSize,
|
||||
ConnectionContextFactory connectionContextFactory,
|
||||
ConnectionWindowFactory connectionWindowFactory,
|
||||
GroupFactory groupFactory, Clock clock) {
|
||||
return new H2Database(dir, password, maxSize, connectionContextFactory,
|
||||
connectionWindowFactory, groupFactory, clock);
|
||||
return new H2Database(dir, password, maxSize, groupFactory, clock);
|
||||
}
|
||||
|
||||
@Provides @Singleton
|
||||
|
||||
@@ -15,8 +15,6 @@ import net.sf.briar.api.db.DatabaseMaxSize;
|
||||
import net.sf.briar.api.db.DatabasePassword;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.protocol.GroupFactory;
|
||||
import net.sf.briar.api.transport.ConnectionContextFactory;
|
||||
import net.sf.briar.api.transport.ConnectionWindowFactory;
|
||||
|
||||
import org.apache.commons.io.FileSystemUtils;
|
||||
|
||||
@@ -39,11 +37,9 @@ class H2Database extends JdbcDatabase {
|
||||
H2Database(@DatabaseDirectory File dir,
|
||||
@DatabasePassword Password password,
|
||||
@DatabaseMaxSize long maxSize,
|
||||
ConnectionContextFactory connectionContextFactory,
|
||||
ConnectionWindowFactory connectionWindowFactory,
|
||||
GroupFactory groupFactory, Clock clock) {
|
||||
super(connectionContextFactory, connectionWindowFactory, groupFactory,
|
||||
clock, HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE);
|
||||
super(groupFactory, clock, HASH_TYPE, BINARY_TYPE, COUNTER_TYPE,
|
||||
SECRET_TYPE);
|
||||
home = new File(dir, "db");
|
||||
this.password = password;
|
||||
url = "jdbc:h2:split:" + home.getPath()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,220 +0,0 @@
|
||||
package net.sf.briar.plugins;
|
||||
|
||||
import static net.sf.briar.api.plugins.InvitationConstants.HASH_LENGTH;
|
||||
import static net.sf.briar.api.plugins.InvitationConstants.INVITATION_TIMEOUT;
|
||||
import static net.sf.briar.api.plugins.InvitationConstants.MAX_CODE;
|
||||
import static net.sf.briar.api.plugins.InvitationConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.KeyPair;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.MessageDigest;
|
||||
import net.sf.briar.api.crypto.PseudoRandom;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.plugins.IncomingInvitationCallback;
|
||||
import net.sf.briar.api.plugins.InvitationCallback;
|
||||
import net.sf.briar.api.plugins.InvitationStarter;
|
||||
import net.sf.briar.api.plugins.OutgoingInvitationCallback;
|
||||
import net.sf.briar.api.plugins.PluginExecutor;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexPlugin;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
class InvitationStarterImpl implements InvitationStarter {
|
||||
|
||||
private static final String TIMED_OUT = "INVITATION_TIMED_OUT";
|
||||
private static final String IO_EXCEPTION = "INVITATION_IO_EXCEPTION";
|
||||
private static final String INVALID_KEY = "INVITATION_INVALID_KEY";
|
||||
private static final String WRONG_CODE = "INVITATION_WRONG_CODE";
|
||||
private static final String DB_EXCEPTION = "INVITATION_DB_EXCEPTION";
|
||||
|
||||
private final Executor pluginExecutor;
|
||||
private final CryptoComponent crypto;
|
||||
private final DatabaseComponent db;
|
||||
private final ReaderFactory readerFactory;
|
||||
private final WriterFactory writerFactory;
|
||||
|
||||
@Inject
|
||||
InvitationStarterImpl(@PluginExecutor Executor pluginExecutor,
|
||||
CryptoComponent crypto, DatabaseComponent db,
|
||||
ReaderFactory readerFactory, WriterFactory writerFactory) {
|
||||
this.pluginExecutor = pluginExecutor;
|
||||
this.crypto = crypto;
|
||||
this.db = db;
|
||||
this.readerFactory = readerFactory;
|
||||
this.writerFactory = writerFactory;
|
||||
}
|
||||
|
||||
public void startIncomingInvitation(DuplexPlugin plugin,
|
||||
IncomingInvitationCallback callback) {
|
||||
pluginExecutor.execute(new IncomingInvitationWorker(plugin, callback));
|
||||
}
|
||||
|
||||
public void startOutgoingInvitation(DuplexPlugin plugin,
|
||||
OutgoingInvitationCallback callback) {
|
||||
pluginExecutor.execute(new OutgoingInvitationWorker(plugin, callback));
|
||||
}
|
||||
|
||||
private abstract class InvitationWorker implements Runnable {
|
||||
|
||||
private final DuplexPlugin plugin;
|
||||
private final InvitationCallback callback;
|
||||
private final boolean initiator;
|
||||
|
||||
protected InvitationWorker(DuplexPlugin plugin,
|
||||
InvitationCallback callback, boolean initiator) {
|
||||
this.plugin = plugin;
|
||||
this.callback = callback;
|
||||
this.initiator = initiator;
|
||||
}
|
||||
|
||||
protected abstract int getInvitationCode();
|
||||
|
||||
public void run() {
|
||||
long end = System.currentTimeMillis() + INVITATION_TIMEOUT;
|
||||
// Use the invitation code to seed the PRNG
|
||||
int code = getInvitationCode();
|
||||
if(code == -1) return; // Cancelled
|
||||
PseudoRandom r = crypto.getPseudoRandom(code);
|
||||
long timeout = end - System.currentTimeMillis();
|
||||
if(timeout <= 0) {
|
||||
callback.showFailure(TIMED_OUT);
|
||||
return;
|
||||
}
|
||||
// Create a connection
|
||||
DuplexTransportConnection conn;
|
||||
if(initiator) conn = plugin.sendInvitation(r, timeout);
|
||||
else conn = plugin.acceptInvitation(r, timeout);
|
||||
if(callback.isCancelled()) {
|
||||
if(conn != null) conn.dispose(false, false);
|
||||
return;
|
||||
}
|
||||
if(conn == null) {
|
||||
callback.showFailure(TIMED_OUT);
|
||||
return;
|
||||
}
|
||||
// Use an ephemeral key pair for key agreement
|
||||
KeyPair ourKeyPair = crypto.generateAgreementKeyPair();
|
||||
MessageDigest messageDigest = crypto.getMessageDigest();
|
||||
byte[] ourKey = ourKeyPair.getPublic().getEncoded();
|
||||
byte[] ourHash = messageDigest.digest(ourKey);
|
||||
byte[] theirKey, theirHash;
|
||||
try {
|
||||
OutputStream out = conn.getOutputStream();
|
||||
Writer writer = writerFactory.createWriter(out);
|
||||
InputStream in = conn.getInputStream();
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
if(initiator) {
|
||||
// Send the public key hash
|
||||
writer.writeBytes(ourHash);
|
||||
out.flush();
|
||||
// Receive the public key hash
|
||||
theirHash = reader.readBytes(HASH_LENGTH);
|
||||
// Send the public key
|
||||
writer.writeBytes(ourKey);
|
||||
out.flush();
|
||||
// Receive the public key
|
||||
theirKey = reader.readBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||
} else {
|
||||
// Receive the public key hash
|
||||
theirHash = reader.readBytes(HASH_LENGTH);
|
||||
// Send the public key hash
|
||||
writer.writeBytes(ourHash);
|
||||
out.flush();
|
||||
// Receive the public key
|
||||
theirKey = reader.readBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||
// Send the public key
|
||||
writer.writeBytes(ourKey);
|
||||
out.flush();
|
||||
}
|
||||
} catch(IOException e) {
|
||||
conn.dispose(true, false);
|
||||
callback.showFailure(IO_EXCEPTION);
|
||||
return;
|
||||
}
|
||||
conn.dispose(false, false);
|
||||
if(callback.isCancelled()) return;
|
||||
// Check that the received hash matches the received key
|
||||
if(!Arrays.equals(theirHash, messageDigest.digest(theirKey))) {
|
||||
callback.showFailure(INVALID_KEY);
|
||||
return;
|
||||
}
|
||||
// Derive the initial shared secrets and the confirmation codes
|
||||
byte[][] secrets = crypto.deriveInitialSecrets(ourKey, theirKey,
|
||||
ourKeyPair.getPrivate(), code, initiator);
|
||||
if(secrets == null) {
|
||||
callback.showFailure(INVALID_KEY);
|
||||
return;
|
||||
}
|
||||
int initCode = crypto.deriveConfirmationCode(secrets[0]);
|
||||
int respCode = crypto.deriveConfirmationCode(secrets[1]);
|
||||
int ourCode = initiator ? initCode : respCode;
|
||||
int theirCode = initiator ? respCode : initCode;
|
||||
// Compare the confirmation codes
|
||||
if(callback.enterConfirmationCode(ourCode) != theirCode) {
|
||||
callback.showFailure(WRONG_CODE);
|
||||
ByteUtils.erase(secrets[0]);
|
||||
ByteUtils.erase(secrets[1]);
|
||||
return;
|
||||
}
|
||||
// Add the contact to the database
|
||||
byte[] inSecret = initiator ? secrets[1] : secrets[0];
|
||||
byte[] outSecret = initiator ? secrets[0] : secrets[1];
|
||||
try {
|
||||
db.addContact(inSecret, outSecret);
|
||||
} catch(DbException e) {
|
||||
callback.showFailure(DB_EXCEPTION);
|
||||
ByteUtils.erase(secrets[0]);
|
||||
ByteUtils.erase(secrets[1]);
|
||||
return;
|
||||
}
|
||||
callback.showSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
private class IncomingInvitationWorker extends InvitationWorker {
|
||||
|
||||
private final IncomingInvitationCallback callback;
|
||||
|
||||
IncomingInvitationWorker(DuplexPlugin plugin,
|
||||
IncomingInvitationCallback callback) {
|
||||
super(plugin, callback, false);
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getInvitationCode() {
|
||||
return callback.enterInvitationCode();
|
||||
}
|
||||
}
|
||||
|
||||
private class OutgoingInvitationWorker extends InvitationWorker {
|
||||
|
||||
private final OutgoingInvitationCallback callback;
|
||||
|
||||
OutgoingInvitationWorker(DuplexPlugin plugin,
|
||||
OutgoingInvitationCallback callback) {
|
||||
super(plugin, callback, true);
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getInvitationCode() {
|
||||
int code = crypto.getSecureRandom().nextInt(MAX_CODE + 1);
|
||||
callback.showInvitationCode(code);
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,6 @@ import net.sf.briar.api.plugins.simplex.SimplexTransportReader;
|
||||
import net.sf.briar.api.plugins.simplex.SimplexTransportWriter;
|
||||
import net.sf.briar.api.protocol.ProtocolConstants;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.transport.ConnectionDispatcher;
|
||||
import net.sf.briar.api.ui.UiCallback;
|
||||
|
||||
@@ -102,14 +101,7 @@ class PluginManagerImpl implements PluginManager {
|
||||
LOG.warning("Duplicate transport ID: " + id);
|
||||
continue;
|
||||
}
|
||||
TransportIndex index = db.getLocalIndex(id);
|
||||
if(index == null) index = db.addTransport(id);
|
||||
if(index == null) {
|
||||
if(LOG.isLoggable(Level.WARNING))
|
||||
LOG.warning("Could not allocate index for ID: " + id);
|
||||
continue;
|
||||
}
|
||||
callback.init(id, index);
|
||||
callback.init(id);
|
||||
plugin.start();
|
||||
simplexPlugins.add(plugin);
|
||||
} catch(ClassCastException e) {
|
||||
@@ -142,14 +134,7 @@ class PluginManagerImpl implements PluginManager {
|
||||
LOG.warning("Duplicate transport ID: " + id);
|
||||
continue;
|
||||
}
|
||||
TransportIndex index = db.getLocalIndex(id);
|
||||
if(index == null) index = db.addTransport(id);
|
||||
if(index == null) {
|
||||
if(LOG.isLoggable(Level.WARNING))
|
||||
LOG.warning("Could not allocate index for ID: " + id);
|
||||
continue;
|
||||
}
|
||||
callback.init(id, index);
|
||||
callback.init(id);
|
||||
plugin.start();
|
||||
duplexPlugins.add(plugin);
|
||||
} catch(ClassCastException e) {
|
||||
@@ -222,12 +207,10 @@ class PluginManagerImpl implements PluginManager {
|
||||
private abstract class PluginCallbackImpl implements PluginCallback {
|
||||
|
||||
protected volatile TransportId id = null;
|
||||
protected volatile TransportIndex index = null;
|
||||
|
||||
protected void init(TransportId id, TransportIndex index) {
|
||||
assert this.id == null && this.index == null;
|
||||
protected void init(TransportId id) {
|
||||
assert this.id == null;
|
||||
this.id = id;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public TransportConfig getConfig() {
|
||||
@@ -320,8 +303,7 @@ class PluginManagerImpl implements PluginManager {
|
||||
}
|
||||
|
||||
public void writerCreated(ContactId c, SimplexTransportWriter w) {
|
||||
assert index != null;
|
||||
dispatcher.dispatchWriter(c, id, index, w);
|
||||
dispatcher.dispatchWriter(c, id, w);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,8 +317,7 @@ class PluginManagerImpl implements PluginManager {
|
||||
|
||||
public void outgoingConnectionCreated(ContactId c,
|
||||
DuplexTransportConnection d) {
|
||||
assert index != null;
|
||||
dispatcher.dispatchOutgoingConnection(c, id, index, d);
|
||||
dispatcher.dispatchOutgoingConnection(c, id, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import javax.mail.internet.MimeMessage;
|
||||
import javax.mail.internet.MimeMultipart;
|
||||
import javax.mail.search.FlagTerm;
|
||||
import javax.mail.util.ByteArrayDataSource;
|
||||
import javax.microedition.io.StreamConnection;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package net.sf.briar.plugins.email;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
|
||||
import javax.activation.DataSource;
|
||||
|
||||
public class PipeDataSource implements DataSource{
|
||||
|
||||
public String getContentType() {
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
public PipedInputStream getInputStream() throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
public PipedOutputStream getOutputStream() throws UnsupportedOperationException {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -148,7 +148,6 @@ class ProtocolWriterImpl implements ProtocolWriter {
|
||||
for(Transport p : t.getTransports()) {
|
||||
w.writeStructId(Types.TRANSPORT);
|
||||
w.writeBytes(p.getId().getBytes());
|
||||
w.writeInt32(p.getIndex().getInt());
|
||||
w.writeMap(p);
|
||||
}
|
||||
w.writeListEnd();
|
||||
|
||||
@@ -15,14 +15,13 @@ import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.protocol.PacketFactory;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.api.protocol.Types;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
import net.sf.briar.api.serial.Consumer;
|
||||
import net.sf.briar.api.serial.CountingConsumer;
|
||||
import net.sf.briar.api.serial.StructReader;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.StructReader;
|
||||
|
||||
class TransportUpdateReader implements StructReader<TransportUpdate> {
|
||||
|
||||
@@ -46,12 +45,10 @@ class TransportUpdateReader implements StructReader<TransportUpdate> {
|
||||
if(transports.size() > MAX_TRANSPORTS) throw new FormatException();
|
||||
long timestamp = r.readInt64();
|
||||
r.removeConsumer(counting);
|
||||
// Check for duplicate IDs or indices
|
||||
// Check for duplicate IDs
|
||||
Set<TransportId> ids = new HashSet<TransportId>();
|
||||
Set<TransportIndex> indices = new HashSet<TransportIndex>();
|
||||
for(Transport t : transports) {
|
||||
if(!ids.add(t.getId())) throw new FormatException();
|
||||
if(!indices.add(t.getIndex())) throw new FormatException();
|
||||
}
|
||||
// Build and return the transport update
|
||||
return packetFactory.createTransportUpdate(transports, timestamp);
|
||||
@@ -65,17 +62,13 @@ class TransportUpdateReader implements StructReader<TransportUpdate> {
|
||||
byte[] b = r.readBytes(UniqueId.LENGTH);
|
||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||
TransportId id = new TransportId(b);
|
||||
// Read the index
|
||||
int i = r.readInt32();
|
||||
if(i < 0 || i >= MAX_TRANSPORTS) throw new FormatException();
|
||||
TransportIndex index = new TransportIndex(i);
|
||||
// Read the properties
|
||||
r.setMaxStringLength(MAX_PROPERTY_LENGTH);
|
||||
Map<String, String> m = r.readMap(String.class, String.class);
|
||||
r.resetMaxStringLength();
|
||||
if(m.size() > MAX_PROPERTIES_PER_TRANSPORT)
|
||||
throw new FormatException();
|
||||
return new Transport(id, index, m);
|
||||
return new Transport(id, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.api.protocol.UnverifiedBatch;
|
||||
import net.sf.briar.api.protocol.VerificationExecutor;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
import net.sf.briar.api.transport.ConnectionReaderFactory;
|
||||
import net.sf.briar.api.transport.ConnectionRegistry;
|
||||
@@ -54,7 +55,7 @@ import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
abstract class DuplexConnection implements DatabaseListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(DuplexConnection.class.getName());
|
||||
Logger.getLogger(DuplexConnection.class.getName());
|
||||
|
||||
private static final Runnable CLOSE = new Runnable() {
|
||||
public void run() {}
|
||||
@@ -66,9 +67,10 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
protected final ConnectionWriterFactory connWriterFactory;
|
||||
protected final ProtocolReaderFactory protoReaderFactory;
|
||||
protected final ProtocolWriterFactory protoWriterFactory;
|
||||
protected final ConnectionContext ctx;
|
||||
protected final DuplexTransportConnection transport;
|
||||
protected final ContactId contactId;
|
||||
protected final TransportId transportId;
|
||||
protected final DuplexTransportConnection transport;
|
||||
|
||||
private final Executor dbExecutor, verificationExecutor;
|
||||
private final AtomicBoolean canSendOffer, disposed;
|
||||
@@ -84,8 +86,8 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
ConnectionReaderFactory connReaderFactory,
|
||||
ConnectionWriterFactory connWriterFactory,
|
||||
ProtocolReaderFactory protoReaderFactory,
|
||||
ProtocolWriterFactory protoWriterFactory, ContactId contactId,
|
||||
TransportId transportId, DuplexTransportConnection transport) {
|
||||
ProtocolWriterFactory protoWriterFactory, ConnectionContext ctx,
|
||||
DuplexTransportConnection transport) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.verificationExecutor = verificationExecutor;
|
||||
this.db = db;
|
||||
@@ -94,19 +96,20 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
this.connWriterFactory = connWriterFactory;
|
||||
this.protoReaderFactory = protoReaderFactory;
|
||||
this.protoWriterFactory = protoWriterFactory;
|
||||
this.contactId = contactId;
|
||||
this.transportId = transportId;
|
||||
this.ctx = ctx;
|
||||
this.transport = transport;
|
||||
contactId = ctx.getContactId();
|
||||
transportId = ctx.getTransportId();
|
||||
canSendOffer = new AtomicBoolean(false);
|
||||
disposed = new AtomicBoolean(false);
|
||||
writerTasks = new LinkedBlockingQueue<Runnable>();
|
||||
}
|
||||
|
||||
protected abstract ConnectionReader createConnectionReader()
|
||||
throws DbException, IOException;
|
||||
throws IOException;
|
||||
|
||||
protected abstract ConnectionWriter createConnectionWriter()
|
||||
throws DbException, IOException;
|
||||
throws IOException;
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof BatchReceivedEvent) {
|
||||
@@ -121,7 +124,7 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
dbExecutor.execute(new GenerateOffer());
|
||||
} else if(e instanceof SubscriptionsUpdatedEvent) {
|
||||
Collection<ContactId> affected =
|
||||
((SubscriptionsUpdatedEvent) e).getAffectedContacts();
|
||||
((SubscriptionsUpdatedEvent) e).getAffectedContacts();
|
||||
if(affected.contains(contactId)) {
|
||||
dbExecutor.execute(new GenerateSubscriptionUpdate());
|
||||
}
|
||||
@@ -176,9 +179,6 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
}
|
||||
// The writer will dispose of the transport
|
||||
writerTasks.add(CLOSE);
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
||||
if(!disposed.getAndSet(true)) transport.dispose(true, true);
|
||||
} catch(IOException e) {
|
||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
||||
if(!disposed.getAndSet(true)) transport.dispose(true, true);
|
||||
@@ -217,9 +217,6 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
writer.flush();
|
||||
writer.close();
|
||||
if(!disposed.getAndSet(true)) transport.dispose(false, true);
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
||||
if(!disposed.getAndSet(true)) transport.dispose(true, true);
|
||||
} catch(InterruptedException e) {
|
||||
if(LOG.isLoggable(Level.INFO))
|
||||
LOG.info("Interrupted while waiting for task");
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package net.sf.briar.protocol.duplex;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.crypto.KeyManager;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
|
||||
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.protocol.VerificationExecutor;
|
||||
import net.sf.briar.api.protocol.duplex.DuplexConnectionFactory;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
@@ -21,8 +23,12 @@ import com.google.inject.Inject;
|
||||
|
||||
class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(DuplexConnectionFactoryImpl.class.getName());
|
||||
|
||||
private final Executor dbExecutor, verificationExecutor;
|
||||
private final DatabaseComponent db;
|
||||
private final KeyManager keyManager;
|
||||
private final ConnectionRegistry connRegistry;
|
||||
private final ConnectionReaderFactory connReaderFactory;
|
||||
private final ConnectionWriterFactory connWriterFactory;
|
||||
@@ -32,14 +38,15 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
|
||||
@Inject
|
||||
DuplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
@VerificationExecutor Executor verificationExecutor,
|
||||
DatabaseComponent db, ConnectionRegistry connRegistry,
|
||||
DatabaseComponent db, KeyManager keyManager,
|
||||
ConnectionRegistry connRegistry,
|
||||
ConnectionReaderFactory connReaderFactory,
|
||||
ConnectionWriterFactory connWriterFactory,
|
||||
ProtocolReaderFactory protoReaderFactory,
|
||||
ProtocolWriterFactory protoWriterFactory) {
|
||||
ProtocolReaderFactory protoReaderFactory, ProtocolWriterFactory protoWriterFactory) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.verificationExecutor = verificationExecutor;
|
||||
this.db = db;
|
||||
this.keyManager = keyManager;
|
||||
this.connRegistry = connRegistry;
|
||||
this.connReaderFactory = connReaderFactory;
|
||||
this.connWriterFactory = connWriterFactory;
|
||||
@@ -47,12 +54,12 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
|
||||
this.protoWriterFactory = protoWriterFactory;
|
||||
}
|
||||
|
||||
public void createIncomingConnection(ConnectionContext ctx, TransportId t,
|
||||
DuplexTransportConnection d) {
|
||||
public void createIncomingConnection(ConnectionContext ctx,
|
||||
DuplexTransportConnection transport) {
|
||||
final DuplexConnection conn = new IncomingDuplexConnection(dbExecutor,
|
||||
verificationExecutor, db, connRegistry, connReaderFactory,
|
||||
connWriterFactory, protoReaderFactory, protoWriterFactory,
|
||||
ctx, t, d);
|
||||
connWriterFactory, protoReaderFactory, protoWriterFactory, ctx,
|
||||
transport);
|
||||
Runnable write = new Runnable() {
|
||||
public void run() {
|
||||
conn.write();
|
||||
@@ -68,11 +75,17 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
|
||||
}
|
||||
|
||||
public void createOutgoingConnection(ContactId c, TransportId t,
|
||||
TransportIndex i, DuplexTransportConnection d) {
|
||||
DuplexTransportConnection transport) {
|
||||
ConnectionContext ctx = keyManager.getConnectionContext(c, t);
|
||||
if(ctx == null) {
|
||||
if(LOG.isLoggable(Level.WARNING))
|
||||
LOG.warning("Could not create outgoing connection context");
|
||||
return;
|
||||
}
|
||||
final DuplexConnection conn = new OutgoingDuplexConnection(dbExecutor,
|
||||
verificationExecutor, db, connRegistry, connReaderFactory,
|
||||
connWriterFactory, protoReaderFactory, protoWriterFactory,
|
||||
c, t, i, d);
|
||||
connWriterFactory, protoReaderFactory, protoWriterFactory, ctx,
|
||||
transport);
|
||||
Runnable write = new Runnable() {
|
||||
public void run() {
|
||||
conn.write();
|
||||
@@ -86,5 +99,4 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
|
||||
};
|
||||
new Thread(read).start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
|
||||
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.VerificationExecutor;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
@@ -28,24 +27,22 @@ class IncomingDuplexConnection extends DuplexConnection {
|
||||
ConnectionWriterFactory connWriterFactory,
|
||||
ProtocolReaderFactory protoReaderFactory,
|
||||
ProtocolWriterFactory protoWriterFactory,
|
||||
ConnectionContext ctx, TransportId transportId,
|
||||
DuplexTransportConnection transport) {
|
||||
ConnectionContext ctx, DuplexTransportConnection transport) {
|
||||
super(dbExecutor, verificationExecutor, db, connRegistry,
|
||||
connReaderFactory, connWriterFactory, protoReaderFactory,
|
||||
protoWriterFactory, ctx.getContactId(), transportId, transport);
|
||||
protoWriterFactory, ctx, transport);
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionReader createConnectionReader() throws IOException {
|
||||
return connReaderFactory.createConnectionReader(
|
||||
transport.getInputStream(), ctx.getSecret(), true);
|
||||
transport.getInputStream(), ctx, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionWriter createConnectionWriter() throws IOException {
|
||||
return connWriterFactory.createConnectionWriter(
|
||||
transport.getOutputStream(), Long.MAX_VALUE, ctx.getSecret(),
|
||||
false);
|
||||
transport.getOutputStream(), Long.MAX_VALUE, ctx, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,11 @@ package net.sf.briar.protocol.duplex;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
|
||||
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.protocol.VerificationExecutor;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
@@ -22,45 +18,28 @@ import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
|
||||
class OutgoingDuplexConnection extends DuplexConnection {
|
||||
|
||||
private final TransportIndex transportIndex;
|
||||
|
||||
private ConnectionContext ctx = null; // Locking: this
|
||||
|
||||
OutgoingDuplexConnection(@DatabaseExecutor Executor dbExecutor,
|
||||
@VerificationExecutor Executor verificationExecutor,
|
||||
DatabaseComponent db, ConnectionRegistry connRegistry,
|
||||
ConnectionReaderFactory connReaderFactory,
|
||||
ConnectionWriterFactory connWriterFactory,
|
||||
ProtocolReaderFactory protoReaderFactory,
|
||||
ProtocolWriterFactory protoWriterFactory, ContactId contactId,
|
||||
TransportId transportId, TransportIndex transportIndex,
|
||||
ProtocolWriterFactory protoWriterFactory, ConnectionContext ctx,
|
||||
DuplexTransportConnection transport) {
|
||||
super(dbExecutor, verificationExecutor, db, connRegistry,
|
||||
connReaderFactory, connWriterFactory, protoReaderFactory,
|
||||
protoWriterFactory, contactId, transportId, transport);
|
||||
this.transportIndex = transportIndex;
|
||||
protoWriterFactory, ctx, transport);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionReader createConnectionReader() throws DbException,
|
||||
IOException {
|
||||
synchronized(this) {
|
||||
if(ctx == null)
|
||||
ctx = db.getConnectionContext(contactId, transportIndex);
|
||||
}
|
||||
protected ConnectionReader createConnectionReader() throws IOException {
|
||||
return connReaderFactory.createConnectionReader(
|
||||
transport.getInputStream(), ctx.getSecret(), false);
|
||||
transport.getInputStream(), ctx, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionWriter createConnectionWriter() throws DbException,
|
||||
IOException {
|
||||
synchronized(this) {
|
||||
if(ctx == null)
|
||||
ctx = db.getConnectionContext(contactId, transportIndex);
|
||||
}
|
||||
protected ConnectionWriter createConnectionWriter() throws IOException {
|
||||
return connWriterFactory.createConnectionWriter(
|
||||
transport.getOutputStream(), Long.MAX_VALUE, ctx.getSecret(),
|
||||
true);
|
||||
transport.getOutputStream(), Long.MAX_VALUE, ctx, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import net.sf.briar.api.transport.ConnectionRegistry;
|
||||
class IncomingSimplexConnection {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(IncomingSimplexConnection.class.getName());
|
||||
Logger.getLogger(IncomingSimplexConnection.class.getName());
|
||||
|
||||
private final Executor dbExecutor, verificationExecutor;
|
||||
private final DatabaseComponent db;
|
||||
@@ -38,16 +38,16 @@ class IncomingSimplexConnection {
|
||||
private final ConnectionReaderFactory connFactory;
|
||||
private final ProtocolReaderFactory protoFactory;
|
||||
private final ConnectionContext ctx;
|
||||
private final TransportId transportId;
|
||||
private final SimplexTransportReader transport;
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
|
||||
IncomingSimplexConnection(@DatabaseExecutor Executor dbExecutor,
|
||||
@VerificationExecutor Executor verificationExecutor,
|
||||
DatabaseComponent db, ConnectionRegistry connRegistry,
|
||||
ConnectionReaderFactory connFactory,
|
||||
ProtocolReaderFactory protoFactory, ConnectionContext ctx,
|
||||
TransportId transportId, SimplexTransportReader transport) {
|
||||
SimplexTransportReader transport) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.verificationExecutor = verificationExecutor;
|
||||
this.db = db;
|
||||
@@ -55,16 +55,16 @@ class IncomingSimplexConnection {
|
||||
this.connFactory = connFactory;
|
||||
this.protoFactory = protoFactory;
|
||||
this.ctx = ctx;
|
||||
this.transportId = transportId;
|
||||
this.transport = transport;
|
||||
contactId = ctx.getContactId();
|
||||
transportId = ctx.getTransportId();
|
||||
}
|
||||
|
||||
void read() {
|
||||
connRegistry.registerConnection(contactId, transportId);
|
||||
try {
|
||||
ConnectionReader conn = connFactory.createConnectionReader(
|
||||
transport.getInputStream(), ctx.getSecret(), true);
|
||||
transport.getInputStream(), ctx, true);
|
||||
InputStream in = conn.getInputStream();
|
||||
ProtocolReader reader = protoFactory.createProtocolReader(in);
|
||||
// Read packets until EOF
|
||||
|
||||
@@ -18,7 +18,6 @@ import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.RawBatch;
|
||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionRegistry;
|
||||
@@ -34,35 +33,32 @@ class OutgoingSimplexConnection {
|
||||
private final ConnectionRegistry connRegistry;
|
||||
private final ConnectionWriterFactory connFactory;
|
||||
private final ProtocolWriterFactory protoFactory;
|
||||
private final ConnectionContext ctx;
|
||||
private final SimplexTransportWriter transport;
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
private final TransportIndex transportIndex;
|
||||
private final SimplexTransportWriter transport;
|
||||
|
||||
OutgoingSimplexConnection(DatabaseComponent db,
|
||||
ConnectionRegistry connRegistry,
|
||||
ConnectionWriterFactory connFactory,
|
||||
ProtocolWriterFactory protoFactory, ContactId contactId,
|
||||
TransportId transportId, TransportIndex transportIndex,
|
||||
ProtocolWriterFactory protoFactory, ConnectionContext ctx,
|
||||
SimplexTransportWriter transport) {
|
||||
this.db = db;
|
||||
this.connRegistry = connRegistry;
|
||||
this.connFactory = connFactory;
|
||||
this.protoFactory = protoFactory;
|
||||
this.contactId = contactId;
|
||||
this.transportId = transportId;
|
||||
this.transportIndex = transportIndex;
|
||||
this.ctx = ctx;
|
||||
this.transport = transport;
|
||||
contactId = ctx.getContactId();
|
||||
transportId = ctx.getTransportId();
|
||||
}
|
||||
|
||||
void write() {
|
||||
connRegistry.registerConnection(contactId, transportId);
|
||||
try {
|
||||
ConnectionContext ctx = db.getConnectionContext(contactId,
|
||||
transportIndex);
|
||||
ConnectionWriter conn = connFactory.createConnectionWriter(
|
||||
transport.getOutputStream(), transport.getCapacity(),
|
||||
ctx.getSecret(), true);
|
||||
ctx, true);
|
||||
OutputStream out = conn.getOutputStream();
|
||||
ProtocolWriter writer = protoFactory.createProtocolWriter(out,
|
||||
transport.shouldFlush());
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package net.sf.briar.protocol.simplex;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.crypto.KeyManager;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.plugins.simplex.SimplexTransportReader;
|
||||
@@ -10,7 +13,6 @@ import net.sf.briar.api.plugins.simplex.SimplexTransportWriter;
|
||||
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.protocol.VerificationExecutor;
|
||||
import net.sf.briar.api.protocol.simplex.SimplexConnectionFactory;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
@@ -22,8 +24,12 @@ import com.google.inject.Inject;
|
||||
|
||||
class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(SimplexConnectionFactoryImpl.class.getName());
|
||||
|
||||
private final Executor dbExecutor, verificationExecutor;
|
||||
private final DatabaseComponent db;
|
||||
private final KeyManager keyManager;
|
||||
private final ConnectionRegistry connRegistry;
|
||||
private final ConnectionReaderFactory connReaderFactory;
|
||||
private final ConnectionWriterFactory connWriterFactory;
|
||||
@@ -33,7 +39,8 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
|
||||
@Inject
|
||||
SimplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
@VerificationExecutor Executor verificationExecutor,
|
||||
DatabaseComponent db, ConnectionRegistry connRegistry,
|
||||
DatabaseComponent db, KeyManager keyManager,
|
||||
ConnectionRegistry connRegistry,
|
||||
ConnectionReaderFactory connReaderFactory,
|
||||
ConnectionWriterFactory connWriterFactory,
|
||||
ProtocolReaderFactory protoReaderFactory,
|
||||
@@ -41,6 +48,7 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.verificationExecutor = verificationExecutor;
|
||||
this.db = db;
|
||||
this.keyManager = keyManager;
|
||||
this.connRegistry = connRegistry;
|
||||
this.connReaderFactory = connReaderFactory;
|
||||
this.connWriterFactory = connWriterFactory;
|
||||
@@ -48,11 +56,10 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
|
||||
this.protoWriterFactory = protoWriterFactory;
|
||||
}
|
||||
|
||||
public void createIncomingConnection(ConnectionContext ctx, TransportId t,
|
||||
SimplexTransportReader r) {
|
||||
public void createIncomingConnection(ConnectionContext ctx, SimplexTransportReader r) {
|
||||
final IncomingSimplexConnection conn = new IncomingSimplexConnection(
|
||||
dbExecutor, verificationExecutor, db, connRegistry,
|
||||
connReaderFactory, protoReaderFactory, ctx, t, r);
|
||||
connReaderFactory, protoReaderFactory, ctx, r);
|
||||
Runnable read = new Runnable() {
|
||||
public void run() {
|
||||
conn.read();
|
||||
@@ -62,10 +69,15 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
|
||||
}
|
||||
|
||||
public void createOutgoingConnection(ContactId c, TransportId t,
|
||||
TransportIndex i, SimplexTransportWriter w) {
|
||||
SimplexTransportWriter w) {
|
||||
ConnectionContext ctx = keyManager.getConnectionContext(c, t);
|
||||
if(ctx == null) {
|
||||
if(LOG.isLoggable(Level.WARNING))
|
||||
LOG.warning("Could not create outgoing connection context");
|
||||
return;
|
||||
}
|
||||
final OutgoingSimplexConnection conn = new OutgoingSimplexConnection(db,
|
||||
connRegistry, connWriterFactory, protoWriterFactory,
|
||||
c, t, i, w);
|
||||
connRegistry, connWriterFactory, protoWriterFactory, ctx, w);
|
||||
Runnable write = new Runnable() {
|
||||
public void run() {
|
||||
conn.write();
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionContextFactory;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
class ConnectionContextFactoryImpl implements ConnectionContextFactory {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
@Inject
|
||||
ConnectionContextFactoryImpl(CryptoComponent crypto) {
|
||||
this.crypto = crypto;
|
||||
}
|
||||
|
||||
public ConnectionContext createConnectionContext(ContactId c,
|
||||
TransportIndex i, long connection, byte[] secret) {
|
||||
return new ConnectionContextImpl(c, i, connection, secret);
|
||||
}
|
||||
|
||||
public ConnectionContext createNextConnectionContext(ContactId c,
|
||||
TransportIndex i, long connection, byte[] previousSecret) {
|
||||
byte[] secret = crypto.deriveNextSecret(previousSecret, i.getInt(),
|
||||
connection);
|
||||
return new ConnectionContextImpl(c, i, connection, secret);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
|
||||
class ConnectionContextImpl implements ConnectionContext {
|
||||
|
||||
private final ContactId contactId;
|
||||
private final TransportIndex transportIndex;
|
||||
private final long connectionNumber;
|
||||
private final byte[] secret;
|
||||
|
||||
ConnectionContextImpl(ContactId contactId, TransportIndex transportIndex,
|
||||
long connectionNumber, byte[] secret) {
|
||||
this.contactId = contactId;
|
||||
this.transportIndex = transportIndex;
|
||||
this.connectionNumber = connectionNumber;
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
public TransportIndex getTransportIndex() {
|
||||
return transportIndex;
|
||||
}
|
||||
|
||||
public long getConnectionNumber() {
|
||||
return connectionNumber;
|
||||
}
|
||||
|
||||
public byte[] getSecret() {
|
||||
return secret;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
|
||||
import net.sf.briar.api.plugins.simplex.SimplexTransportReader;
|
||||
import net.sf.briar.api.plugins.simplex.SimplexTransportWriter;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.protocol.duplex.DuplexConnectionFactory;
|
||||
import net.sf.briar.api.protocol.simplex.SimplexConnectionFactory;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
@@ -31,27 +30,27 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
|
||||
|
||||
private final Executor connExecutor;
|
||||
private final ConnectionRecogniser recogniser;
|
||||
private final SimplexConnectionFactory batchConnFactory;
|
||||
private final DuplexConnectionFactory streamConnFactory;
|
||||
private final SimplexConnectionFactory simplexConnFactory;
|
||||
private final DuplexConnectionFactory duplexConnFactory;
|
||||
|
||||
@Inject
|
||||
ConnectionDispatcherImpl(@IncomingConnectionExecutor Executor connExecutor,
|
||||
ConnectionRecogniser recogniser,
|
||||
SimplexConnectionFactory batchConnFactory,
|
||||
DuplexConnectionFactory streamConnFactory) {
|
||||
SimplexConnectionFactory simplexConnFactory,
|
||||
DuplexConnectionFactory duplexConnFactory) {
|
||||
this.connExecutor = connExecutor;
|
||||
this.recogniser = recogniser;
|
||||
this.batchConnFactory = batchConnFactory;
|
||||
this.streamConnFactory = streamConnFactory;
|
||||
this.simplexConnFactory = simplexConnFactory;
|
||||
this.duplexConnFactory = duplexConnFactory;
|
||||
}
|
||||
|
||||
public void dispatchReader(TransportId t, SimplexTransportReader r) {
|
||||
connExecutor.execute(new DispatchSimplexConnection(t, r));
|
||||
}
|
||||
|
||||
public void dispatchWriter(ContactId c, TransportId t, TransportIndex i,
|
||||
public void dispatchWriter(ContactId c, TransportId t,
|
||||
SimplexTransportWriter w) {
|
||||
batchConnFactory.createOutgoingConnection(c, t, i, w);
|
||||
simplexConnFactory.createOutgoingConnection(c, t, w);
|
||||
}
|
||||
|
||||
public void dispatchIncomingConnection(TransportId t,
|
||||
@@ -60,8 +59,8 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
|
||||
}
|
||||
|
||||
public void dispatchOutgoingConnection(ContactId c, TransportId t,
|
||||
TransportIndex i, DuplexTransportConnection d) {
|
||||
streamConnFactory.createOutgoingConnection(c, t, i, d);
|
||||
DuplexTransportConnection d) {
|
||||
duplexConnFactory.createOutgoingConnection(c, t, d);
|
||||
}
|
||||
|
||||
private byte[] readTag(InputStream in) throws IOException {
|
||||
@@ -91,9 +90,12 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
|
||||
byte[] tag = readTag(transport.getInputStream());
|
||||
ConnectionContext ctx = recogniser.acceptConnection(transportId,
|
||||
tag);
|
||||
if(ctx == null) transport.dispose(false, false);
|
||||
else batchConnFactory.createIncomingConnection(ctx, transportId,
|
||||
transport);
|
||||
if(ctx == null) {
|
||||
transport.dispose(false, false);
|
||||
} else {
|
||||
simplexConnFactory.createIncomingConnection(ctx,
|
||||
transport);
|
||||
}
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
||||
try {
|
||||
@@ -128,9 +130,11 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
|
||||
byte[] tag = readTag(transport.getInputStream());
|
||||
ConnectionContext ctx = recogniser.acceptConnection(transportId,
|
||||
tag);
|
||||
if(ctx == null) transport.dispose(false, false);
|
||||
else streamConnFactory.createIncomingConnection(ctx,
|
||||
transportId, transport);
|
||||
if(ctx == null) {
|
||||
transport.dispose(false, false);
|
||||
} else {
|
||||
duplexConnFactory.createIncomingConnection(ctx, transport);
|
||||
}
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
||||
transport.dispose(true, false);
|
||||
|
||||
@@ -4,14 +4,11 @@ import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import net.sf.briar.api.crypto.AuthenticatedCipher;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
import net.sf.briar.api.transport.ConnectionReaderFactory;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@@ -25,27 +22,14 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
|
||||
}
|
||||
|
||||
public ConnectionReader createConnectionReader(InputStream in,
|
||||
byte[] secret, boolean initiator) {
|
||||
if(initiator) {
|
||||
// Derive the frame key and erase the secret
|
||||
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
|
||||
ByteUtils.erase(secret);
|
||||
// Create a reader for the responder's side of the connection
|
||||
AuthenticatedCipher frameCipher = crypto.getFrameCipher();
|
||||
FrameReader encryption = new IncomingEncryptionLayer(in,
|
||||
frameCipher, frameKey, MAX_FRAME_LENGTH);
|
||||
return new ConnectionReaderImpl(encryption, MAX_FRAME_LENGTH);
|
||||
} else {
|
||||
// Derive the tag and frame keys and erase the secret
|
||||
ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
|
||||
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
|
||||
ByteUtils.erase(secret);
|
||||
// Create a reader for the initiator's side of the connection
|
||||
Cipher tagCipher = crypto.getTagCipher();
|
||||
AuthenticatedCipher frameCipher = crypto.getFrameCipher();
|
||||
FrameReader encryption = new IncomingEncryptionLayer(in, tagCipher,
|
||||
frameCipher, tagKey, frameKey, MAX_FRAME_LENGTH);
|
||||
return new ConnectionReaderImpl(encryption, MAX_FRAME_LENGTH);
|
||||
}
|
||||
ConnectionContext ctx, boolean initiator) {
|
||||
byte[] secret = ctx.getSecret();
|
||||
long connection = ctx.getConnectionNumber();
|
||||
boolean alice = ctx.getAlice();
|
||||
ErasableKey frameKey = crypto.deriveFrameKey(secret, connection, alice,
|
||||
initiator);
|
||||
FrameReader encryption = new IncomingEncryptionLayer(in,
|
||||
crypto.getFrameCipher(), frameKey, MAX_FRAME_LENGTH);
|
||||
return new ConnectionReaderImpl(encryption, MAX_FRAME_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,263 +0,0 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import net.sf.briar.api.Bytes;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.NoSuchContactException;
|
||||
import net.sf.briar.api.db.event.ContactRemovedEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
|
||||
import net.sf.briar.api.db.event.TransportAddedEvent;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionRecogniser;
|
||||
import net.sf.briar.api.transport.ConnectionWindow;
|
||||
import net.sf.briar.api.transport.IncomingConnectionExecutor;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
class ConnectionRecogniserImpl implements ConnectionRecogniser,
|
||||
DatabaseListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ConnectionRecogniserImpl.class.getName());
|
||||
|
||||
private final Executor connExecutor;
|
||||
private final DatabaseComponent db;
|
||||
private final CryptoComponent crypto;
|
||||
private final Cipher tagCipher; // Locking: this
|
||||
private final Set<TransportId> localTransportIds; // Locking: this
|
||||
private final Map<Bytes, Context> expected; // Locking: this
|
||||
|
||||
private boolean initialised = false; // Locking: this
|
||||
|
||||
@Inject
|
||||
ConnectionRecogniserImpl(@IncomingConnectionExecutor Executor connExecutor,
|
||||
DatabaseComponent db, CryptoComponent crypto) {
|
||||
this.connExecutor = connExecutor;
|
||||
this.db = db;
|
||||
this.crypto = crypto;
|
||||
tagCipher = crypto.getTagCipher();
|
||||
localTransportIds = new HashSet<TransportId>();
|
||||
expected = new HashMap<Bytes, Context>();
|
||||
}
|
||||
|
||||
// Package access for testing
|
||||
synchronized boolean isInitialised() {
|
||||
return initialised;
|
||||
}
|
||||
|
||||
// Locking: this
|
||||
private void initialise() throws DbException {
|
||||
assert !initialised;
|
||||
db.addListener(this);
|
||||
Map<Bytes, Context> ivs = new HashMap<Bytes, Context>();
|
||||
Collection<TransportId> transports = new ArrayList<TransportId>();
|
||||
for(Transport t : db.getLocalTransports()) transports.add(t.getId());
|
||||
for(ContactId c : db.getContacts()) {
|
||||
try {
|
||||
for(TransportId t : transports) {
|
||||
TransportIndex i = db.getRemoteIndex(c, t);
|
||||
if(i == null) continue; // Contact doesn't support transport
|
||||
ConnectionWindow w = db.getConnectionWindow(c, i);
|
||||
for(Entry<Long, byte[]> e : w.getUnseen().entrySet()) {
|
||||
Context ctx = new Context(c, t, i, e.getKey());
|
||||
ivs.put(calculateTag(ctx, e.getValue()), ctx);
|
||||
}
|
||||
w.erase();
|
||||
}
|
||||
} catch(NoSuchContactException e) {
|
||||
// The contact was removed - clean up in removeContact()
|
||||
continue;
|
||||
}
|
||||
}
|
||||
localTransportIds.addAll(transports);
|
||||
expected.putAll(ivs);
|
||||
initialised = true;
|
||||
}
|
||||
|
||||
// Locking: this
|
||||
private Bytes calculateTag(Context ctx, byte[] secret) {
|
||||
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
TagEncoder.encodeTag(tag, tagCipher, tagKey);
|
||||
tagKey.erase();
|
||||
return new Bytes(tag);
|
||||
}
|
||||
|
||||
public ConnectionContext acceptConnection(TransportId t, byte[] tag)
|
||||
throws DbException {
|
||||
if(tag.length != TAG_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
synchronized(this) {
|
||||
if(!initialised) initialise();
|
||||
Bytes b = new Bytes(tag);
|
||||
Context ctx = expected.get(b);
|
||||
if(ctx == null || !ctx.transportId.equals(t)) return null;
|
||||
// The IV was expected
|
||||
expected.remove(b);
|
||||
ContactId c = ctx.contactId;
|
||||
TransportIndex i = ctx.transportIndex;
|
||||
long connection = ctx.connection;
|
||||
ConnectionWindow w = null;
|
||||
byte[] secret = null;
|
||||
// Get the secret and update the connection window
|
||||
try {
|
||||
w = db.getConnectionWindow(c, i);
|
||||
secret = w.setSeen(connection);
|
||||
db.setConnectionWindow(c, i, w);
|
||||
} catch(NoSuchContactException e) {
|
||||
// The contact was removed - reject the connection
|
||||
if(w != null) w.erase();
|
||||
if(secret != null) ByteUtils.erase(secret);
|
||||
return null;
|
||||
}
|
||||
// Update the connection window's expected IVs
|
||||
Iterator<Context> it = expected.values().iterator();
|
||||
while(it.hasNext()) {
|
||||
Context ctx1 = it.next();
|
||||
if(ctx1.contactId.equals(c) && ctx1.transportIndex.equals(i))
|
||||
it.remove();
|
||||
}
|
||||
for(Entry<Long, byte[]> e : w.getUnseen().entrySet()) {
|
||||
Context ctx1 = new Context(c, t, i, e.getKey());
|
||||
expected.put(calculateTag(ctx1, e.getValue()), ctx1);
|
||||
}
|
||||
w.erase();
|
||||
return new ConnectionContextImpl(c, i, connection, secret);
|
||||
}
|
||||
}
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof ContactRemovedEvent) {
|
||||
// Remove the expected IVs for the ex-contact
|
||||
final ContactId c = ((ContactRemovedEvent) e).getContactId();
|
||||
connExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
removeContact(c);
|
||||
}
|
||||
});
|
||||
} else if(e instanceof TransportAddedEvent) {
|
||||
// Add the expected IVs for the new transport
|
||||
final TransportId t = ((TransportAddedEvent) e).getTransportId();
|
||||
connExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
addTransport(t);
|
||||
}
|
||||
});
|
||||
} else if(e instanceof RemoteTransportsUpdatedEvent) {
|
||||
// Update the expected IVs for the contact
|
||||
RemoteTransportsUpdatedEvent r = (RemoteTransportsUpdatedEvent) e;
|
||||
final ContactId c = r.getContactId();
|
||||
final Collection<Transport> transports = r.getTransports();
|
||||
connExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
updateContact(c, transports);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void removeContact(ContactId c) {
|
||||
if(!initialised) return;
|
||||
Iterator<Context> it = expected.values().iterator();
|
||||
while(it.hasNext()) if(it.next().contactId.equals(c)) it.remove();
|
||||
}
|
||||
|
||||
private synchronized void addTransport(TransportId t) {
|
||||
if(!initialised) return;
|
||||
Map<Bytes, Context> ivs = new HashMap<Bytes, Context>();
|
||||
try {
|
||||
for(ContactId c : db.getContacts()) {
|
||||
try {
|
||||
TransportIndex i = db.getRemoteIndex(c, t);
|
||||
if(i == null) continue; // Contact doesn't support transport
|
||||
ConnectionWindow w = db.getConnectionWindow(c, i);
|
||||
for(Entry<Long, byte[]> e : w.getUnseen().entrySet()) {
|
||||
Context ctx = new Context(c, t, i, e.getKey());
|
||||
ivs.put(calculateTag(ctx, e.getValue()), ctx);
|
||||
}
|
||||
w.erase();
|
||||
} catch(NoSuchContactException e) {
|
||||
// The contact was removed - clean up in removeContact()
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
||||
return;
|
||||
}
|
||||
localTransportIds.add(t);
|
||||
expected.putAll(ivs);
|
||||
}
|
||||
|
||||
private synchronized void updateContact(ContactId c,
|
||||
Collection<Transport> transports) {
|
||||
if(!initialised) return;
|
||||
// The ID <-> index mappings may have changed, so recalculate everything
|
||||
Map<Bytes, Context> ivs = new HashMap<Bytes, Context>();
|
||||
try {
|
||||
for(Transport transport: transports) {
|
||||
TransportId t = transport.getId();
|
||||
if(!localTransportIds.contains(t)) continue;
|
||||
TransportIndex i = transport.getIndex();
|
||||
ConnectionWindow w = db.getConnectionWindow(c, i);
|
||||
for(Entry<Long, byte[]> e : w.getUnseen().entrySet()) {
|
||||
Context ctx = new Context(c, t, i, e.getKey());
|
||||
ivs.put(calculateTag(ctx, e.getValue()), ctx);
|
||||
}
|
||||
w.erase();
|
||||
}
|
||||
} catch(NoSuchContactException e) {
|
||||
// The contact was removed - clean up in removeContact()
|
||||
return;
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
||||
return;
|
||||
}
|
||||
// Remove the old IVs
|
||||
Iterator<Context> it = expected.values().iterator();
|
||||
while(it.hasNext()) if(it.next().contactId.equals(c)) it.remove();
|
||||
// Store the new IVs
|
||||
expected.putAll(ivs);
|
||||
}
|
||||
|
||||
private static class Context {
|
||||
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
private final TransportIndex transportIndex;
|
||||
private final long connection;
|
||||
|
||||
private Context(ContactId contactId, TransportId transportId,
|
||||
TransportIndex transportIndex, long connection) {
|
||||
this.contactId = contactId;
|
||||
this.transportId = transportId;
|
||||
this.transportIndex = transportIndex;
|
||||
this.connection = connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.transport.ConnectionWindow;
|
||||
import net.sf.briar.api.transport.ConnectionWindowFactory;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
class ConnectionWindowFactoryImpl implements ConnectionWindowFactory {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
@Inject
|
||||
ConnectionWindowFactoryImpl(CryptoComponent crypto) {
|
||||
this.crypto = crypto;
|
||||
}
|
||||
|
||||
public ConnectionWindow createConnectionWindow(TransportIndex i,
|
||||
byte[] secret) {
|
||||
return new ConnectionWindowImpl(crypto, i, secret);
|
||||
}
|
||||
|
||||
public ConnectionWindow createConnectionWindow(TransportIndex i,
|
||||
Map<Long, byte[]> unseen) {
|
||||
return new ConnectionWindowImpl(crypto, i, unseen);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,30 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.CONNECTION_WINDOW_SIZE;
|
||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.transport.ConnectionWindow;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
// This class is not thread-safe
|
||||
class ConnectionWindowImpl implements ConnectionWindow {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final int index;
|
||||
private final Map<Long, byte[]> unseen;
|
||||
private final Set<Long> unseen;
|
||||
|
||||
private long centre;
|
||||
|
||||
ConnectionWindowImpl(CryptoComponent crypto, TransportIndex i,
|
||||
byte[] secret) {
|
||||
this.crypto = crypto;
|
||||
index = i.getInt();
|
||||
unseen = new HashMap<Long, byte[]>();
|
||||
for(long l = 0; l < CONNECTION_WINDOW_SIZE / 2; l++) {
|
||||
secret = crypto.deriveNextSecret(secret, index, l);
|
||||
unseen.put(l, secret);
|
||||
}
|
||||
ConnectionWindowImpl() {
|
||||
unseen = new HashSet<Long>();
|
||||
for(long l = 0; l < CONNECTION_WINDOW_SIZE / 2; l++) unseen.add(l);
|
||||
centre = 0;
|
||||
}
|
||||
|
||||
ConnectionWindowImpl(CryptoComponent crypto, TransportIndex i,
|
||||
Map<Long, byte[]> unseen) {
|
||||
ConnectionWindowImpl(Set<Long> unseen) {
|
||||
long min = Long.MAX_VALUE, max = Long.MIN_VALUE;
|
||||
for(long l : unseen.keySet()) {
|
||||
if(l < 0 || l > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||
for(long l : unseen) {
|
||||
if(l < 0 || l > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if(l < min) min = l;
|
||||
if(l > max) max = l;
|
||||
@@ -44,42 +33,29 @@ class ConnectionWindowImpl implements ConnectionWindow {
|
||||
throw new IllegalArgumentException();
|
||||
centre = max - CONNECTION_WINDOW_SIZE / 2 + 1;
|
||||
for(long l = centre; l <= max; l++) {
|
||||
if(!unseen.containsKey(l)) throw new IllegalArgumentException();
|
||||
if(!unseen.contains(l)) throw new IllegalArgumentException();
|
||||
}
|
||||
this.crypto = crypto;
|
||||
index = i.getInt();
|
||||
this.unseen = unseen;
|
||||
}
|
||||
|
||||
public boolean isSeen(long connection) {
|
||||
return !unseen.containsKey(connection);
|
||||
return !unseen.contains(connection);
|
||||
}
|
||||
|
||||
public byte[] setSeen(long connection) {
|
||||
public void setSeen(long connection) {
|
||||
long bottom = getBottom(centre);
|
||||
long top = getTop(centre);
|
||||
if(connection < bottom || connection > top)
|
||||
throw new IllegalArgumentException();
|
||||
if(!unseen.containsKey(connection))
|
||||
if(!unseen.remove(connection))
|
||||
throw new IllegalArgumentException();
|
||||
if(connection >= centre) {
|
||||
centre = connection + 1;
|
||||
long newBottom = getBottom(centre);
|
||||
long newTop = getTop(centre);
|
||||
for(long l = bottom; l < newBottom; l++) {
|
||||
byte[] expired = unseen.remove(l);
|
||||
if(expired != null) ByteUtils.erase(expired);
|
||||
}
|
||||
byte[] topSecret = unseen.get(top);
|
||||
assert topSecret != null;
|
||||
for(long l = top + 1; l <= newTop; l++) {
|
||||
topSecret = crypto.deriveNextSecret(topSecret, index, l);
|
||||
unseen.put(l, topSecret);
|
||||
}
|
||||
for(long l = bottom; l < newBottom; l++) unseen.remove(l);
|
||||
for(long l = top + 1; l <= newTop; l++) unseen.add(l);
|
||||
}
|
||||
byte[] seen = unseen.remove(connection);
|
||||
assert seen != null;
|
||||
return seen;
|
||||
}
|
||||
|
||||
// Returns the lowest value contained in a window with the given centre
|
||||
@@ -89,15 +65,11 @@ class ConnectionWindowImpl implements ConnectionWindow {
|
||||
|
||||
// Returns the highest value contained in a window with the given centre
|
||||
private static long getTop(long centre) {
|
||||
return Math.min(ByteUtils.MAX_32_BIT_UNSIGNED,
|
||||
return Math.min(MAX_32_BIT_UNSIGNED,
|
||||
centre + CONNECTION_WINDOW_SIZE / 2 - 1);
|
||||
}
|
||||
|
||||
public Map<Long, byte[]> getUnseen() {
|
||||
public Set<Long> getUnseen() {
|
||||
return unseen;
|
||||
}
|
||||
|
||||
public void erase() {
|
||||
for(byte[] secret : unseen.values()) ByteUtils.erase(secret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,11 @@ import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import net.sf.briar.api.crypto.AuthenticatedCipher;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionWriter;
|
||||
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@@ -25,27 +22,21 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
|
||||
}
|
||||
|
||||
public ConnectionWriter createConnectionWriter(OutputStream out,
|
||||
long capacity, byte[] secret, boolean initiator) {
|
||||
long capacity, ConnectionContext ctx, boolean initiator) {
|
||||
byte[] secret = ctx.getSecret();
|
||||
long connection = ctx.getConnectionNumber();
|
||||
boolean alice = ctx.getAlice();
|
||||
ErasableKey frameKey = crypto.deriveFrameKey(secret, connection, alice,
|
||||
initiator);
|
||||
FrameWriter encryption;
|
||||
if(initiator) {
|
||||
// Derive the tag and frame keys and erase the secret
|
||||
ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
|
||||
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
|
||||
ByteUtils.erase(secret);
|
||||
// Create a writer for the initiator's side of the connection
|
||||
Cipher tagCipher = crypto.getTagCipher();
|
||||
AuthenticatedCipher frameCipher = crypto.getFrameCipher();
|
||||
FrameWriter encryption = new OutgoingEncryptionLayer(out, capacity,
|
||||
tagCipher, frameCipher, tagKey, frameKey, MAX_FRAME_LENGTH);
|
||||
return new ConnectionWriterImpl(encryption, MAX_FRAME_LENGTH);
|
||||
encryption = new OutgoingEncryptionLayer(out, capacity,
|
||||
crypto.getFrameCipher(), frameKey, MAX_FRAME_LENGTH,
|
||||
ctx.getTag());
|
||||
} else {
|
||||
// Derive the frame key and erase the secret
|
||||
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
|
||||
ByteUtils.erase(secret);
|
||||
// Create a writer for the responder's side of the connection
|
||||
AuthenticatedCipher frameCipher = crypto.getFrameCipher();
|
||||
FrameWriter encryption = new OutgoingEncryptionLayer(out, capacity,
|
||||
frameCipher, frameKey, MAX_FRAME_LENGTH);
|
||||
return new ConnectionWriterImpl(encryption, MAX_FRAME_LENGTH);
|
||||
encryption = new OutgoingEncryptionLayer(out, capacity,
|
||||
crypto.getFrameCipher(), frameKey, MAX_FRAME_LENGTH);
|
||||
}
|
||||
return new ConnectionWriterImpl(encryption, MAX_FRAME_LENGTH);
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,12 @@ import static net.sf.briar.api.transport.TransportConstants.AAD_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.crypto.AuthenticatedCipher;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
@@ -21,70 +18,29 @@ import net.sf.briar.api.crypto.ErasableKey;
|
||||
class IncomingEncryptionLayer implements FrameReader {
|
||||
|
||||
private final InputStream in;
|
||||
private final Cipher tagCipher;
|
||||
private final AuthenticatedCipher frameCipher;
|
||||
private final ErasableKey tagKey, frameKey;
|
||||
private final ErasableKey frameKey;
|
||||
private final byte[] iv, aad, ciphertext;
|
||||
private final int frameLength;
|
||||
|
||||
private long frameNumber;
|
||||
private boolean readTag, finalFrame;
|
||||
private boolean finalFrame;
|
||||
|
||||
/** Constructor for the initiator's side of a connection. */
|
||||
IncomingEncryptionLayer(InputStream in, Cipher tagCipher,
|
||||
AuthenticatedCipher frameCipher, ErasableKey tagKey,
|
||||
ErasableKey frameKey, int frameLength) {
|
||||
this.in = in;
|
||||
this.tagCipher = tagCipher;
|
||||
this.frameCipher = frameCipher;
|
||||
this.tagKey = tagKey;
|
||||
this.frameKey = frameKey;
|
||||
this.frameLength = frameLength;
|
||||
iv = new byte[IV_LENGTH];
|
||||
aad = new byte[AAD_LENGTH];
|
||||
ciphertext = new byte[frameLength];
|
||||
frameNumber = 0L;
|
||||
readTag = true;
|
||||
finalFrame = false;
|
||||
}
|
||||
|
||||
/** Constructor for the responder's side of a connection. */
|
||||
IncomingEncryptionLayer(InputStream in, AuthenticatedCipher frameCipher,
|
||||
ErasableKey frameKey, int frameLength) {
|
||||
this.in = in;
|
||||
this.frameCipher = frameCipher;
|
||||
this.frameKey = frameKey;
|
||||
this.frameLength = frameLength;
|
||||
tagCipher = null;
|
||||
tagKey = null;
|
||||
iv = new byte[IV_LENGTH];
|
||||
aad = new byte[AAD_LENGTH];
|
||||
ciphertext = new byte[frameLength];
|
||||
frameNumber = 0L;
|
||||
readTag = false;
|
||||
finalFrame = false;
|
||||
}
|
||||
|
||||
public int readFrame(byte[] frame) throws IOException {
|
||||
if(finalFrame) return -1;
|
||||
// Read the tag if required
|
||||
if(readTag) {
|
||||
int offset = 0;
|
||||
try {
|
||||
while(offset < TAG_LENGTH) {
|
||||
int read = in.read(ciphertext, offset, TAG_LENGTH - offset);
|
||||
if(read == -1) throw new EOFException();
|
||||
offset += read;
|
||||
}
|
||||
} catch(IOException e) {
|
||||
frameKey.erase();
|
||||
tagKey.erase();
|
||||
throw e;
|
||||
}
|
||||
if(!TagEncoder.decodeTag(ciphertext, tagCipher, tagKey))
|
||||
throw new FormatException();
|
||||
readTag = false;
|
||||
}
|
||||
// Read the frame
|
||||
int ciphertextLength = 0;
|
||||
try {
|
||||
@@ -96,7 +52,6 @@ class IncomingEncryptionLayer implements FrameReader {
|
||||
}
|
||||
} catch(IOException e) {
|
||||
frameKey.erase();
|
||||
tagKey.erase();
|
||||
throw e;
|
||||
}
|
||||
int plaintextLength = ciphertextLength - MAC_LENGTH;
|
||||
|
||||
@@ -5,41 +5,36 @@ import static net.sf.briar.api.transport.TransportConstants.AAD_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import net.sf.briar.api.crypto.AuthenticatedCipher;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
|
||||
class OutgoingEncryptionLayer implements FrameWriter {
|
||||
|
||||
private final OutputStream out;
|
||||
private final Cipher tagCipher;
|
||||
private final AuthenticatedCipher frameCipher;
|
||||
private final ErasableKey tagKey, frameKey;
|
||||
private final byte[] iv, aad, ciphertext;
|
||||
private final ErasableKey frameKey;
|
||||
private final byte[] tag, iv, aad, ciphertext;
|
||||
private final int frameLength, maxPayloadLength;
|
||||
|
||||
private long capacity, frameNumber;
|
||||
private boolean writeTag;
|
||||
|
||||
/** Constructor for the initiator's side of a connection. */
|
||||
OutgoingEncryptionLayer(OutputStream out, long capacity, Cipher tagCipher,
|
||||
AuthenticatedCipher frameCipher, ErasableKey tagKey,
|
||||
ErasableKey frameKey, int frameLength) {
|
||||
OutgoingEncryptionLayer(OutputStream out, long capacity,
|
||||
AuthenticatedCipher frameCipher, ErasableKey frameKey,
|
||||
int frameLength, byte[] tag) {
|
||||
this.out = out;
|
||||
this.capacity = capacity;
|
||||
this.tagCipher = tagCipher;
|
||||
this.frameCipher = frameCipher;
|
||||
this.tagKey = tagKey;
|
||||
this.frameKey = frameKey;
|
||||
this.frameLength = frameLength;
|
||||
this.tag = tag;
|
||||
maxPayloadLength = frameLength - HEADER_LENGTH - MAC_LENGTH;
|
||||
iv = new byte[IV_LENGTH];
|
||||
aad = new byte[AAD_LENGTH];
|
||||
@@ -57,9 +52,8 @@ class OutgoingEncryptionLayer implements FrameWriter {
|
||||
this.frameCipher = frameCipher;
|
||||
this.frameKey = frameKey;
|
||||
this.frameLength = frameLength;
|
||||
tag = null;
|
||||
maxPayloadLength = frameLength - HEADER_LENGTH - MAC_LENGTH;
|
||||
tagCipher = null;
|
||||
tagKey = null;
|
||||
iv = new byte[IV_LENGTH];
|
||||
aad = new byte[AAD_LENGTH];
|
||||
ciphertext = new byte[frameLength];
|
||||
@@ -75,15 +69,13 @@ class OutgoingEncryptionLayer implements FrameWriter {
|
||||
if(writeTag && finalFrame && payloadLength == 0) return;
|
||||
// Write the tag if required
|
||||
if(writeTag) {
|
||||
TagEncoder.encodeTag(ciphertext, tagCipher, tagKey);
|
||||
try {
|
||||
out.write(ciphertext, 0, TAG_LENGTH);
|
||||
out.write(tag, 0, tag.length);
|
||||
} catch(IOException e) {
|
||||
frameKey.erase();
|
||||
tagKey.erase();
|
||||
throw e;
|
||||
}
|
||||
capacity -= TAG_LENGTH;
|
||||
capacity -= tag.length;
|
||||
writeTag = false;
|
||||
}
|
||||
// Encode the header
|
||||
@@ -117,7 +109,6 @@ class OutgoingEncryptionLayer implements FrameWriter {
|
||||
out.write(ciphertext, 0, ciphertextLength);
|
||||
} catch(IOException e) {
|
||||
frameKey.erase();
|
||||
tagKey.erase();
|
||||
throw e;
|
||||
}
|
||||
capacity -= ciphertextLength;
|
||||
@@ -132,7 +123,7 @@ class OutgoingEncryptionLayer implements FrameWriter {
|
||||
// How many frame numbers can we use?
|
||||
long frameNumbers = MAX_32_BIT_UNSIGNED - frameNumber + 1;
|
||||
// How many full frames do we have space for?
|
||||
long bytes = writeTag ? capacity - TAG_LENGTH : capacity;
|
||||
long bytes = writeTag ? capacity - tag.length : capacity;
|
||||
long fullFrames = bytes / frameLength;
|
||||
// Are we limited by frame numbers or space?
|
||||
if(frameNumbers > fullFrames) {
|
||||
|
||||
@@ -3,12 +3,9 @@ package net.sf.briar.transport;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.sf.briar.api.transport.ConnectionContextFactory;
|
||||
import net.sf.briar.api.transport.ConnectionDispatcher;
|
||||
import net.sf.briar.api.transport.ConnectionReaderFactory;
|
||||
import net.sf.briar.api.transport.ConnectionRecogniser;
|
||||
import net.sf.briar.api.transport.ConnectionRegistry;
|
||||
import net.sf.briar.api.transport.ConnectionWindowFactory;
|
||||
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
import net.sf.briar.api.transport.IncomingConnectionExecutor;
|
||||
|
||||
@@ -18,15 +15,10 @@ public class TransportModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ConnectionContextFactory.class).to(
|
||||
ConnectionContextFactoryImpl.class);
|
||||
bind(ConnectionDispatcher.class).to(ConnectionDispatcherImpl.class);
|
||||
bind(ConnectionReaderFactory.class).to(
|
||||
ConnectionReaderFactoryImpl.class);
|
||||
bind(ConnectionRecogniser.class).to(ConnectionRecogniserImpl.class);
|
||||
bind(ConnectionRegistry.class).toInstance(new ConnectionRegistryImpl());
|
||||
bind(ConnectionWindowFactory.class).to(
|
||||
ConnectionWindowFactoryImpl.class);
|
||||
bind(ConnectionWriterFactory.class).to(
|
||||
ConnectionWriterFactoryImpl.class);
|
||||
// The executor is unbounded, so tasks can be dependent or long-lived
|
||||
|
||||
Reference in New Issue
Block a user