diff --git a/api/net/sf/briar/api/ContactTransport.java b/api/net/sf/briar/api/ContactTransport.java new file mode 100644 index 0000000000000000000000000000000000000000..c1d25893fe099fee92d4c48cc56ac054f2663831 --- /dev/null +++ b/api/net/sf/briar/api/ContactTransport.java @@ -0,0 +1,36 @@ +package net.sf.briar.api; + +import net.sf.briar.api.protocol.TransportId; + +public class ContactTransport extends TemporarySecret { + + private final long epoch, clockDiff, latency; + private final boolean alice; + + public ContactTransport(ContactId contactId, TransportId transportId, + long epoch, long clockDiff, long latency, boolean alice, + long period, byte[] secret, long outgoing, long centre, + byte[] bitmap) { + super(contactId, transportId, period, secret, outgoing, centre, bitmap); + this.epoch = epoch; + this.clockDiff = clockDiff; + this.latency = latency; + this.alice = alice; + } + + public long getEpoch() { + return epoch; + } + + public long getClockDifference() { + return clockDiff; + } + + public long getLatency() { + return latency; + } + + public boolean getAlice() { + return alice; + } +} diff --git a/api/net/sf/briar/api/TemporarySecret.java b/api/net/sf/briar/api/TemporarySecret.java new file mode 100644 index 0000000000000000000000000000000000000000..2ea4f26ccf38a5c0f6cb1ffacb9279fc66e839d3 --- /dev/null +++ b/api/net/sf/briar/api/TemporarySecret.java @@ -0,0 +1,51 @@ +package net.sf.briar.api; + +import net.sf.briar.api.protocol.TransportId; + +public class TemporarySecret { + + protected final ContactId contactId; + protected final TransportId transportId; + protected final long period, outgoing, centre; + protected final byte[] secret, bitmap; + + public TemporarySecret(ContactId contactId, TransportId transportId, + long period, byte[] secret, long outgoing, long centre, + byte[] bitmap) { + this.contactId = contactId; + this.transportId = transportId; + this.period = period; + this.secret = secret; + this.outgoing = outgoing; + this.centre = centre; + this.bitmap = bitmap; + } + + public ContactId getContactId() { + return contactId; + } + + public TransportId getTransportId() { + return transportId; + } + + public long getPeriod() { + return period; + } + + public byte[] getSecret() { + return secret; + } + + public long getOutgoingConnectionCounter() { + return outgoing; + } + + public long getWindowCentre() { + return centre; + } + + public byte[] getWindowBitmap() { + return bitmap; + } +} diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java index 90de9bc4f71abf6a843d230e8b6518998e256e65..83e5da292edb7899e763ebe3a3e649be76bf6423 100644 --- a/api/net/sf/briar/api/crypto/CryptoComponent.java +++ b/api/net/sf/briar/api/crypto/CryptoComponent.java @@ -9,16 +9,49 @@ import javax.crypto.Cipher; public interface CryptoComponent { - ErasableKey deriveTagKey(byte[] secret, boolean initiator); - - ErasableKey deriveFrameKey(byte[] secret, boolean initiator); - - byte[][] deriveInitialSecrets(byte[] ourPublicKey, byte[] theirPublicKey, - PrivateKey ourPrivateKey, int invitationCode, boolean initiator); - - int deriveConfirmationCode(byte[] secret); - - byte[] deriveNextSecret(byte[] secret, int index, long connection); + /** + * Derives a tag key from the given temporary secret. + * @param alice Indicates whether the key is for connections initiated by + * Alice or Bob. + */ + ErasableKey deriveTagKey(byte[] secret, boolean alice); + + /** + * Derives a frame key from the given temporary secret and connection + * number. + * @param alice Indicates whether the key is for a connection initiated by + * Alice or Bob. + * @param initiator Indicates whether the key is for the initiator's or the + * responder's side of the connection. + */ + ErasableKey deriveFrameKey(byte[] secret, long connection, boolean alice, + boolean initiator); + + /** + * Derives an initial shared secret from two public keys and one of the + * corresponding private keys. + * @param alice Indicates whether the private key belongs to Alice or Bob. + */ + byte[] deriveInitialSecret(byte[] ourPublicKey, byte[] theirPublicKey, + PrivateKey ourPrivateKey, boolean alice); + + /** + * Generates a random invitation code. + */ + int generateInvitationCode(); + + /** + * Derives two confirmation codes from the given initial shared secret. The + * first code is for Alice to give to Bob; the second is for Bob to give to + * Alice. + */ + int[] deriveConfirmationCodes(byte[] secret); + + /** + * Derives a temporary secret for the given period from the previous + * period's temporary secret. + */ + byte[] deriveNextSecret(byte[] secret, long period); KeyPair generateAgreementKeyPair(); diff --git a/api/net/sf/briar/api/crypto/KeyManager.java b/api/net/sf/briar/api/crypto/KeyManager.java new file mode 100644 index 0000000000000000000000000000000000000000..7d71eb199dd85d8229fc6f126e53b3c9dacaed08 --- /dev/null +++ b/api/net/sf/briar/api/crypto/KeyManager.java @@ -0,0 +1,15 @@ +package net.sf.briar.api.crypto; + +import net.sf.briar.api.ContactId; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.transport.ConnectionContext; + +public interface KeyManager { + + /** + * Returns a connection context for connecting to the given contact over + * the given transport, or null if the contact does not support the + * transport. + */ + ConnectionContext getConnectionContext(ContactId c, TransportId t); +} diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index 85b35016d697acc5561b74221d1462b0470cae41..2581773ec36f927669361162861dc741d0673ce0 100644 --- a/api/net/sf/briar/api/db/DatabaseComponent.java +++ b/api/net/sf/briar/api/db/DatabaseComponent.java @@ -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.event.DatabaseListener; @@ -22,10 +24,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; /** * Encapsulates the database implementation and exposes high-level operations @@ -50,10 +49,9 @@ public interface DatabaseComponent { void removeListener(DatabaseListener d); /** - * 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. */ - ContactId addContact(byte[] inSecret, byte[] outSecret) throws DbException; + ContactId addContact() throws DbException; /** Adds a locally generated group message to the database. */ void addLocalGroupMessage(Message m) throws DbException; @@ -62,10 +60,10 @@ public interface DatabaseComponent { void addLocalPrivateMessage(Message m, ContactId c) throws DbException; /** - * Allocates and returns a local index for the given transport. Returns - * null if all indices have been allocated. + * Stores the given temporary secrets and deletes any secrets that have + * been made obsolete. */ - TransportIndex addTransport(TransportId t) throws DbException; + void addSecrets(Collection<TemporarySecret> secrets) throws DbException; /** * Generates an acknowledgement for the given contact. Returns null if @@ -101,7 +99,7 @@ public interface DatabaseComponent { * an update is not due. */ SubscriptionUpdate generateSubscriptionUpdate(ContactId c) - throws DbException; + throws DbException; /** * Generates a transport update for the given contact. Returns null if an @@ -112,28 +110,11 @@ public interface DatabaseComponent { /** Returns the configuration for the given transport. */ TransportConfig getConfig(TransportId t) throws DbException; - /** - * Returns an outgoing connection context for the given contact and - * transport. - */ - ConnectionContext getConnectionContext(ContactId c, TransportIndex i) - throws DbException; - - /** - * Returns the connection reordering window for the given contact and - * transport. - */ - ConnectionWindow getConnectionWindow(ContactId c, TransportIndex i) - throws DbException; - /** Returns the IDs of all contacts. */ Collection<ContactId> getContacts() throws DbException; - /** - * Returns the local index for the given transport, or null if no index - * has been allocated. - */ - TransportIndex getLocalIndex(TransportId t) throws DbException; + /** Returns all contact transports. */ + Collection<ContactTransport> getContactTransports() throws DbException; /** Returns the local transport properties for the given transport. */ TransportProperties getLocalProperties(TransportId t) throws DbException; @@ -147,16 +128,9 @@ public interface DatabaseComponent { /** Returns the user's rating for the given author. */ Rating getRating(AuthorId a) throws DbException; - /** - * Returns the given contact's index for the given transport, or null if - * the contact does not support the transport. - */ - TransportIndex getRemoteIndex(ContactId c, TransportId t) - throws DbException; - /** Returns all remote transport properties for the given transport. */ Map<ContactId, TransportProperties> getRemoteProperties(TransportId t) - throws DbException; + throws DbException; /** Returns the set of groups to which the user subscribes. */ Collection<Group> getSubscriptions() throws DbException; @@ -170,6 +144,13 @@ public interface DatabaseComponent { /** Returns true if any messages are sendable to the given contact. */ boolean hasSendableMessages(ContactId c) throws DbException; + /** + * Increments the outgoing connection counter for the given contact + * transport in the given rotation period. + */ + void incrementConnectionCounter(ContactId c, TransportId t, long period) + throws DbException; + /** Processes an acknowledgement from the given contact. */ void receiveAck(ContactId c, Ack a) throws DbException; @@ -188,11 +169,11 @@ public interface DatabaseComponent { /** Processes a subscription update from the given contact. */ void receiveSubscriptionUpdate(ContactId c, SubscriptionUpdate s) - throws DbException; + throws DbException; /** Processes a transport update from the given contact. */ void receiveTransportUpdate(ContactId c, TransportUpdate t) - throws DbException; + throws DbException; /** Removes a contact (and all associated state) from the database. */ void removeContact(ContactId c) throws DbException; @@ -204,18 +185,18 @@ public interface DatabaseComponent { void setConfig(TransportId t, TransportConfig c) 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. */ - void setConnectionWindow(ContactId c, TransportIndex i, - ConnectionWindow w) throws DbException; + void setConnectionWindow(ContactId c, TransportId t, long period, + long centre, byte[] bitmap) throws DbException; /** * Sets the local transport properties for the given transport, replacing * any existing properties for that transport. */ void setLocalProperties(TransportId t, TransportProperties p) - throws DbException; + throws DbException; /** Records the user's rating for the given author. */ void setRating(AuthorId a, Rating r) throws DbException; @@ -228,7 +209,7 @@ public interface DatabaseComponent { * to any other contacts. */ void setVisibility(GroupId g, Collection<ContactId> visible) - throws DbException; + throws DbException; /** Subscribes to the given group. */ void subscribe(Group g) throws DbException; diff --git a/api/net/sf/briar/api/db/NoSuchContactException.java b/api/net/sf/briar/api/db/NoSuchContactException.java index 72b735493fc7020f3128d295ebdb79af04735ab8..8813cafa70687562ac36ac619719cd114c7e56f1 100644 --- a/api/net/sf/briar/api/db/NoSuchContactException.java +++ b/api/net/sf/briar/api/db/NoSuchContactException.java @@ -7,8 +7,4 @@ package net.sf.briar.api.db; public class NoSuchContactException extends DbException { private static final long serialVersionUID = -7048538231308207386L; - - public NoSuchContactException() { - super(); - } } diff --git a/api/net/sf/briar/api/db/NoSuchContactTransportException.java b/api/net/sf/briar/api/db/NoSuchContactTransportException.java new file mode 100644 index 0000000000000000000000000000000000000000..0bbf6828f320848cd9e6689ee0452fde36ecf50d --- /dev/null +++ b/api/net/sf/briar/api/db/NoSuchContactTransportException.java @@ -0,0 +1,10 @@ +package net.sf.briar.api.db; + +/** + * Thrown when a database operation is attempted for a contact transport that + * is not in the database. + */ +public class NoSuchContactTransportException extends DbException { + + private static final long serialVersionUID = -6274982612759573100L; +} diff --git a/api/net/sf/briar/api/db/event/TransportAddedEvent.java b/api/net/sf/briar/api/db/event/TransportAddedEvent.java deleted file mode 100644 index 75156882292478155b0ef2b3c29e30511c5e6f8b..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/db/event/TransportAddedEvent.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.sf.briar.api.db.event; - -import net.sf.briar.api.protocol.TransportId; - -/** An event that is broadcast when a transport is added. */ -public class TransportAddedEvent extends DatabaseEvent { - - private final TransportId transportId; - - public TransportAddedEvent(TransportId transportId) { - this.transportId = transportId; - } - - public TransportId getTransportId() { - return transportId; - } -} diff --git a/api/net/sf/briar/api/plugins/IncomingInvitationCallback.java b/api/net/sf/briar/api/plugins/IncomingInvitationCallback.java deleted file mode 100644 index 64199a59c1e8e43357d0f7d7ff172898fec2e8a0..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/plugins/IncomingInvitationCallback.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.sf.briar.api.plugins; - -public interface IncomingInvitationCallback extends InvitationCallback { - - int enterInvitationCode(); -} diff --git a/api/net/sf/briar/api/plugins/InvitationCallback.java b/api/net/sf/briar/api/plugins/InvitationCallback.java deleted file mode 100644 index fb37cf40dd6b840d805d0f4c750f53218d31611c..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/plugins/InvitationCallback.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.sf.briar.api.plugins; - -public interface InvitationCallback { - - boolean isCancelled(); - - int enterConfirmationCode(int code); - - void showProgress(String... message); - - void showFailure(String... message); - - void showSuccess(); -} diff --git a/api/net/sf/briar/api/plugins/InvitationStarter.java b/api/net/sf/briar/api/plugins/InvitationStarter.java deleted file mode 100644 index 92c9acd65ade2b900bd8ee7808af5b4dc3d54e51..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/plugins/InvitationStarter.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.sf.briar.api.plugins; - -import net.sf.briar.api.plugins.duplex.DuplexPlugin; - -public interface InvitationStarter { - - void startIncomingInvitation(DuplexPlugin plugin, - IncomingInvitationCallback callback); - - void startOutgoingInvitation(DuplexPlugin plugin, - OutgoingInvitationCallback callback); -} diff --git a/api/net/sf/briar/api/plugins/OutgoingInvitationCallback.java b/api/net/sf/briar/api/plugins/OutgoingInvitationCallback.java deleted file mode 100644 index b80e82bc7c3beafd349d3e3deceb7962cebb0fb7..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/plugins/OutgoingInvitationCallback.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.sf.briar.api.plugins; - -public interface OutgoingInvitationCallback extends InvitationCallback { - - void showInvitationCode(int code); -} diff --git a/api/net/sf/briar/api/protocol/Transport.java b/api/net/sf/briar/api/protocol/Transport.java index 37c9ab021c65e35050ece282d044820e4bd4bbfb..707d84fa7ef8e5c95e934b0024ac24fd089f8d51 100644 --- a/api/net/sf/briar/api/protocol/Transport.java +++ b/api/net/sf/briar/api/protocol/Transport.java @@ -8,29 +8,21 @@ public class Transport extends TreeMap<String, String> { private static final long serialVersionUID = 4900420175715429560L; private final TransportId id; - private final TransportIndex index; - public Transport(TransportId id, TransportIndex index, - Map<String, String> p) { + public Transport(TransportId id, Map<String, String> p) { super(p); this.id = id; - this.index = index; } - public Transport(TransportId id, TransportIndex index) { + public Transport(TransportId id) { super(); this.id = id; - this.index = index; } public TransportId getId() { return id; } - public TransportIndex getIndex() { - return index; - } - @Override public int hashCode() { return id.hashCode(); @@ -40,7 +32,7 @@ public class Transport extends TreeMap<String, String> { public boolean equals(Object o) { if(o instanceof Transport) { Transport t = (Transport) o; - return id.equals(t.id) && index.equals(t.index) && super.equals(o); + return id.equals(t.id) && super.equals(o); } return false; } diff --git a/api/net/sf/briar/api/protocol/TransportIndex.java b/api/net/sf/briar/api/protocol/TransportIndex.java deleted file mode 100644 index c167b49c69ac23498bea7870aae293d216a2674d..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/protocol/TransportIndex.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.sf.briar.api.protocol; - - -/** - * Type-safe wrapper for an integer that uniquely identifies a transport plugin - * within the scope of a single node. - */ -public class TransportIndex { - - private final int index; - - public TransportIndex(int index) { - if(index < 0 || index >= ProtocolConstants.MAX_TRANSPORTS) - throw new IllegalArgumentException(); - this.index = index; - } - - public int getInt() { - return index; - } - - @Override - public boolean equals(Object o) { - if(o instanceof TransportIndex) - return index == ((TransportIndex) o).index; - return false; - } - - @Override - public int hashCode() { - return index; - } -} diff --git a/api/net/sf/briar/api/protocol/duplex/DuplexConnectionFactory.java b/api/net/sf/briar/api/protocol/duplex/DuplexConnectionFactory.java index 0d8e1dce2d5ef2f7371a863e7bba2f7a07cf3b38..c7504f376e81ea019e7a8145ed65e66ce0706c5c 100644 --- a/api/net/sf/briar/api/protocol/duplex/DuplexConnectionFactory.java +++ b/api/net/sf/briar/api/protocol/duplex/DuplexConnectionFactory.java @@ -3,14 +3,12 @@ package net.sf.briar.api.protocol.duplex; import net.sf.briar.api.ContactId; import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; import net.sf.briar.api.protocol.TransportId; -import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.ConnectionContext; public interface DuplexConnectionFactory { - void createIncomingConnection(ConnectionContext ctx, TransportId t, - DuplexTransportConnection d); + void createIncomingConnection(ConnectionContext ctx, DuplexTransportConnection d); - void createOutgoingConnection(ContactId c, TransportId t, TransportIndex i, + void createOutgoingConnection(ContactId c, TransportId t, DuplexTransportConnection d); } diff --git a/api/net/sf/briar/api/protocol/simplex/SimplexConnectionFactory.java b/api/net/sf/briar/api/protocol/simplex/SimplexConnectionFactory.java index 63c33a290b7bbb6560a08266155a3313732350a8..fa8d2c021f25de08e604ed0803de38c77d41352f 100644 --- a/api/net/sf/briar/api/protocol/simplex/SimplexConnectionFactory.java +++ b/api/net/sf/briar/api/protocol/simplex/SimplexConnectionFactory.java @@ -4,14 +4,12 @@ import net.sf.briar.api.ContactId; 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.transport.ConnectionContext; public interface SimplexConnectionFactory { - void createIncomingConnection(ConnectionContext ctx, TransportId t, - SimplexTransportReader r); + void createIncomingConnection(ConnectionContext ctx, SimplexTransportReader r); - void createOutgoingConnection(ContactId c, TransportId t, TransportIndex i, + void createOutgoingConnection(ContactId c, TransportId t, SimplexTransportWriter w); } diff --git a/api/net/sf/briar/api/transport/ConnectionContext.java b/api/net/sf/briar/api/transport/ConnectionContext.java index 1a5dbd551954a4c08197a9177e60837a99494403..f9f547a4fdfbc4889acadb4b81416f4bd1d80db8 100644 --- a/api/net/sf/briar/api/transport/ConnectionContext.java +++ b/api/net/sf/briar/api/transport/ConnectionContext.java @@ -1,15 +1,47 @@ package net.sf.briar.api.transport; import net.sf.briar.api.ContactId; -import net.sf.briar.api.protocol.TransportIndex; +import net.sf.briar.api.protocol.TransportId; -public interface ConnectionContext { +public class ConnectionContext { - ContactId getContactId(); + private final ContactId contactId; + private final TransportId transportId; + private final byte[] tag, secret; + private final long connection; + private final boolean alice; - TransportIndex getTransportIndex(); + public ConnectionContext(ContactId contactId, TransportId transportId, + byte[] tag, byte[] secret, long connection, boolean alice) { + this.contactId = contactId; + this.transportId = transportId; + this.tag = tag; + this.secret = secret; + this.connection = connection; + this.alice = alice; + } - long getConnectionNumber(); + public ContactId getContactId() { + return contactId; + } - byte[] getSecret(); + public TransportId getTransportId() { + return transportId; + } + + public byte[] getTag() { + return tag; + } + + public byte[] getSecret() { + return secret; + } + + public long getConnectionNumber() { + return connection; + } + + public boolean getAlice() { + return alice; + } } diff --git a/api/net/sf/briar/api/transport/ConnectionContextFactory.java b/api/net/sf/briar/api/transport/ConnectionContextFactory.java deleted file mode 100644 index 374a357ef8515b38757d1c2903e2a9c96da1c4e4..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/transport/ConnectionContextFactory.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.sf.briar.api.transport; - -import net.sf.briar.api.ContactId; -import net.sf.briar.api.protocol.TransportIndex; - -public interface ConnectionContextFactory { - - ConnectionContext createConnectionContext(ContactId c, TransportIndex i, - long connection, byte[] secret); - - ConnectionContext createNextConnectionContext(ContactId c, TransportIndex i, - long connection, byte[] previousSecret); -} diff --git a/api/net/sf/briar/api/transport/ConnectionDispatcher.java b/api/net/sf/briar/api/transport/ConnectionDispatcher.java index 4d2e144136511e1c186faec36b83521b932ab40e..4e7dbd793dab7a8bc2cc71557258d33f8d8c07b8 100644 --- a/api/net/sf/briar/api/transport/ConnectionDispatcher.java +++ b/api/net/sf/briar/api/transport/ConnectionDispatcher.java @@ -5,17 +5,16 @@ 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; public interface ConnectionDispatcher { void dispatchReader(TransportId t, SimplexTransportReader r); - void dispatchWriter(ContactId c, TransportId t, TransportIndex i, + void dispatchWriter(ContactId c, TransportId t, SimplexTransportWriter w); void dispatchIncomingConnection(TransportId t, DuplexTransportConnection d); void dispatchOutgoingConnection(ContactId c, TransportId t, - TransportIndex i, DuplexTransportConnection d); + DuplexTransportConnection d); } diff --git a/api/net/sf/briar/api/transport/ConnectionReaderFactory.java b/api/net/sf/briar/api/transport/ConnectionReaderFactory.java index c688ca9852b5be11c35830c60195e7473b98af1a..171a594bd92f6b85fea7f534b4cfbb62f4643a18 100644 --- a/api/net/sf/briar/api/transport/ConnectionReaderFactory.java +++ b/api/net/sf/briar/api/transport/ConnectionReaderFactory.java @@ -8,6 +8,6 @@ public interface ConnectionReaderFactory { * Creates a connection reader for a simplex connection or one side of a * duplex connection. The secret is erased before this method returns. */ - ConnectionReader createConnectionReader(InputStream in, byte[] secret, - boolean initiator); + ConnectionReader createConnectionReader(InputStream in, + ConnectionContext ctx, boolean initiator); } diff --git a/api/net/sf/briar/api/transport/ConnectionWindow.java b/api/net/sf/briar/api/transport/ConnectionWindow.java index 794846fcd408a7b15e6890766f0100ae29872eda..e2ecd949033c134251a9305d123e03f803b7a083 100644 --- a/api/net/sf/briar/api/transport/ConnectionWindow.java +++ b/api/net/sf/briar/api/transport/ConnectionWindow.java @@ -1,14 +1,12 @@ package net.sf.briar.api.transport; -import java.util.Map; +import java.util.Set; public interface ConnectionWindow { boolean isSeen(long connection); - byte[] setSeen(long connection); + void setSeen(long connection); - Map<Long, byte[]> getUnseen(); - - void erase(); + Set<Long> getUnseen(); } diff --git a/api/net/sf/briar/api/transport/ConnectionWindowFactory.java b/api/net/sf/briar/api/transport/ConnectionWindowFactory.java deleted file mode 100644 index 110df0e99cc206df3d9bb26c422043ca1ef9c728..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/transport/ConnectionWindowFactory.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.sf.briar.api.transport; - -import java.util.Map; - -import net.sf.briar.api.protocol.TransportIndex; - -public interface ConnectionWindowFactory { - - ConnectionWindow createConnectionWindow(TransportIndex i, byte[] secret); - - ConnectionWindow createConnectionWindow(TransportIndex i, - Map<Long, byte[]> unseen); -} diff --git a/api/net/sf/briar/api/transport/ConnectionWriterFactory.java b/api/net/sf/briar/api/transport/ConnectionWriterFactory.java index b5996f3f5f9a6d9e799349537579410a3b5488f3..f2fd8aaf0727e9909fa1f7bd8adccb2e0ce1c6b8 100644 --- a/api/net/sf/briar/api/transport/ConnectionWriterFactory.java +++ b/api/net/sf/briar/api/transport/ConnectionWriterFactory.java @@ -9,5 +9,5 @@ public interface ConnectionWriterFactory { * duplex connection. The secret is erased before this method returns. */ ConnectionWriter createConnectionWriter(OutputStream out, long capacity, - byte[] secret, boolean initiator); + ConnectionContext ctx, boolean initiator); } diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java index 8436319d220c426191180d0421f528a0f30bcd70..58a7a1e5cba835c5ad149e929da1303866b4d49b 100644 --- a/components/net/sf/briar/crypto/CryptoComponentImpl.java +++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java @@ -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) - throw new IllegalArgumentException(); - if(connection < 0 || connection > ByteUtils.MAX_32_BIT_UNSIGNED) + public byte[] deriveNextSecret(byte[] secret, long period) { + if(period < 0 || period > 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 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 deriveConfirmationCode(byte[] secret) { - byte[] output = counterModeKdf(secret, CODE, CODE); - int code = ByteUtils.readUint(output, CODE_BITS); - ByteUtils.erase(output); - return code; + 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() { diff --git a/components/net/sf/briar/db/KeyRotator.java b/components/net/sf/briar/crypto/KeyRotator.java similarity index 94% rename from components/net/sf/briar/db/KeyRotator.java rename to components/net/sf/briar/crypto/KeyRotator.java index 200a1ed24f1f3f9b1f074aa3ebda8954558b0796..71dd43d7e2028fe3b31cab8d578f3242c86e21b4 100644 --- a/components/net/sf/briar/db/KeyRotator.java +++ b/components/net/sf/briar/crypto/KeyRotator.java @@ -1,4 +1,4 @@ -package net.sf.briar.db; +package net.sf.briar.crypto; import net.sf.briar.api.db.DbException; diff --git a/components/net/sf/briar/db/KeyRotatorImpl.java b/components/net/sf/briar/crypto/KeyRotatorImpl.java similarity index 97% rename from components/net/sf/briar/db/KeyRotatorImpl.java rename to components/net/sf/briar/crypto/KeyRotatorImpl.java index 26db0228b3b5392ba856b6b99135a16e550344f9..0d653561fc9fa4d95a2abbf92c2368143168907a 100644 --- a/components/net/sf/briar/db/KeyRotatorImpl.java +++ b/components/net/sf/briar/crypto/KeyRotatorImpl.java @@ -1,4 +1,4 @@ -package net.sf.briar.db; +package net.sf.briar.crypto; import java.util.Timer; import java.util.TimerTask; diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java index cc6be9b2fbd03aa4d78f4ae4b556251a3f44278f..75a6bd15eb291db251bf4182ce945075c2abde0a 100644 --- a/components/net/sf/briar/db/Database.java +++ b/components/net/sf/briar/db/Database.java @@ -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. + * Locking: contact write, subscription write, transport write. + */ + ContactId addContact(T txn) throws DbException; + + /** + * Adds a contact transport to the database. * <p> - * Locking: contact write, subscription write, transport write, - * window write. + * Locking: contact read, window write. */ - ContactId addContact(T txn, byte[] inSecret, byte[] outSecret, - Collection<byte[]> erase) throws DbException; + 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. @@ -206,33 +215,18 @@ 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. + * Returns the IDs of all contacts. * <p> - * Locking: contact read, window write. + * Locking: contact read. */ - ConnectionContext getConnectionContext(T txn, ContactId c, TransportIndex i, - Collection<byte[]> erase) throws DbException; + Collection<ContactId> getContacts(T txn) throws DbException; /** - * Returns the connection reordering window for the given contact and - * transport. + * Returns all contact transports. * <p> * Locking: contact read, window read. */ - ConnectionWindow getConnectionWindow(T txn, ContactId c, TransportIndex i) - throws DbException; - - /** - * Returns the IDs of all contacts. - * <p> - * Locking: contact read. - */ - Collection<ContactId> getContacts(T txn) throws DbException; + Collection<ContactTransport> getContactTransports(T txn) throws DbException; /** * Returns the approximate expiry time of the database. @@ -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; } diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index ebe17a3097950d204503744ee12830df68567ebc..5828b36a7daf4d0382f9bf31518117f0ebb8441b 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -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,46 +721,35 @@ DatabaseCleaner.Callback { } } - public ConnectionContext getConnectionContext(ContactId c, TransportIndex i) - throws DbException { - Collection<byte[]> erase = new ArrayList<byte[]>(); + public Collection<ContactId> getContacts() throws DbException { contactLock.readLock().lock(); try { - if(!containsContact(c)) throw new NoSuchContactException(); - windowLock.writeLock().lock(); + T txn = db.startTransaction(); 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(); + Collection<ContactId> contacts = db.getContacts(txn); + db.commitTransaction(txn); + return contacts; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } 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 { + public Collection<ContactTransport> getContactTransports() + 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); + Collection<ContactTransport> contactTransports = + db.getContactTransports(txn); db.commitTransaction(txn); - return w; + return contactTransports; } catch(DbException e) { db.abortTransaction(txn); throw e; @@ -775,49 +762,15 @@ DatabaseCleaner.Callback { } } - public Collection<ContactId> getContacts() throws DbException { - contactLock.readLock().lock(); - try { - T txn = db.startTransaction(); - try { - Collection<ContactId> contacts = db.getContacts(txn); - db.commitTransaction(txn); - return contacts; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - contactLock.readLock().unlock(); - } - } - - public TransportIndex getLocalIndex(TransportId t) throws DbException { - transportLock.readLock().lock(); - try { - T txn = db.startTransaction(); - try { - TransportIndex i = db.getLocalIndex(txn, t); - db.commitTransaction(txn); - return i; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - transportLock.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 { + + } } diff --git a/components/net/sf/briar/db/DatabaseModule.java b/components/net/sf/briar/db/DatabaseModule.java index bb51aa89c0c34495084be456c6519c409a91a9a8..7532896e281e0db25e065b8a79ad70da63536840 100644 --- a/components/net/sf/briar/db/DatabaseModule.java +++ b/components/net/sf/briar/db/DatabaseModule.java @@ -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 diff --git a/components/net/sf/briar/db/H2Database.java b/components/net/sf/briar/db/H2Database.java index ed1dd1d47e27fd6cba951f61e62206d8e90a468d..fb91b6696513654c370bc671953728e6cb49143e 100644 --- a/components/net/sf/briar/db/H2Database.java +++ b/components/net/sf/briar/db/H2Database.java @@ -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() diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java index b3a42ac5e5b3dc78b541e94b95b1139b9df3af31..fde56a67e6053464df5168e61a5bd5ecbc4bc48b 100644 --- a/components/net/sf/briar/db/JdbcDatabase.java +++ b/components/net/sf/briar/db/JdbcDatabase.java @@ -1,5 +1,8 @@ package net.sf.briar.db; +import static net.sf.briar.db.DatabaseConstants.EXPIRY_MODULUS; +import static net.sf.briar.db.DatabaseConstants.RETRANSMIT_THRESHOLD; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -21,7 +24,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; @@ -35,14 +40,8 @@ import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.ProtocolConstants; 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.ConnectionContextFactory; -import net.sf.briar.api.transport.ConnectionWindow; -import net.sf.briar.api.transport.ConnectionWindowFactory; import net.sf.briar.util.FileUtils; /** @@ -52,248 +51,232 @@ import net.sf.briar.util.FileUtils; abstract class JdbcDatabase implements Database<Connection> { private static final String CREATE_SUBSCRIPTIONS = - "CREATE TABLE subscriptions" - + " (groupId HASH NOT NULL," - + " groupName VARCHAR NOT NULL," - + " groupKey BINARY," // Null for unrestricted groups - + " start BIGINT NOT NULL," - + " PRIMARY KEY (groupId))"; + "CREATE TABLE subscriptions" + + " (groupId HASH NOT NULL," + + " groupName VARCHAR NOT NULL," + + " groupKey BINARY," // Null for unrestricted groups + + " start BIGINT NOT NULL," + + " PRIMARY KEY (groupId))"; private static final String CREATE_CONTACTS = - "CREATE TABLE contacts" - + " (contactId COUNTER," - + " PRIMARY KEY (contactId))"; + "CREATE TABLE contacts" + + " (contactId COUNTER," + + " PRIMARY KEY (contactId))"; private static final String CREATE_MESSAGES = - "CREATE TABLE messages" - + " (messageId HASH NOT NULL," - + " parentId HASH," // Null for the first message in a thread - + " groupId HASH," // Null for private messages - + " authorId HASH," // Null for private or anonymous messages - + " subject VARCHAR NOT NULL," - + " timestamp BIGINT NOT NULL," - + " length INT NOT NULL," - + " bodyStart INT NOT NULL," - + " bodyLength INT NOT NULL," - + " raw BLOB NOT NULL," - + " sendability INT," // Null for private messages - + " contactId INT," // Null for group messages - + " PRIMARY KEY (messageId)," - + " FOREIGN KEY (groupId) REFERENCES subscriptions (groupId)" - + " ON DELETE CASCADE," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE messages" + + " (messageId HASH NOT NULL," + + " parentId HASH," // Null for the first message in a thread + + " groupId HASH," // Null for private messages + + " authorId HASH," // Null for private or anonymous messages + + " subject VARCHAR NOT NULL," + + " timestamp BIGINT NOT NULL," + + " length INT NOT NULL," + + " bodyStart INT NOT NULL," + + " bodyLength INT NOT NULL," + + " raw BLOB NOT NULL," + + " sendability INT," // Null for private messages + + " contactId INT," // Null for group messages + + " PRIMARY KEY (messageId)," + + " FOREIGN KEY (groupId) REFERENCES subscriptions (groupId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; private static final String INDEX_MESSAGES_BY_PARENT = - "CREATE INDEX messagesByParent ON messages (parentId)"; + "CREATE INDEX messagesByParent ON messages (parentId)"; private static final String INDEX_MESSAGES_BY_AUTHOR = - "CREATE INDEX messagesByAuthor ON messages (authorId)"; + "CREATE INDEX messagesByAuthor ON messages (authorId)"; private static final String INDEX_MESSAGES_BY_TIMESTAMP = - "CREATE INDEX messagesByTimestamp ON messages (timestamp)"; + "CREATE INDEX messagesByTimestamp ON messages (timestamp)"; private static final String INDEX_MESSAGES_BY_SENDABILITY = - "CREATE INDEX messagesBySendability ON messages (sendability)"; + "CREATE INDEX messagesBySendability ON messages (sendability)"; private static final String CREATE_VISIBILITIES = - "CREATE TABLE visibilities" - + " (contactId INT NOT NULL," - + " groupId HASH," // Null for the head of the linked list - + " nextId HASH," // Null for the tail of the linked list - + " deleted BIGINT NOT NULL," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE," - + " FOREIGN KEY (groupId) REFERENCES subscriptions (groupId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE visibilities" + + " (contactId INT NOT NULL," + + " groupId HASH," // Null for the head of the linked list + + " nextId HASH," // Null for the tail of the linked list + + " deleted BIGINT NOT NULL," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (groupId) REFERENCES subscriptions (groupId)" + + " ON DELETE CASCADE)"; private static final String INDEX_VISIBILITIES_BY_GROUP = - "CREATE INDEX visibilitiesByGroup ON visibilities (groupId)"; + "CREATE INDEX visibilitiesByGroup ON visibilities (groupId)"; private static final String INDEX_VISIBILITIES_BY_NEXT = - "CREATE INDEX visibilitiesByNext on visibilities (nextId)"; + "CREATE INDEX visibilitiesByNext on visibilities (nextId)"; private static final String CREATE_BATCHES_TO_ACK = - "CREATE TABLE batchesToAck" - + " (batchId HASH NOT NULL," - + " contactId INT NOT NULL," - + " PRIMARY KEY (batchId, contactId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE batchesToAck" + + " (batchId HASH NOT NULL," + + " contactId INT NOT NULL," + + " PRIMARY KEY (batchId, contactId)," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; private static final String CREATE_CONTACT_SUBSCRIPTIONS = - "CREATE TABLE contactSubscriptions" - + " (contactId INT NOT NULL," - + " groupId HASH NOT NULL," - + " groupName VARCHAR NOT NULL," - + " groupKey BINARY," // Null for unrestricted groups - + " start BIGINT NOT NULL," - + " PRIMARY KEY (contactId, groupId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE contactSubscriptions" + + " (contactId INT NOT NULL," + + " groupId HASH NOT NULL," + + " groupName VARCHAR NOT NULL," + + " groupKey BINARY," // Null for unrestricted groups + + " start BIGINT NOT NULL," + + " PRIMARY KEY (contactId, groupId)," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; private static final String CREATE_OUTSTANDING_BATCHES = - "CREATE TABLE outstandingBatches" - + " (batchId HASH NOT NULL," - + " contactId INT NOT NULL," - + " timestamp BIGINT NOT NULL," - + " passover INT NOT NULL," - + " PRIMARY KEY (batchId, contactId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE outstandingBatches" + + " (batchId HASH NOT NULL," + + " contactId INT NOT NULL," + + " timestamp BIGINT NOT NULL," + + " passover INT NOT NULL," + + " PRIMARY KEY (batchId, contactId)," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; private static final String CREATE_OUTSTANDING_MESSAGES = - "CREATE TABLE outstandingMessages" - + " (batchId HASH NOT NULL," - + " contactId INT NOT NULL," - + " messageId HASH NOT NULL," - + " PRIMARY KEY (batchId, contactId, messageId)," - + " FOREIGN KEY (batchId, contactId)" - + " REFERENCES outstandingBatches (batchId, contactId)" - + " ON DELETE CASCADE," - + " FOREIGN KEY (messageId) REFERENCES messages (messageId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE outstandingMessages" + + " (batchId HASH NOT NULL," + + " contactId INT NOT NULL," + + " messageId HASH NOT NULL," + + " PRIMARY KEY (batchId, contactId, messageId)," + + " FOREIGN KEY (batchId, contactId)" + + " REFERENCES outstandingBatches (batchId, contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (messageId) REFERENCES messages (messageId)" + + " ON DELETE CASCADE)"; private static final String INDEX_OUTSTANDING_MESSAGES_BY_BATCH = - "CREATE INDEX outstandingMessagesByBatch" - + " ON outstandingMessages (batchId)"; + "CREATE INDEX outstandingMessagesByBatch" + + " ON outstandingMessages (batchId)"; private static final String CREATE_RATINGS = - "CREATE TABLE ratings" - + " (authorId HASH NOT NULL," - + " rating SMALLINT NOT NULL," - + " PRIMARY KEY (authorId))"; + "CREATE TABLE ratings" + + " (authorId HASH NOT NULL," + + " rating SMALLINT NOT NULL," + + " PRIMARY KEY (authorId))"; private static final String CREATE_STATUSES = - "CREATE TABLE statuses" - + " (messageId HASH NOT NULL," - + " contactId INT NOT NULL," - + " status SMALLINT NOT NULL," - + " PRIMARY KEY (messageId, contactId)," - + " FOREIGN KEY (messageId) REFERENCES messages (messageId)" - + " ON DELETE CASCADE," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE statuses" + + " (messageId HASH NOT NULL," + + " contactId INT NOT NULL," + + " status SMALLINT NOT NULL," + + " PRIMARY KEY (messageId, contactId)," + + " FOREIGN KEY (messageId) REFERENCES messages (messageId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; private static final String INDEX_STATUSES_BY_MESSAGE = - "CREATE INDEX statusesByMessage ON statuses (messageId)"; + "CREATE INDEX statusesByMessage ON statuses (messageId)"; private static final String INDEX_STATUSES_BY_CONTACT = - "CREATE INDEX statusesByContact ON statuses (contactId)"; - - private static final String CREATE_TRANSPORTS = - "CREATE TABLE transports" - + " (transportId HASH NOT NULL," - + " index COUNTER," - + " PRIMARY KEY (transportId))"; + "CREATE INDEX statusesByContact ON statuses (contactId)"; private static final String CREATE_TRANSPORT_CONFIGS = - "CREATE TABLE transportConfigs" - + " (transportId HASH NOT NULL," - + " key VARCHAR NOT NULL," - + " value VARCHAR NOT NULL," - + " PRIMARY KEY (transportId, key))"; + "CREATE TABLE transportConfigs" + + " (transportId HASH NOT NULL," + + " key VARCHAR NOT NULL," + + " value VARCHAR NOT NULL," + + " PRIMARY KEY (transportId, key))"; private static final String CREATE_TRANSPORT_PROPS = - "CREATE TABLE transportProperties" - + " (transportId HASH NOT NULL," - + " key VARCHAR NOT NULL," - + " value VARCHAR NOT NULL," - + " PRIMARY KEY (transportId, key))"; - - private static final String CREATE_CONTACT_TRANSPORTS = - "CREATE TABLE contactTransports" - + " (contactId INT NOT NULL," - + " transportId HASH NOT NULL," - + " index INT NOT NULL," - + " UNIQUE (contactId, index)," - + " PRIMARY KEY (contactId, transportId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE transportProperties" + + " (transportId HASH NOT NULL," + + " key VARCHAR NOT NULL," + + " value VARCHAR NOT NULL," + + " PRIMARY KEY (transportId, key))"; private static final String CREATE_CONTACT_TRANSPORT_PROPS = - "CREATE TABLE contactTransportProperties" - + " (contactId INT NOT NULL," - + " transportId HASH NOT NULL," - + " key VARCHAR NOT NULL," - + " value VARCHAR NOT NULL," - + " PRIMARY KEY (contactId, transportId, key)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_CONNECTION_CONTEXTS = - "CREATE TABLE connectionContexts" - + " (contactId INT NOT NULL," - + " index INT NOT NULL," - + " connection BIGINT NOT NULL," - + " secret SECRET NOT NULL," - + " PRIMARY KEY (contactId, index)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_CONNECTION_WINDOWS = - "CREATE TABLE connectionWindows" - + " (contactId INT NOT NULL," - + " index INT NOT NULL," - + " connection BIGINT NOT NULL," - + " secret SECRET NOT NULL," - + " PRIMARY KEY (contactId, index, connection)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE contactTransportProperties" + + " (contactId INT NOT NULL," + + " transportId HASH NOT NULL," + + " key VARCHAR NOT NULL," + + " value VARCHAR NOT NULL," + + " PRIMARY KEY (contactId, transportId, key)," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + + private static final String CREATE_CONTACT_TRANSPORTS = + "CREATE TABLE contactTransports" + + " (contactId INT NOT NULL," + + " transportId HASH NOT NULL," + + " epoch BIGINT NOT NULL," + + " clockDiff BIGINT NOT NULL," + + " latency BIGINT NOT NULL," + + " alice BOOLEAN NOT NULL," + + " PRIMARY KEY (contactId, transportId)," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + + private static final String CREATE_SECRETS = + "CREATE TABLE secrets" + + " (contactId INT NOT NULL," + + " transportId HASH NOT NULL," + + " period BIGINT NOT NULL," + + " secret SECRET NOT NULL," + + " outgoing BIGINT NOT NULL," + + " centre BIGINT NOT NULL," + + " bitmap BINARY NOT NULL," + + " PRIMARY KEY (contactId, transportId, period)," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; private static final String CREATE_SUBSCRIPTION_TIMES = - "CREATE TABLE subscriptionTimes" - + " (contactId INT NOT NULL," - + " received BIGINT NOT NULL," - + " acked BIGINT NOT NULL," - + " expiry BIGINT NOT NULL," - + " PRIMARY KEY (contactId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE subscriptionTimes" + + " (contactId INT NOT NULL," + + " received BIGINT NOT NULL," + + " acked BIGINT NOT NULL," + + " expiry BIGINT NOT NULL," + + " PRIMARY KEY (contactId)," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; private static final String CREATE_TRANSPORT_TIMESTAMPS = - "CREATE TABLE transportTimestamps" - + " (contactId INT NOT NULL," - + " sent BIGINT NOT NULL," - + " received BIGINT NOT NULL," - + " modified BIGINT NOT NULL," - + " PRIMARY KEY (contactId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE transportTimestamps" + + " (contactId INT NOT NULL," + + " sent BIGINT NOT NULL," + + " received BIGINT NOT NULL," + + " modified BIGINT NOT NULL," + + " PRIMARY KEY (contactId)," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; private static final String CREATE_FLAGS = - "CREATE TABLE flags" - + " (messageId HASH NOT NULL," - + " read BOOLEAN NOT NULL," - + " starred BOOLEAN NOT NULL," - + " PRIMARY KEY (messageId)," - + " FOREIGN KEY (messageId) REFERENCES messages (messageId)" - + " ON DELETE CASCADE)"; + "CREATE TABLE flags" + + " (messageId HASH NOT NULL," + + " read BOOLEAN NOT NULL," + + " starred BOOLEAN NOT NULL," + + " PRIMARY KEY (messageId)," + + " FOREIGN KEY (messageId) REFERENCES messages (messageId)" + + " ON DELETE CASCADE)"; private static final Logger LOG = - Logger.getLogger(JdbcDatabase.class.getName()); + Logger.getLogger(JdbcDatabase.class.getName()); - private final ConnectionContextFactory connectionContextFactory; - private final ConnectionWindowFactory connectionWindowFactory; + // FIXME: Can this factory be done away with? private final GroupFactory groupFactory; private final Clock clock; // Different database libraries use different names for certain types private final String hashType, binaryType, counterType, secretType; private final LinkedList<Connection> connections = - new LinkedList<Connection>(); // Locking: self + new LinkedList<Connection>(); // Locking: self private int openConnections = 0; // Locking: connections private boolean closed = false; // Locking: connections protected abstract Connection createConnection() throws SQLException; - JdbcDatabase(ConnectionContextFactory connectionContextFactory, - ConnectionWindowFactory connectionWindowFactory, - GroupFactory groupFactory, Clock clock, String hashType, + JdbcDatabase(GroupFactory groupFactory, Clock clock, String hashType, String binaryType, String counterType, String secretType) { - this.connectionContextFactory = connectionContextFactory; - this.connectionWindowFactory = connectionWindowFactory; this.groupFactory = groupFactory; this.clock = clock; this.hashType = hashType; @@ -303,7 +286,7 @@ abstract class JdbcDatabase implements Database<Connection> { } protected void open(boolean resume, File dir, String driverClass) - throws DbException, IOException { + throws DbException, IOException { if(resume) { if(!dir.exists()) throw new FileNotFoundException(); if(!dir.isDirectory()) throw new FileNotFoundException(); @@ -351,13 +334,11 @@ abstract class JdbcDatabase implements Database<Connection> { s.executeUpdate(insertTypeNames(CREATE_STATUSES)); s.executeUpdate(INDEX_STATUSES_BY_MESSAGE); s.executeUpdate(INDEX_STATUSES_BY_CONTACT); - s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_CONFIGS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_PROPS)); - s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS)); s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_PROPS)); - s.executeUpdate(insertTypeNames(CREATE_CONNECTION_CONTEXTS)); - s.executeUpdate(insertTypeNames(CREATE_CONNECTION_WINDOWS)); + s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS)); + s.executeUpdate(insertTypeNames(CREATE_SECRETS)); s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMES)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS)); s.executeUpdate(insertTypeNames(CREATE_FLAGS)); @@ -475,12 +456,12 @@ abstract class JdbcDatabase implements Database<Connection> { } public void addBatchToAck(Connection txn, ContactId c, BatchId b) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT NULL FROM batchesToAck" - + " WHERE batchId = ? AND contactId = ?"; + + " WHERE batchId = ? AND contactId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, b.getBytes()); ps.setInt(2, c.getInt()); @@ -491,7 +472,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); if(found) return; sql = "INSERT INTO batchesToAck (batchId, contactId)" - + " VALUES (?, ?)"; + + " VALUES (?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, b.getBytes()); ps.setInt(2, c.getInt()); @@ -505,8 +486,7 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public ContactId addContact(Connection txn, byte[] inSecret, - byte[] outSecret, Collection<byte[]> erase) throws DbException { + public ContactId addContact(Connection txn) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -518,7 +498,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); // Get the new (highest) contact ID sql = "SELECT contactId FROM contacts" - + " ORDER BY contactId DESC LIMIT ?"; + + " ORDER BY contactId DESC LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, 1); rs = ps.executeQuery(); @@ -529,7 +509,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); // Create the head-of-list pointer for the visibility list sql = "INSERT INTO visibilities (contactId, deleted)" - + " VALUES (?, ZERO())"; + + " VALUES (?, ZERO())"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); affected = ps.executeUpdate(); @@ -537,8 +517,8 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); // Initialise the subscription timestamps sql = "INSERT INTO subscriptionTimes" - + " (contactId, received, acked, expiry)" - + " VALUES (?, ZERO(), ZERO(), ZERO())"; + + " (contactId, received, acked, expiry)" + + " VALUES (?, ZERO(), ZERO(), ZERO())"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); affected = ps.executeUpdate(); @@ -546,81 +526,70 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); // Initialise the transport timestamps sql = "INSERT INTO transportTimestamps" - + " (contactId, sent, received, modified)" - + " VALUES (?, ZERO(), ZERO(), ZERO())"; + + " (contactId, sent, received, modified)" + + " VALUES (?, ZERO(), ZERO(), ZERO())"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); - // Initialise the outgoing connection contexts for all transports - sql = "INSERT INTO connectionContexts" - + " (contactId, index, connection, secret)" - + " VALUES (?, ?, ZERO(), ?)"; + return c; + } catch(SQLException e) { + tryToClose(rs); + tryToClose(ps); + throw new DbException(e); + } + } + + public void addContactTransport(Connection txn, ContactTransport ct) + throws DbException { + PreparedStatement ps = null; + try { + String sql = "INSERT INTO contactTransports" + + " (contactId, transportId, epoch, clockDiff, latency," + + " alice)" + + " VALUES (?, ?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - for(int i = 0; i < ProtocolConstants.MAX_TRANSPORTS; i++) { - ps.setInt(2, i); - ConnectionContext ctx = - connectionContextFactory.createNextConnectionContext(c, - new TransportIndex(i), 0L, outSecret); - byte[] secret = ctx.getSecret(); - erase.add(secret); - ps.setBytes(3, secret); - ps.addBatch(); - } - int[] batchAffected = ps.executeBatch(); - if(batchAffected.length != ProtocolConstants.MAX_TRANSPORTS) - throw new DbStateException(); - for(int i = 0; i < batchAffected.length; i++) { - if(batchAffected[i] != 1) throw new DbStateException(); - } + ps.setInt(1, ct.getContactId().getInt()); + ps.setBytes(2, ct.getTransportId().getBytes()); + ps.setLong(3, ct.getEpoch()); + ps.setLong(4, ct.getClockDifference()); + ps.setLong(5, ct.getLatency()); + ps.setBoolean(6, ct.getAlice()); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); ps.close(); - // Initialise the incoming connection windows for all transports - sql = "INSERT INTO connectionWindows" - + " (contactId, index, connection, secret)" - + " VALUES (?, ?, ?, ?)"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - int batchSize = 0; - for(int i = 0; i < ProtocolConstants.MAX_TRANSPORTS; i++) { - ps.setInt(2, i); - ConnectionWindow w = - connectionWindowFactory.createConnectionWindow( - new TransportIndex(i), inSecret); - for(Entry<Long, byte[]> e : w.getUnseen().entrySet()) { - ps.setLong(3, e.getKey()); - byte[] secret = e.getValue(); - erase.add(secret); - ps.setBytes(4, secret); - ps.addBatch(); - batchSize++; - } - } - batchAffected = ps.executeBatch(); - if(batchAffected.length != batchSize) throw new DbStateException(); - for(int i = 0; i < batchAffected.length; i++) { - if(batchAffected[i] != 1) throw new DbStateException(); - } + sql = "INSERT INTO secrets" + + " (contactId, transportId, period, secret, outgoing," + + " centre, bitmap)" + + " VALUES (?, ?, ?, ?, ?, ?, ?)"; + ps = txn.prepareStatement(sql); + ps.setInt(1, ct.getContactId().getInt()); + ps.setBytes(2, ct.getTransportId().getBytes()); + ps.setLong(3, ct.getPeriod()); + ps.setBytes(4, ct.getSecret()); + ps.setLong(5, ct.getOutgoingConnectionCounter()); + ps.setLong(6, ct.getWindowCentre()); + ps.setBytes(7, ct.getWindowBitmap()); + affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); ps.close(); - return c; } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); throw new DbException(e); } } public boolean addGroupMessage(Connection txn, Message m) - throws DbException { + throws DbException { assert m.getGroup() != null; if(containsMessage(txn, m.getId())) return false; PreparedStatement ps = null; try { String sql = "INSERT INTO messages (messageId, parentId, groupId," - + " authorId, subject, timestamp, length, bodyStart," - + " bodyLength, raw, sendability)" - + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ZERO())"; + + " authorId, subject, timestamp, length, bodyStart," + + " bodyLength, raw, sendability)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ZERO())"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getId().getBytes()); if(m.getParent() == null) ps.setNull(2, Types.BINARY); @@ -652,8 +621,8 @@ abstract class JdbcDatabase implements Database<Connection> { try { // Create an outstanding batch row String sql = "INSERT INTO outstandingBatches" - + " (batchId, contactId, timestamp, passover)" - + " VALUES (?, ?, ?, ZERO())"; + + " (batchId, contactId, timestamp, passover)" + + " VALUES (?, ?, ?, ZERO())"; ps = txn.prepareStatement(sql); ps.setBytes(1, b.getBytes()); ps.setInt(2, c.getInt()); @@ -663,8 +632,8 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); // Create an outstanding message row for each message in the batch sql = "INSERT INTO outstandingMessages" - + " (batchId, contactId, messageId)" - + " VALUES (?, ?, ?)"; + + " (batchId, contactId, messageId)" + + " VALUES (?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, b.getBytes()); ps.setInt(2, c.getInt()); @@ -681,7 +650,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); // Set the status of each message in the batch to SENT sql = "UPDATE statuses SET status = ?" - + " WHERE messageId = ? AND contactId = ? AND status = ?"; + + " WHERE messageId = ? AND contactId = ? AND status = ?"; ps = txn.prepareStatement(sql); ps.setShort(1, (short) Status.SENT.ordinal()); ps.setInt(3, c.getInt()); @@ -705,14 +674,15 @@ abstract class JdbcDatabase implements Database<Connection> { } public boolean addPrivateMessage(Connection txn, Message m, ContactId c) - throws DbException { + throws DbException { assert m.getGroup() == null; if(containsMessage(txn, m.getId())) return false; PreparedStatement ps = null; try { - String sql = "INSERT INTO messages (messageId, parentId, subject," - + " timestamp, length, bodyStart, bodyLength, raw, contactId)" - + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + String sql = "INSERT INTO messages" + + " (messageId, parentId, subject, timestamp, length," + + " bodyStart, bodyLength, raw, contactId)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getId().getBytes()); if(m.getParent() == null) ps.setNull(2, Types.BINARY); @@ -739,7 +709,8 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; try { String sql = "INSERT INTO subscriptions" - + " (groupId, groupName, groupKey, start) VALUES (?, ?, ?, ?)"; + + " (groupId, groupName, groupKey, start)" + + " VALUES (?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getId().getBytes()); ps.setString(2, g.getName()); @@ -755,6 +726,55 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public void addSecrets(Connection txn, Collection<TemporarySecret> secrets) + throws DbException { + PreparedStatement ps = null; + try { + // Store the new secrets + String sql = "INSERT INTO secrets" + + " (contactId, transportId, period, secret, outgoing," + + " centre, bitmap)"; + ps = txn.prepareStatement(sql); + for(TemporarySecret s : secrets) { + ps.setInt(1, s.getContactId().getInt()); + ps.setBytes(2, s.getTransportId().getBytes()); + ps.setLong(3, s.getPeriod()); + ps.setBytes(4, s.getSecret()); + ps.setLong(5, s.getOutgoingConnectionCounter()); + ps.setLong(6, s.getWindowCentre()); + ps.setBytes(7, s.getWindowBitmap()); + ps.addBatch(); + } + int[] batchAffected = ps.executeBatch(); + if(batchAffected.length != secrets.size()) + throw new DbStateException(); + for(int i = 0; i < batchAffected.length; i++) { + if(batchAffected[i] != 1) throw new DbStateException(); + } + ps.close(); + // Delete any obsolete secrets + sql = "DELETE FROM secrets" + + " WHERE contactId = ? AND transportId = ? AND period < ?"; + ps = txn.prepareStatement(sql); + for(TemporarySecret s : secrets) { + ps.setInt(1, s.getContactId().getInt()); + ps.setBytes(2, s.getTransportId().getBytes()); + ps.setLong(3, s.getPeriod() - 1); + ps.addBatch(); + } + batchAffected = ps.executeBatch(); + if(batchAffected.length != secrets.size()) + throw new DbStateException(); + for(int i = 0; i < batchAffected.length; i++) { + if(batchAffected[i] > 1) throw new DbStateException(); + } + ps.close(); + } catch(SQLException e) { + tryToClose(ps); + throw new DbException(e); + } + } + public void addSubscription(Connection txn, ContactId c, Group g, long start) throws DbException { PreparedStatement ps = null; @@ -762,7 +782,7 @@ abstract class JdbcDatabase implements Database<Connection> { try { // Check whether the subscription already exists String sql = "SELECT NULL FROM contactSubscriptions" - + " WHERE contactId = ? AND groupId = ?"; + + " WHERE contactId = ? AND groupId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, g.getId().getBytes()); @@ -774,8 +794,8 @@ abstract class JdbcDatabase implements Database<Connection> { if(found) return; // Add the subscription sql = "INSERT INTO contactSubscriptions" - + " (contactId, groupId, groupName, groupKey, start)" - + " VALUES (?, ?, ?, ?, ?)"; + + " (contactId, groupId, groupName, groupKey, start)" + + " VALUES (?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, g.getId().getBytes()); @@ -792,46 +812,8 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public TransportIndex addTransport(Connection txn, TransportId t) - throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - // Allocate a new index - String sql = "INSERT INTO transports (transportId) VALUES (?)"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, t.getBytes()); - int affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); - ps.close(); - // If the new index is in range, return it - sql = "SELECT index FROM transports WHERE transportId = ?"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, t.getBytes()); - rs = ps.executeQuery(); - if(!rs.next()) throw new DbStateException(); - int i = rs.getInt(1); - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - if(i < ProtocolConstants.MAX_TRANSPORTS) - return new TransportIndex(i); - // Too many transports - delete the new index and return null - sql = "DELETE FROM transports WHERE transportId = ?"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, t.getBytes()); - affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); - return null; - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - public void addVisibility(Connection txn, ContactId c, GroupId g) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -839,7 +821,8 @@ abstract class JdbcDatabase implements Database<Connection> { byte[] groupId = null, nextId = null; long deleted = 0L; String sql = "SELECT groupId, nextId, deleted FROM visibilities" - + " WHERE contactId = ? AND nextId > ? ORDER BY nextId LIMIT ?"; + + " WHERE contactId = ? AND nextId > ?" + + " ORDER BY nextId LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, g.getBytes()); @@ -850,7 +833,7 @@ abstract class JdbcDatabase implements Database<Connection> { rs.close(); ps.close(); sql = "SELECT groupId, nextId, deleted FROM visibilities" - + " WHERE contactId = ? AND nextId IS NULL"; + + " WHERE contactId = ? AND nextId IS NULL"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); rs = ps.executeQuery(); @@ -866,14 +849,14 @@ abstract class JdbcDatabase implements Database<Connection> { if(groupId == null) { // Inserting at the head of the list sql = "UPDATE visibilities SET nextId = ?" - + " WHERE contactId = ? AND groupId IS NULL"; + + " WHERE contactId = ? AND groupId IS NULL"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); ps.setInt(2, c.getInt()); } else { // Inserting in the middle or at the tail of the list sql = "UPDATE visibilities SET nextId = ?" - + " WHERE contactId = ? AND groupId = ?"; + + " WHERE contactId = ? AND groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); ps.setInt(2, c.getInt()); @@ -884,7 +867,8 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); // Insert the new element sql = "INSERT INTO visibilities" - + " (contactId, groupId, nextId, deleted) VALUES (?, ?, ?, ?)"; + + " (contactId, groupId, nextId, deleted)" + + " VALUES (?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, g.getBytes()); @@ -902,7 +886,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public boolean containsContact(Connection txn, ContactId c) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -922,8 +906,31 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public boolean containsContactTransport(Connection txn, ContactId c, + TransportId t) throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT NULL FROM contactTransports" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getBytes()); + rs = ps.executeQuery(); + boolean found = rs.next(); + if(rs.next()) throw new DbStateException(); + rs.close(); + ps.close(); + return found; + } catch(SQLException e) { + tryToClose(rs); + tryToClose(ps); + throw new DbException(e); + } + } + public boolean containsMessage(Connection txn, MessageId m) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -944,7 +951,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public boolean containsSubscription(Connection txn, GroupId g) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -965,12 +972,12 @@ abstract class JdbcDatabase implements Database<Connection> { } public boolean containsSubscription(Connection txn, GroupId g, long time) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT NULL FROM subscriptions" - + " WHERE groupId = ? AND start <= ?"; + + " WHERE groupId = ? AND start <= ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); ps.setLong(2, time); @@ -993,9 +1000,10 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT start FROM subscriptions JOIN visibilities" - + " ON subscriptions.groupId = visibilities.groupId" - + " WHERE subscriptions.groupId = ? AND contactId = ?"; + String sql = "SELECT start FROM subscriptions AS s" + + " JOIN visibilities AS v" + + " ON s.groupId = v.groupId" + + " WHERE s.groupId = ? AND contactId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); ps.setInt(2, c.getInt()); @@ -1021,8 +1029,8 @@ abstract class JdbcDatabase implements Database<Connection> { ResultSet rs = null; try { String sql = "SELECT batchId FROM batchesToAck" - + " WHERE contactId = ?" - + " LIMIT ?"; + + " WHERE contactId = ?" + + " LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setInt(2, maxBatches); @@ -1040,12 +1048,12 @@ abstract class JdbcDatabase implements Database<Connection> { } public TransportConfig getConfig(Connection txn, TransportId t) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT key, value FROM transportConfigs" - + " WHERE transportId = ?"; + + " WHERE transportId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, t.getBytes()); rs = ps.executeQuery(); @@ -1061,67 +1069,19 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public ConnectionContext getConnectionContext(Connection txn, ContactId c, - TransportIndex i, Collection<byte[]> erase) throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - // Retrieve the current context - String sql = "SELECT connection, secret FROM connectionContexts" - + " WHERE contactId = ? AND index = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setInt(2, i.getInt()); - rs = ps.executeQuery(); - if(!rs.next()) throw new DbStateException(); - long connection = rs.getLong(1); - byte[] secret = rs.getBytes(2); - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - ConnectionContext ctx = - connectionContextFactory.createConnectionContext(c, i, - connection, secret); - // Calculate and store the next context - ConnectionContext next = - connectionContextFactory.createNextConnectionContext(c, i, - connection + 1, secret); - byte[] nextSecret = next.getSecret(); - erase.add(nextSecret); - sql = "UPDATE connectionContexts" - + " SET connection = connection + 1, secret = ?" - + " WHERE contactId = ? AND index = ?"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, nextSecret); - ps.setInt(2, c.getInt()); - ps.setInt(3, i.getInt()); - int affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); - ps.close(); - return ctx; - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - - public ConnectionWindow getConnectionWindow(Connection txn, ContactId c, - TransportIndex i) throws DbException { + public Collection<ContactId> getContacts(Connection txn) + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT connection, secret FROM connectionWindows" - + " WHERE contactId = ? AND index = ?"; + String sql = "SELECT contactId FROM contacts"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setInt(2, i.getInt()); rs = ps.executeQuery(); - Map<Long, byte[]> unseen = new HashMap<Long, byte[]>(); - while(rs.next()) unseen.put(rs.getLong(1), rs.getBytes(2)); + List<ContactId> ids = new ArrayList<ContactId>(); + while(rs.next()) ids.add(new ContactId(rs.getInt(1))); rs.close(); ps.close(); - return connectionWindowFactory.createConnectionWindow(i, unseen); + return Collections.unmodifiableList(ids); } catch(SQLException e) { tryToClose(rs); tryToClose(ps); @@ -1129,19 +1089,39 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public Collection<ContactId> getContacts(Connection txn) - throws DbException { + public Collection<ContactTransport> getContactTransports(Connection txn) + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT contactId FROM contacts"; + String sql = "SELECT c.contactId, c.transportId, epoch," + + " clockDiff, latency, alice, period, secret," + + " outgoing, centre, bitmap" + + " FROM contactTransports AS c" + + " JOIN secrets AS s" + + " ON c.contactId = s.contactId" + + " AND c.transportId = s.transportId"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); - List<ContactId> ids = new ArrayList<ContactId>(); - while(rs.next()) ids.add(new ContactId(rs.getInt(1))); - rs.close(); - ps.close(); - return Collections.unmodifiableList(ids); + List<ContactTransport> cts = new ArrayList<ContactTransport>(); + while(rs.next()) { + ContactId contactId = new ContactId(rs.getInt(1)); + TransportId transportId = new TransportId(rs.getBytes(2)); + long epoch = rs.getLong(3); + long clockDiff = rs.getLong(4); + long latency = rs.getLong(5); + boolean alice = rs.getBoolean(6); + long period = rs.getLong(7); + byte[] secret = rs.getBytes(8); + long outgoing = rs.getLong(9); + long centre = rs.getLong(10); + byte[] bitmap = rs.getBytes(11); + ContactTransport ct = new ContactTransport(contactId, + transportId, epoch, clockDiff, latency, alice, period, + secret, outgoing, centre, bitmap); + cts.add(ct); + } + return Collections.unmodifiableList(cts); } catch(SQLException e) { tryToClose(rs); tryToClose(ps); @@ -1163,13 +1143,13 @@ abstract class JdbcDatabase implements Database<Connection> { try { long timestamp = 0L; String sql = "SELECT timestamp FROM messages" - + " ORDER BY timestamp LIMIT ?"; + + " ORDER BY timestamp LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, 1); rs = ps.executeQuery(); if(rs.next()) { timestamp = rs.getLong(1); - timestamp -= timestamp % DatabaseConstants.EXPIRY_MODULUS; + timestamp -= timestamp % EXPIRY_MODULUS; } if(rs.next()) throw new DbStateException(); rs.close(); @@ -1183,15 +1163,15 @@ abstract class JdbcDatabase implements Database<Connection> { } public MessageId getGroupMessageParent(Connection txn, MessageId m) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT m1.parentId FROM messages AS m1" - + " JOIN messages AS m2" - + " ON m1.parentId = m2.messageId" - + " AND m1.groupId = m2.groupId" - + " WHERE m1.messageId = ?"; + + " JOIN messages AS m2" + + " ON m1.parentId = m2.messageId" + + " AND m1.groupId = m2.groupId" + + " WHERE m1.messageId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); rs = ps.executeQuery(); @@ -1210,37 +1190,13 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public TransportIndex getLocalIndex(Connection txn, TransportId t) - throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT index FROM transports WHERE transportId = ?"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, t.getBytes()); - rs = ps.executeQuery(); - TransportIndex index = null; - if(rs.next()) { - index = new TransportIndex(rs.getInt(1)); - if(rs.next()) throw new DbStateException(); - } - rs.close(); - ps.close(); - return index; - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - public TransportProperties getLocalProperties(Connection txn, TransportId t) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT key, value FROM transportProperties" - + " WHERE transportId = ?"; + + " WHERE transportId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, t.getBytes()); rs = ps.executeQuery(); @@ -1257,14 +1213,13 @@ abstract class JdbcDatabase implements Database<Connection> { } public Collection<Transport> getLocalTransports(Connection txn) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT transports.transportId, index, key, value" - + " FROM transports LEFT OUTER JOIN transportProperties" - + " ON transports.transportId = transportProperties.transportId" - + " ORDER BY transports.transportId"; + String sql = "SELECT transportId, key, value" + + " FROM transportProperties" + + " ORDER BY transportId"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); List<Transport> transports = new ArrayList<Transport>(); @@ -1273,13 +1228,10 @@ abstract class JdbcDatabase implements Database<Connection> { while(rs.next()) { TransportId id = new TransportId(rs.getBytes(1)); if(!id.equals(lastId)) { - t = new Transport(id, new TransportIndex(rs.getInt(2))); + t = new Transport(id); transports.add(t); } - // Key and value may be null due to the left outer join - String key = rs.getString(3); - String value = rs.getString(4); - if(key != null && value != null) t.put(key, value); + t.put(rs.getString(2), rs.getString(3)); } rs.close(); ps.close(); @@ -1292,15 +1244,15 @@ abstract class JdbcDatabase implements Database<Connection> { } public Collection<BatchId> getLostBatches(Connection txn, ContactId c) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT batchId FROM outstandingBatches" - + " WHERE contactId = ? AND passover >= ?"; + + " WHERE contactId = ? AND passover >= ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setInt(2, DatabaseConstants.RETRANSMIT_THRESHOLD); + ps.setInt(2, RETRANSMIT_THRESHOLD); rs = ps.executeQuery(); List<BatchId> ids = new ArrayList<BatchId>(); while(rs.next()) ids.add(new BatchId(rs.getBytes(1))); @@ -1338,12 +1290,12 @@ abstract class JdbcDatabase implements Database<Connection> { } public byte[] getMessageBody(Connection txn, MessageId m) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT bodyStart, bodyLength, raw FROM messages" - + " WHERE messageId = ?"; + + " WHERE messageId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); rs = ps.executeQuery(); @@ -1368,11 +1320,12 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT messages.messageId, parentId, authorId," - + " subject, timestamp, read, starred" - + " FROM messages LEFT OUTER JOIN flags" - + " ON messages.messageId = flags.messageId" - + " WHERE groupId = ?"; + String sql = "SELECT m.messageId, parentId, authorId," + + " subject, timestamp, read, starred" + + " FROM messages AS m" + + " LEFT OUTER JOIN flags AS f" + + " ON m.messageId = f.messageId" + + " WHERE groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); rs = ps.executeQuery(); @@ -1400,15 +1353,16 @@ abstract class JdbcDatabase implements Database<Connection> { } public byte[] getMessageIfSendable(Connection txn, ContactId c, MessageId m) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { // Do we have a sendable private message with the given ID? - String sql = "SELECT length, raw FROM messages" - + " JOIN statuses ON messages.messageId = statuses.messageId" - + " WHERE messages.messageId = ? AND messages.contactId = ?" - + " AND status = ?"; + String sql = "SELECT length, raw FROM messages AS m" + + " JOIN statuses AS s" + + " ON m.messageId = s.messageId" + + " WHERE m.messageId = ? AND m.contactId = ?" + + " AND status = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setInt(2, c.getInt()); @@ -1425,23 +1379,23 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); if(raw != null) return raw; // Do we have a sendable group message with the given ID? - sql = "SELECT length, raw FROM messages" - + " JOIN contactSubscriptions AS cs" - + " ON messages.groupId = cs.groupId" - + " JOIN visibilities" - + " ON messages.groupId = visibilities.groupId" - + " AND cs.contactId = visibilities.contactId" - + " JOIN statuses" - + " ON messages.messageId = statuses.messageId" - + " AND cs.contactId = statuses.contactId" - + " JOIN subscriptionTimes" - + " ON cs.contactId = subscriptionTimes.contactId" - + " WHERE messages.messageId = ?" - + " AND cs.contactId = ?" - + " AND timestamp >= start" - + " AND timestamp >= expiry" - + " AND status = ?" - + " AND sendability > ZERO()"; + sql = "SELECT length, raw FROM messages AS m" + + " JOIN contactSubscriptions AS cs" + + " ON m.groupId = cs.groupId" + + " JOIN visibilities AS v" + + " ON m.groupId = v.groupId" + + " AND cs.contactId = v.contactId" + + " JOIN statuses AS s" + + " ON m.messageId = s.messageId" + + " AND cs.contactId = s.contactId" + + " JOIN subscriptionTimes AS st" + + " ON cs.contactId = st.contactId" + + " WHERE m.messageId = ?" + + " AND cs.contactId = ?" + + " AND timestamp >= start" + + " AND timestamp >= expiry" + + " AND status = ?" + + " AND sendability > ZERO()"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setInt(2, c.getInt()); @@ -1464,7 +1418,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public Collection<MessageId> getMessagesByAuthor(Connection txn, AuthorId a) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -1485,7 +1439,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public int getNumberOfSendableChildren(Connection txn, MessageId m) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -1500,8 +1454,8 @@ abstract class JdbcDatabase implements Database<Connection> { rs.close(); ps.close(); sql = "SELECT COUNT(messageId) FROM messages" - + " WHERE parentId = ? AND groupId = ?" - + " AND sendability > ZERO()"; + + " WHERE parentId = ? AND groupId = ?" + + " AND sendability > ZERO()"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setBytes(2, groupId); @@ -1520,12 +1474,12 @@ abstract class JdbcDatabase implements Database<Connection> { } public Collection<MessageId> getOldMessages(Connection txn, int capacity) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT length, messageId FROM messages" - + " ORDER BY timestamp"; + + " ORDER BY timestamp"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); List<MessageId> ids = new ArrayList<MessageId>(); @@ -1589,48 +1543,20 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public TransportIndex getRemoteIndex(Connection txn, ContactId c, - TransportId t) throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT index FROM contactTransports" - + " WHERE contactId = ? AND transportId = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, t.getBytes()); - rs = ps.executeQuery(); - TransportIndex index = null; - if(rs.next()) { - index = new TransportIndex(rs.getInt(1)); - if(rs.next()) throw new DbStateException(); - } - rs.close(); - ps.close(); - return index; - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - public Map<ContactId, TransportProperties> getRemoteProperties( Connection txn, TransportId t) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT contactTransports.contactId, key, value" - + " FROM contactTransports" - + " LEFT OUTER JOIN contactTransportProperties AS ctp" - + " ON contactTransports.transportId = ctp.transportId" - + " WHERE contactTransports.transportId = ?" - + " ORDER BY contactTransports.contactId"; + String sql = "SELECT contactId, key, value" + + " FROM contactTransportProperties" + + " WHERE transportId = ?" + + " ORDER BY contactId"; ps = txn.prepareStatement(sql); ps.setBytes(1, t.getBytes()); rs = ps.executeQuery(); Map<ContactId, TransportProperties> properties = - new HashMap<ContactId, TransportProperties>(); + new HashMap<ContactId, TransportProperties>(); ContactId lastId = null; TransportProperties p = null; while(rs.next()) { @@ -1639,10 +1565,7 @@ abstract class JdbcDatabase implements Database<Connection> { p = new TransportProperties(); properties.put(id, p); } - // Key and value may be null due to the left outer join - String key = rs.getString(2); - String value = rs.getString(3); - if(key != null && value != null) p.put(key, value); + p.put(rs.getString(2), rs.getString(3)); } rs.close(); ps.close(); @@ -1681,11 +1604,12 @@ abstract class JdbcDatabase implements Database<Connection> { ResultSet rs = null; try { // Do we have any sendable private messages? - String sql = "SELECT messages.messageId FROM messages" - + " JOIN statuses ON messages.messageId = statuses.messageId" - + " WHERE messages.contactId = ? AND status = ?" - + " ORDER BY timestamp" - + " LIMIT ?"; + String sql = "SELECT m.messageId FROM messages AS m" + + " JOIN statuses AS s" + + " ON m.messageId = s.messageId" + + " WHERE m.contactId = ? AND status = ?" + + " ORDER BY timestamp" + + " LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setShort(2, (short) Status.NEW.ordinal()); @@ -1698,24 +1622,24 @@ abstract class JdbcDatabase implements Database<Connection> { if(ids.size() == maxMessages) return Collections.unmodifiableList(ids); // Do we have any sendable group messages? - sql = "SELECT messages.messageId FROM messages" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities" - + " ON messages.groupId = visibilities.groupId" - + " AND cs.contactId = visibilities.contactId" - + " JOIN statuses" - + " ON messages.messageId = statuses.messageId" - + " AND cs.contactId = statuses.contactId" - + " JOIN subscriptionTimes" - + " ON cs.contactId = subscriptionTimes.contactId" - + " WHERE cs.contactId = ?" - + " AND timestamp >= start" - + " AND timestamp >= expiry" - + " AND status = ?" - + " AND sendability > ZERO()" - + " ORDER BY timestamp" - + " LIMIT ?"; + sql = "SELECT m.messageId FROM messages AS m" + + " JOIN contactSubscriptions AS cs" + + " ON m.groupId = cs.groupId" + + " JOIN visibilities AS v" + + " ON m.groupId = v.groupId" + + " AND cs.contactId = v.contactId" + + " JOIN statuses AS s" + + " ON m.messageId = s.messageId" + + " AND cs.contactId = s.contactId" + + " JOIN subscriptionTimes AS st" + + " ON cs.contactId = st.contactId" + + " WHERE cs.contactId = ?" + + " AND timestamp >= start" + + " AND timestamp >= expiry" + + " AND status = ?" + + " AND sendability > ZERO()" + + " ORDER BY timestamp" + + " LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setShort(2, (short) Status.NEW.ordinal()); @@ -1738,10 +1662,11 @@ abstract class JdbcDatabase implements Database<Connection> { ResultSet rs = null; try { // Do we have any sendable private messages? - String sql = "SELECT length, messages.messageId FROM messages" - + " JOIN statuses ON messages.messageId = statuses.messageId" - + " WHERE messages.contactId = ? AND status = ?" - + " ORDER BY timestamp"; + String sql = "SELECT length, m.messageId FROM messages AS m" + + " JOIN statuses AS s" + + " ON m.messageId = s.messageId" + + " WHERE m.contactId = ? AND status = ?" + + " ORDER BY timestamp"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setShort(2, (short) Status.NEW.ordinal()); @@ -1758,23 +1683,23 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); if(total == capacity) return Collections.unmodifiableList(ids); // Do we have any sendable group messages? - sql = "SELECT length, messages.messageId FROM messages" - + " JOIN contactSubscriptions AS cs" - + " ON messages.groupId = cs.groupId" - + " JOIN visibilities" - + " ON messages.groupId = visibilities.groupId" - + " AND cs.contactId = visibilities.contactId" - + " JOIN statuses" - + " ON messages.messageId = statuses.messageId" - + " AND cs.contactId = statuses.contactId" - + " JOIN subscriptionTimes" - + " ON cs.contactId = subscriptionTimes.contactId" - + " WHERE cs.contactId = ?" - + " AND timestamp >= start" - + " AND timestamp >= expiry" - + " AND status = ?" - + " AND sendability > ZERO()" - + " ORDER BY timestamp"; + sql = "SELECT length, m.messageId FROM messages AS m" + + " JOIN contactSubscriptions AS cs" + + " ON m.groupId = cs.groupId" + + " JOIN visibilities AS v" + + " ON m.groupId = v.groupId" + + " AND cs.contactId = v.contactId" + + " JOIN statuses AS s" + + " ON m.messageId = s.messageId" + + " AND cs.contactId = s.contactId" + + " JOIN subscriptionTimes AS st" + + " ON cs.contactId = st.contactId" + + " WHERE cs.contactId = ?" + + " AND timestamp >= start" + + " AND timestamp >= expiry" + + " AND status = ?" + + " AND sendability > ZERO()" + + " ORDER BY timestamp"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setShort(2, (short) Status.NEW.ordinal()); @@ -1817,12 +1742,12 @@ abstract class JdbcDatabase implements Database<Connection> { } public Collection<Group> getSubscriptions(Connection txn) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT groupId, groupName, groupKey" - + " FROM subscriptions"; + + " FROM subscriptions"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); List<Group> subs = new ArrayList<Group>(); @@ -1843,13 +1768,13 @@ abstract class JdbcDatabase implements Database<Connection> { } public Collection<Group> getSubscriptions(Connection txn, ContactId c) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT groupId, groupName, groupKey" - + " FROM contactSubscriptions" - + " WHERE contactId = ?"; + + " FROM contactSubscriptions" + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); rs = ps.executeQuery(); @@ -1891,12 +1816,12 @@ abstract class JdbcDatabase implements Database<Connection> { } public long getTransportsSent(Connection txn, ContactId c) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT sent FROM transportTimestamps" - + " WHERE contactId = ?"; + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); rs = ps.executeQuery(); @@ -1914,15 +1839,16 @@ abstract class JdbcDatabase implements Database<Connection> { } public Map<GroupId, Integer> getUnreadMessageCounts(Connection txn) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT groupId, COUNT(*)" - + " FROM messages LEFT OUTER JOIN flags" - + " ON messages.messageId = flags.messageId" - + " WHERE (NOT read) OR (read IS NULL)" - + " GROUP BY groupId"; + + " FROM messages AS m" + + " LEFT OUTER JOIN flags AS f" + + " ON m.messageId = f.messageId" + + " WHERE (NOT read) OR (read IS NULL)" + + " GROUP BY groupId"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); Map<GroupId, Integer> counts = new HashMap<GroupId, Integer>(); @@ -1941,7 +1867,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public Collection<ContactId> getVisibility(Connection txn, GroupId g) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -1966,11 +1892,11 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT groupId, nextId FROM visibilities" - + " JOIN subscriptionTimes" - + " ON visibilities.contactId = subscriptionTimes.contactId" - + " WHERE visibilities.contactId = ?" - + " AND deleted > acked AND deleted < ?"; + String sql = "SELECT groupId, nextId FROM visibilities AS v" + + " JOIN subscriptionTimes AS st" + + " ON v.contactId = st.contactId" + + " WHERE v.contactId = ?" + + " AND deleted > acked AND deleted < ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setLong(2, timestamp); @@ -2000,14 +1926,14 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = - "SELECT subscriptions.groupId, groupName, groupKey, start" - + " FROM subscriptions JOIN visibilities" - + " ON subscriptions.groupId = visibilities.groupId" - + " JOIN subscriptionTimes" - + " ON visibilities.contactId = subscriptionTimes.contactId" - + " WHERE visibilities.contactId = ?" - + " AND start > acked AND start < ?"; + String sql = "SELECT s.groupId, groupName, groupKey, start" + + " FROM subscriptions AS s" + + " JOIN visibilities AS v" + + " ON s.groupId = v.groupId" + + " JOIN subscriptionTimes AS st" + + " ON v.contactId = st.contactId" + + " WHERE v.contactId = ?" + + " AND start > acked AND start < ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setLong(2, timestamp); @@ -2033,15 +1959,16 @@ abstract class JdbcDatabase implements Database<Connection> { } public boolean hasSendableMessages(Connection txn, ContactId c) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { // Do we have any sendable private messages? - String sql = "SELECT messages.messageId FROM messages" - + " JOIN statuses ON messages.messageId = statuses.messageId" - + " WHERE messages.contactId = ? AND status = ?" - + " LIMIT ?"; + String sql = "SELECT m.messageId FROM messages AS m" + + " JOIN statuses AS s" + + " ON m.messageId = s.messageId" + + " WHERE m.contactId = ? AND status = ?" + + " LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setShort(2, (short) Status.NEW.ordinal()); @@ -2053,23 +1980,23 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); if(found) return true; // Do we have any sendable group messages? - sql = "SELECT messages.messageId FROM messages" - + " JOIN contactSubscriptions AS cs" - + " ON messages.groupId = cs.groupId" - + " JOIN visibilities" - + " ON messages.groupId = visibilities.groupId" - + " AND cs.contactId = visibilities.contactId" - + " JOIN statuses" - + " ON messages.messageId = statuses.messageId" - + " AND cs.contactId = statuses.contactId" - + " JOIN subscriptionTimes" - + " ON cs.contactId = subscriptionTimes.contactId" - + " WHERE cs.contactId = ?" - + " AND timestamp >= start" - + " AND timestamp >= expiry" - + " AND status = ?" - + " AND sendability > ZERO()" - + " LIMIT ?"; + sql = "SELECT m.messageId FROM messages AS m" + + " JOIN contactSubscriptions AS cs" + + " ON m.groupId = cs.groupId" + + " JOIN visibilities AS v" + + " ON m.groupId = v.groupId" + + " AND cs.contactId = v.contactId" + + " JOIN statuses AS s" + + " ON m.messageId = s.messageId" + + " AND cs.contactId = s.contactId" + + " JOIN subscriptionTimes AS st" + + " ON cs.contactId = st.contactId" + + " WHERE cs.contactId = ?" + + " AND timestamp >= start" + + " AND timestamp >= expiry" + + " AND status = ?" + + " AND sendability > ZERO()" + + " LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setShort(2, (short) Status.NEW.ordinal()); @@ -2087,13 +2014,32 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public void incrementConnectionCounter(Connection txn, ContactId c, + TransportId t, long period) throws DbException { + PreparedStatement ps = null; + try { + String sql = "UPDATE secrets SET outgoing = outgoing + 1" + + " WHERE contactId = ? AND transportId = ? AND period = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getBytes()); + ps.setLong(3, period); + int affected = ps.executeUpdate(); + if(affected > 1) throw new DbStateException(); + ps.close(); + } catch(SQLException e) { + tryToClose(ps); + throw new DbException(e); + } + } + public void removeAckedBatch(Connection txn, ContactId c, BatchId b) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT timestamp FROM outstandingBatches" - + " WHERE contactId = ? AND batchId = ?"; + + " WHERE contactId = ? AND batchId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, b.getBytes()); @@ -2105,7 +2051,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); // Increment the passover count of all older outstanding batches sql = "UPDATE outstandingBatches SET passover = passover + ?" - + " WHERE contactId = ? AND timestamp < ?"; + + " WHERE contactId = ? AND timestamp < ?"; ps = txn.prepareStatement(sql); ps.setInt(1, 1); ps.setInt(2, c.getInt()); @@ -2126,13 +2072,13 @@ abstract class JdbcDatabase implements Database<Connection> { ResultSet rs = null; try { String sql = "SELECT messageId FROM outstandingMessages" - + " WHERE contactId = ? AND batchId = ?"; + + " WHERE contactId = ? AND batchId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, b.getBytes()); rs = ps.executeQuery(); sql = "UPDATE statuses SET status = ?" - + " WHERE messageId = ? AND contactId = ? AND status = ?"; + + " WHERE messageId = ? AND contactId = ? AND status = ?"; ps1 = txn.prepareStatement(sql); ps1.setShort(1, (short) newStatus.ordinal()); ps1.setInt(3, c.getInt()); @@ -2171,7 +2117,7 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; try { String sql = "DELETE FROM batchesToAck" - + " WHERE contactId = ? and batchId = ?"; + + " WHERE contactId = ? and batchId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); for(BatchId b : sent) { @@ -2192,7 +2138,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public void removeContact(Connection txn, ContactId c) - throws DbException { + throws DbException { PreparedStatement ps = null; try { String sql = "DELETE FROM contacts WHERE contactId = ?"; @@ -2208,7 +2154,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public void removeLostBatch(Connection txn, ContactId c, BatchId b) - throws DbException { + throws DbException { removeBatch(txn, c, b, Status.NEW); } @@ -2228,14 +2174,14 @@ abstract class JdbcDatabase implements Database<Connection> { } public void removeSubscription(Connection txn, GroupId g) - throws DbException { + throws DbException { PreparedStatement ps = null, ps1 = null; ResultSet rs = null; try { // Remove the group ID from the visibility lists long now = clock.currentTimeMillis(); String sql = "SELECT contactId, nextId FROM visibilities" - + " WHERE groupId = ?"; + + " WHERE groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); rs = ps.executeQuery(); @@ -2243,7 +2189,7 @@ abstract class JdbcDatabase implements Database<Connection> { int contactId = rs.getInt(1); byte[] nextId = rs.getBytes(2); sql = "UPDATE visibilities SET nextId = ?, deleted = ?" - + " WHERE contactId = ? AND nextId = ?"; + + " WHERE contactId = ? AND nextId = ?"; ps1 = txn.prepareStatement(sql); if(nextId == null) ps1.setNull(1, Types.BINARY); // At the tail else ps1.setBytes(1, nextId); // At the head or in the middle @@ -2279,28 +2225,28 @@ abstract class JdbcDatabase implements Database<Connection> { if(start == null && end == null) { // Delete everything String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ?"; + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); } else if(start == null) { // Delete everything before end String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ? AND groupId < ?"; + + " WHERE contactId = ? AND groupId < ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, end.getBytes()); } else if(end == null) { // Delete everything after start String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ? AND groupId > ?"; + + " WHERE contactId = ? AND groupId > ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, start.getBytes()); } else { // Delete everything between start and end String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ?" - + " AND groupId > ? AND groupId < ?"; + + " WHERE contactId = ?" + + " AND groupId > ? AND groupId < ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, start.getBytes()); @@ -2316,13 +2262,13 @@ abstract class JdbcDatabase implements Database<Connection> { } public void removeVisibility(Connection txn, ContactId c, GroupId g) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { // Remove the group ID from the linked list String sql = "SELECT nextId FROM visibilities" - + " WHERE contactId = ? AND groupId = ?"; + + " WHERE contactId = ? AND groupId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, g.getBytes()); @@ -2333,7 +2279,7 @@ abstract class JdbcDatabase implements Database<Connection> { rs.close(); ps.close(); sql = "DELETE FROM visibilities" - + " WHERE contactId = ? AND groupId = ?"; + + " WHERE contactId = ? AND groupId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setBytes(2, g.getBytes()); @@ -2341,7 +2287,7 @@ abstract class JdbcDatabase implements Database<Connection> { if(affected != 1) throw new DbStateException(); ps.close(); sql = "UPDATE visibilities SET nextId = ?, deleted = ?" - + " WHERE contactId = ? AND nextId = ?"; + + " WHERE contactId = ? AND nextId = ?"; ps = txn.prepareStatement(sql); if(nextId == null) ps.setNull(1, Types.BINARY); // At the tail else ps.setBytes(1, nextId); // At the head or in the middle @@ -2359,7 +2305,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public void setConfig(Connection txn, TransportId t, TransportConfig c) - throws DbException { + throws DbException { PreparedStatement ps = null; try { // Delete any existing config for the given transport @@ -2370,7 +2316,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); // Store the new config sql = "INSERT INTO transportConfigs (transportId, key, value)" - + " VALUES (?, ?, ?)"; + + " VALUES (?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, t.getBytes()); for(Entry<String, String> e : c.entrySet()) { @@ -2379,8 +2325,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.addBatch(); } int[] batchAffected = ps.executeBatch(); - if(batchAffected.length != c.size()) - throw new DbStateException(); + if(batchAffected.length != c.size()) throw new DbStateException(); for(int i = 0; i < batchAffected.length; i++) { if(batchAffected[i] != 1) throw new DbStateException(); } @@ -2391,37 +2336,20 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public void setConnectionWindow(Connection txn, ContactId c, - TransportIndex i, ConnectionWindow w) throws DbException { + public void setConnectionWindow(Connection txn, ContactId c, TransportId t, + long period, long centre, byte[] bitmap) throws DbException { PreparedStatement ps = null; try { - // Delete any existing connection window - String sql = "DELETE FROM connectionWindows" - + " WHERE contactId = ? AND index = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setInt(2, i.getInt()); - ps.executeUpdate(); - ps.close(); - // Store the new connection window - sql = "INSERT INTO connectionWindows" - + " (contactId, index, connection, secret)" - + " VALUES(?, ?, ?, ?)"; + String sql = "UPDATE secrets SET centre = ? AND bitmap = ?" + + " WHERE contactId = ? AND transportId = ? AND period = ?"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setInt(2, i.getInt()); - Map<Long, byte[]> unseen = w.getUnseen(); - for(Entry<Long, byte[]> e : unseen.entrySet()) { - ps.setLong(3, e.getKey()); - ps.setBytes(4, e.getValue()); - ps.addBatch(); - } - int[] affectedBatch = ps.executeBatch(); - if(affectedBatch.length != unseen.size()) - throw new DbStateException(); - for(int j = 0; j < affectedBatch.length; j++) { - if(affectedBatch[j] != 1) throw new DbStateException(); - } + ps.setLong(1, centre); + ps.setBytes(2, bitmap); + ps.setInt(3, c.getInt()); + ps.setBytes(4, t.getBytes()); + ps.setLong(5, period); + int affected = ps.executeUpdate(); + if(affected > 1) throw new DbStateException(); ps.close(); } catch(SQLException e) { tryToClose(ps); @@ -2430,11 +2358,11 @@ abstract class JdbcDatabase implements Database<Connection> { } public void setExpiryTime(Connection txn, ContactId c, long expiry) - throws DbException { + throws DbException { PreparedStatement ps = null; try { String sql = "UPDATE subscriptionTimes SET expiry = ?" - + " WHERE contactId = ?"; + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setLong(1, expiry); ps.setInt(2, c.getInt()); @@ -2453,14 +2381,14 @@ abstract class JdbcDatabase implements Database<Connection> { try { // Delete any existing properties for the given transport String sql = "DELETE FROM transportProperties" - + " WHERE transportId = ?"; + + " WHERE transportId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, t.getBytes()); ps.executeUpdate(); ps.close(); // Store the new properties sql = "INSERT INTO transportProperties (transportId, key, value)" - + " VALUES (?, ?, ?)"; + + " VALUES (?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, t.getBytes()); for(Entry<String, String> e : p.entrySet()) { @@ -2469,8 +2397,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.addBatch(); } int[] batchAffected = ps.executeBatch(); - if(batchAffected.length != p.size()) - throw new DbStateException(); + if(batchAffected.length != p.size()) throw new DbStateException(); for(int i = 0; i < batchAffected.length; i++) { if(batchAffected[i] != 1) throw new DbStateException(); } @@ -2482,7 +2409,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public Rating setRating(Connection txn, AuthorId a, Rating r) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -2513,7 +2440,7 @@ abstract class JdbcDatabase implements Database<Connection> { old = Rating.UNRATED; if(!old.equals(r)) { sql = "INSERT INTO ratings (authorId, rating)" - + " VALUES (?, ?)"; + + " VALUES (?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, a.getBytes()); ps.setShort(2, (short) r.ordinal()); @@ -2531,7 +2458,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public boolean setRead(Connection txn, MessageId m, boolean read) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -2562,7 +2489,7 @@ abstract class JdbcDatabase implements Database<Connection> { old = false; if(old != read) { sql = "INSERT INTO flags (messageId, read, starred)" - + " VALUES (?, ?, ?)"; + + " VALUES (?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setBoolean(2, read); @@ -2581,11 +2508,11 @@ abstract class JdbcDatabase implements Database<Connection> { } public void setSendability(Connection txn, MessageId m, int sendability) - throws DbException { + throws DbException { PreparedStatement ps = null; try { String sql = "UPDATE messages SET sendability = ?" - + " WHERE messageId = ?"; + + " WHERE messageId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, sendability); ps.setBytes(2, m.getBytes()); @@ -2599,7 +2526,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public boolean setStarred(Connection txn, MessageId m, boolean starred) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -2630,7 +2557,7 @@ abstract class JdbcDatabase implements Database<Connection> { old = false; if(old != starred) { sql = "INSERT INTO flags (messageId, read, starred)" - + " VALUES (?, ?, ?)"; + + " VALUES (?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setBoolean(2, false); @@ -2649,12 +2576,12 @@ abstract class JdbcDatabase implements Database<Connection> { } public void setStatus(Connection txn, ContactId c, MessageId m, Status s) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT status FROM statuses" - + " WHERE messageId = ? AND contactId = ?"; + + " WHERE messageId = ? AND contactId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setInt(2, c.getInt()); @@ -2667,7 +2594,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); if(!old.equals(Status.SEEN) && !old.equals(s)) { sql = "UPDATE statuses SET status = ?" - + " WHERE messageId = ? AND contactId = ?"; + + " WHERE messageId = ? AND contactId = ?"; ps = txn.prepareStatement(sql); ps.setShort(1, (short) s.ordinal()); ps.setBytes(2, m.getBytes()); @@ -2681,7 +2608,7 @@ abstract class JdbcDatabase implements Database<Connection> { rs.close(); ps.close(); sql = "INSERT INTO statuses (messageId, contactId, status)" - + " VALUES (?, ?, ?)"; + + " VALUES (?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setInt(2, c.getInt()); @@ -2702,18 +2629,18 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT NULL FROM messages" - + " JOIN contactSubscriptions AS cs" - + " ON messages.groupId = cs.groupId" - + " JOIN visibilities" - + " ON messages.groupId = visibilities.groupId" - + " AND cs.contactId = visibilities.contactId" - + " JOIN subscriptionTimes" - + " ON cs.contactId = subscriptionTimes.contactId" - + " WHERE messageId = ?" - + " AND cs.contactId = ?" - + " AND timestamp >= start" - + " AND timestamp >= expiry"; + String sql = "SELECT NULL FROM messages AS m" + + " JOIN contactSubscriptions AS cs" + + " ON m.groupId = cs.groupId" + + " JOIN visibilities AS v" + + " ON m.groupId = v.groupId" + + " AND cs.contactId = v.contactId" + + " JOIN subscriptionTimes AS st" + + " ON cs.contactId = st.contactId" + + " WHERE messageId = ?" + + " AND cs.contactId = ?" + + " AND timestamp >= start" + + " AND timestamp >= expiry"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setInt(2, c.getInt()); @@ -2724,7 +2651,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); if(!found) return false; sql = "UPDATE statuses SET status = ?" - + " WHERE messageId = ? AND contactId = ?"; + + " WHERE messageId = ? AND contactId = ?"; ps = txn.prepareStatement(sql); ps.setShort(1, (short) Status.SEEN.ordinal()); ps.setBytes(2, m.getBytes()); @@ -2745,7 +2672,7 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; try { String sql = "UPDATE subscriptionTimes SET acked = ?" - + " WHERE contactId = ?"; + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setLong(1, timestamp); ps.setInt(2, c.getInt()); @@ -2763,7 +2690,7 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; try { String sql = "UPDATE subscriptionTimes SET received = ?" - + " WHERE contactId = ?"; + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setLong(1, timestamp); ps.setInt(2, c.getInt()); @@ -2778,13 +2705,13 @@ abstract class JdbcDatabase implements Database<Connection> { public void setTransports(Connection txn, ContactId c, Collection<Transport> transports, long timestamp) - throws DbException { + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { // Return if the timestamp isn't fresh String sql = "SELECT received FROM transportTimestamps" - + " WHERE contactId = ?"; + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); rs = ps.executeQuery(); @@ -2794,38 +2721,16 @@ abstract class JdbcDatabase implements Database<Connection> { rs.close(); ps.close(); if(lastTimestamp >= timestamp) return; - // Delete any existing transports - sql = "DELETE FROM contactTransports WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.executeUpdate(); - ps.close(); // Delete any existing transport properties sql = "DELETE FROM contactTransportProperties WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.executeUpdate(); ps.close(); - // Store the new transports - sql = "INSERT INTO contactTransports" - + " (contactId, transportId, index) VALUES (?, ?, ?)"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - for(Transport t : transports) { - ps.setBytes(2, t.getId().getBytes()); - ps.setInt(3, t.getIndex().getInt()); - ps.addBatch(); - } - int[] batchAffected = ps.executeBatch(); - if(batchAffected.length != transports.size()) - throw new DbStateException(); - for(int i = 0; i < batchAffected.length; i++) { - if(batchAffected[i] != 1) throw new DbStateException(); - } - ps.close(); // Store the new transport properties sql = "INSERT INTO contactTransportProperties" - + " (contactId, transportId, key, value) VALUES (?, ?, ?, ?)"; + + " (contactId, transportId, key, value)" + + " VALUES (?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); int batchSize = 0; @@ -2838,7 +2743,7 @@ abstract class JdbcDatabase implements Database<Connection> { batchSize++; } } - batchAffected = ps.executeBatch(); + int[] batchAffected = ps.executeBatch(); if(batchAffected.length != batchSize) throw new DbStateException(); for(int i = 0; i < batchAffected.length; i++) { if(batchAffected[i] != 1) throw new DbStateException(); @@ -2846,7 +2751,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.close(); // Update the timestamp sql = "UPDATE transportTimestamps SET received = ?" - + " WHERE contactId = ?"; + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setLong(1, timestamp); ps.setInt(2, c.getInt()); @@ -2861,7 +2766,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public void setTransportsModified(Connection txn, long timestamp) - throws DbException { + throws DbException { PreparedStatement ps = null; try { String sql = "UPDATE transportTimestamps set modified = ?"; @@ -2876,11 +2781,11 @@ abstract class JdbcDatabase implements Database<Connection> { } public void setTransportsSent(Connection txn, ContactId c, long timestamp) - throws DbException { + throws DbException { PreparedStatement ps = null; try { String sql = "UPDATE transportTimestamps SET sent = ?" - + " WHERE contactId = ? AND sent < ?"; + + " WHERE contactId = ? AND sent < ?"; ps = txn.prepareStatement(sql); ps.setLong(1, timestamp); ps.setInt(2, c.getInt()); diff --git a/components/net/sf/briar/plugins/InvitationStarterImpl.java b/components/net/sf/briar/plugins/InvitationStarterImpl.java deleted file mode 100644 index f67be85716375930e82b7ecff9c4624a2779a917..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/plugins/InvitationStarterImpl.java +++ /dev/null @@ -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; - } - } -} diff --git a/components/net/sf/briar/plugins/PluginManagerImpl.java b/components/net/sf/briar/plugins/PluginManagerImpl.java index ab05b6de48f47c5fe0bec1a36ec412a67c3f0c4e..79782b912030a70a9c6b27256a78abb0d6dd5151 100644 --- a/components/net/sf/briar/plugins/PluginManagerImpl.java +++ b/components/net/sf/briar/plugins/PluginManagerImpl.java @@ -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); } } } \ No newline at end of file diff --git a/components/net/sf/briar/plugins/email/GmailPlugin.java b/components/net/sf/briar/plugins/email/GmailPlugin.java index 323afd885df4da6cec3a0efb10a2c2c1059f348d..32bda238e56ebdf5df4c9ef0c4e0d42012535b34 100644 --- a/components/net/sf/briar/plugins/email/GmailPlugin.java +++ b/components/net/sf/briar/plugins/email/GmailPlugin.java @@ -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; diff --git a/components/net/sf/briar/plugins/email/PipeDataSource.java b/components/net/sf/briar/plugins/email/PipeDataSource.java deleted file mode 100644 index 06f23cd896521979a5393bb7a4b78f46e390e657..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/plugins/email/PipeDataSource.java +++ /dev/null @@ -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; - } - - - -} diff --git a/components/net/sf/briar/protocol/ProtocolWriterImpl.java b/components/net/sf/briar/protocol/ProtocolWriterImpl.java index 862ee880bd584fa6e728d8388e9da0781e54d434..d7bebf4e2e64c5edb3b39aa82e735ab919272605 100644 --- a/components/net/sf/briar/protocol/ProtocolWriterImpl.java +++ b/components/net/sf/briar/protocol/ProtocolWriterImpl.java @@ -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(); diff --git a/components/net/sf/briar/protocol/TransportUpdateReader.java b/components/net/sf/briar/protocol/TransportUpdateReader.java index 63e6b899c402a258e6baebb3be203bae51cd7648..685a00b1463273740428a9572d56e0fe7357564b 100644 --- a/components/net/sf/briar/protocol/TransportUpdateReader.java +++ b/components/net/sf/briar/protocol/TransportUpdateReader.java @@ -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); } } } diff --git a/components/net/sf/briar/protocol/duplex/DuplexConnection.java b/components/net/sf/briar/protocol/duplex/DuplexConnection.java index bb172db2804891122e528474d30c35d0e5cc087a..6cb64768d3a9cd8a259f0c73dce4bdbfe8b28779 100644 --- a/components/net/sf/briar/protocol/duplex/DuplexConnection.java +++ b/components/net/sf/briar/protocol/duplex/DuplexConnection.java @@ -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"); diff --git a/components/net/sf/briar/protocol/duplex/DuplexConnectionFactoryImpl.java b/components/net/sf/briar/protocol/duplex/DuplexConnectionFactoryImpl.java index ed4f08f1fb52b2f332f5e5c8852c0a8e105a48bb..73aa74d16c7a2a2d54412505bdf93ecdc09df965 100644 --- a/components/net/sf/briar/protocol/duplex/DuplexConnectionFactoryImpl.java +++ b/components/net/sf/briar/protocol/duplex/DuplexConnectionFactoryImpl.java @@ -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(); } - } diff --git a/components/net/sf/briar/protocol/duplex/IncomingDuplexConnection.java b/components/net/sf/briar/protocol/duplex/IncomingDuplexConnection.java index 3a888eaa7fa5f487830fe53111119449762c921a..75fc8932aa650146def75c0ea84291baab60464d 100644 --- a/components/net/sf/briar/protocol/duplex/IncomingDuplexConnection.java +++ b/components/net/sf/briar/protocol/duplex/IncomingDuplexConnection.java @@ -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); } } diff --git a/components/net/sf/briar/protocol/duplex/OutgoingDuplexConnection.java b/components/net/sf/briar/protocol/duplex/OutgoingDuplexConnection.java index b784b8115eda9f7c67273e5dd6f6ad51cd2d7c4c..e3f9c8c723c0a97109e03501f2bc3786706f7330 100644 --- a/components/net/sf/briar/protocol/duplex/OutgoingDuplexConnection.java +++ b/components/net/sf/briar/protocol/duplex/OutgoingDuplexConnection.java @@ -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); } } diff --git a/components/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java b/components/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java index a15c7bd8dd80d60a3adea1d2f09cec1613ac79da..e83b36e1bf71ade162cc4b265a6562683cd2eea5 100644 --- a/components/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java +++ b/components/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java @@ -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 diff --git a/components/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java b/components/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java index 6333a700ed8de6937a866d5e3d69e05b6bbdbac4..fc0e926e4d886420d4c43475380cb016ec7ac2fc 100644 --- a/components/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java +++ b/components/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java @@ -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()); diff --git a/components/net/sf/briar/protocol/simplex/SimplexConnectionFactoryImpl.java b/components/net/sf/briar/protocol/simplex/SimplexConnectionFactoryImpl.java index 1c08ffe8942e1658f31c7f47065e90b4418fc7a7..c20d8b15bd601ae54ce25f66bac56d5ce70dd1f8 100644 --- a/components/net/sf/briar/protocol/simplex/SimplexConnectionFactoryImpl.java +++ b/components/net/sf/briar/protocol/simplex/SimplexConnectionFactoryImpl.java @@ -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(); diff --git a/components/net/sf/briar/transport/ConnectionContextFactoryImpl.java b/components/net/sf/briar/transport/ConnectionContextFactoryImpl.java deleted file mode 100644 index f50d5193c6e79221fc1cdd57c5a8f763209e81d1..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/transport/ConnectionContextFactoryImpl.java +++ /dev/null @@ -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); - } -} diff --git a/components/net/sf/briar/transport/ConnectionContextImpl.java b/components/net/sf/briar/transport/ConnectionContextImpl.java deleted file mode 100644 index 27e8caee68a3680890f329aaf3cb5e4fd14f3c81..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/transport/ConnectionContextImpl.java +++ /dev/null @@ -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; - } -} diff --git a/components/net/sf/briar/transport/ConnectionDispatcherImpl.java b/components/net/sf/briar/transport/ConnectionDispatcherImpl.java index 1ac49b86b79ad9a37bf4c2831bed09518578fb32..2eb5849de53c9cd0f879baddf877e970eb95b84d 100644 --- a/components/net/sf/briar/transport/ConnectionDispatcherImpl.java +++ b/components/net/sf/briar/transport/ConnectionDispatcherImpl.java @@ -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); diff --git a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java index fc951da24a9ab393516db85b478e25a1bf0f9e32..c6fae40198acdf33190d326bdc1094bcb962c26a 100644 --- a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java +++ b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java @@ -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); } } diff --git a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java deleted file mode 100644 index 5babc2278cd3c3f1d33e9fe9e53b2b4cb97f504c..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java +++ /dev/null @@ -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; - } - } -} diff --git a/components/net/sf/briar/transport/ConnectionWindowFactoryImpl.java b/components/net/sf/briar/transport/ConnectionWindowFactoryImpl.java deleted file mode 100644 index 2890e04272535ce2ba558a189f2dce08274ad898..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/transport/ConnectionWindowFactoryImpl.java +++ /dev/null @@ -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); - } -} diff --git a/components/net/sf/briar/transport/ConnectionWindowImpl.java b/components/net/sf/briar/transport/ConnectionWindowImpl.java index d20eca411de139ec13e7a44e25e29101c86ee498..057cd9335a380f8dd81fe2b52d6478e374717c7f 100644 --- a/components/net/sf/briar/transport/ConnectionWindowImpl.java +++ b/components/net/sf/briar/transport/ConnectionWindowImpl.java @@ -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); - } } diff --git a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java index 415eb2fdf74ece8fa3f2be2f1cfc1309b50c854f..d853371d8a28e5818a69033d107acbb505b8ccc8 100644 --- a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java +++ b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java @@ -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); } } \ No newline at end of file diff --git a/components/net/sf/briar/transport/IncomingEncryptionLayer.java b/components/net/sf/briar/transport/IncomingEncryptionLayer.java index 4b36a89d26bb633fc078e1a90f29a1970fa9613b..6d446a8a5faeec0795092fc09acd3c0b33c74c08 100644 --- a/components/net/sf/briar/transport/IncomingEncryptionLayer.java +++ b/components/net/sf/briar/transport/IncomingEncryptionLayer.java @@ -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; diff --git a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java index d691bd39bccccf836674835964af43c97162d2bd..a9c45fd961db375bfa0eb2e07148d9c52deaf94e 100644 --- a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java +++ b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java @@ -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) { diff --git a/components/net/sf/briar/transport/TransportModule.java b/components/net/sf/briar/transport/TransportModule.java index 89db1a259b7455e298f7202456a253928e339cb6..daceddd3b11022d6b7a6151e205e6df34cfe2875 100644 --- a/components/net/sf/briar/transport/TransportModule.java +++ b/components/net/sf/briar/transport/TransportModule.java @@ -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 diff --git a/test/build.xml b/test/build.xml index 1720cf75662cf75baefe82790924bf61ab92a639..b006dad3127a6a7ae3517660abd1c6697ab4f73a 100644 --- a/test/build.xml +++ b/test/build.xml @@ -19,6 +19,7 @@ <test name='net.sf.briar.crypto.CounterModeTest'/> <test name='net.sf.briar.crypto.ErasableKeyTest'/> <test name='net.sf.briar.crypto.KeyDerivationTest'/> + <test name='net.sf.briar.crypto.KeyRotatorImplTest'/> <test name='net.sf.briar.db.BasicH2Test'/> <test name='net.sf.briar.db.DatabaseCleanerImplTest'/> <test name='net.sf.briar.db.DatabaseComponentImplTest'/> @@ -46,7 +47,6 @@ <test name='net.sf.briar.serial.ReaderImplTest'/> <test name='net.sf.briar.serial.WriterImplTest'/> <test name='net.sf.briar.transport.ConnectionReaderImplTest'/> - <test name='net.sf.briar.transport.ConnectionRecogniserImplTest'/> <test name='net.sf.briar.transport.ConnectionRegistryImplTest'/> <test name='net.sf.briar.transport.ConnectionWindowImplTest'/> <test name='net.sf.briar.transport.ConnectionWriterImplTest'/> diff --git a/test/net/sf/briar/ProtocolIntegrationTest.java b/test/net/sf/briar/ProtocolIntegrationTest.java index 5f742831710fefde2f4b36d7a840c1ddc1adf490..e03a3389dd80959c95e2ec23c688e8ffe38cc26f 100644 --- a/test/net/sf/briar/ProtocolIntegrationTest.java +++ b/test/net/sf/briar/ProtocolIntegrationTest.java @@ -17,6 +17,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Random; +import net.sf.briar.api.ContactId; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Author; @@ -40,8 +41,8 @@ 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.ConnectionReader; import net.sf.briar.api.transport.ConnectionReaderFactory; import net.sf.briar.api.transport.ConnectionWriter; @@ -72,8 +73,9 @@ public class ProtocolIntegrationTest extends BriarTestCase { private final ProtocolWriterFactory protocolWriterFactory; private final PacketFactory packetFactory; private final CryptoComponent crypto; + private final ContactId contactId; + private final TransportId transportId; private final byte[] secret; - private final TransportIndex transportIndex = new TransportIndex(13); private final Author author; private final Group group, group1; private final Message message, message1, message2, message3; @@ -95,6 +97,8 @@ public class ProtocolIntegrationTest extends BriarTestCase { protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class); packetFactory = i.getInstance(PacketFactory.class); crypto = i.getInstance(CryptoComponent.class); + contactId = new ContactId(234); + transportId = new TransportId(TestUtils.getRandomId()); // Create a shared secret Random r = new Random(); secret = new byte[32]; @@ -125,7 +129,7 @@ public class ProtocolIntegrationTest extends BriarTestCase { subject, messageBody.getBytes("UTF-8")); // Create some transports TransportId transportId = new TransportId(TestUtils.getRandomId()); - Transport transport = new Transport(transportId, transportIndex, + Transport transport = new Transport(transportId, Collections.singletonMap("bar", "baz")); transports = Collections.singletonList(transport); } @@ -137,8 +141,11 @@ public class ProtocolIntegrationTest extends BriarTestCase { private byte[] write() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] tag = new byte[TAG_LENGTH]; + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + tag, secret.clone(), 0L, true); ConnectionWriter conn = connectionWriterFactory.createConnectionWriter( - out, Long.MAX_VALUE, secret.clone(), true); + out, Long.MAX_VALUE, ctx, true); OutputStream out1 = conn.getOutputStream(); ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out1, false); @@ -190,8 +197,11 @@ public class ProtocolIntegrationTest extends BriarTestCase { InputStream in = new ByteArrayInputStream(connectionData); byte[] tag = new byte[TAG_LENGTH]; assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH)); + assertArrayEquals(new byte[TAG_LENGTH], tag); + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + tag, secret.clone(), 0L, true); ConnectionReader conn = connectionReaderFactory.createConnectionReader( - in, secret.clone(), true); + in, ctx, true); InputStream in1 = conn.getInputStream(); ProtocolReader reader = protocolReaderFactory.createProtocolReader(in1); diff --git a/test/net/sf/briar/crypto/KeyDerivationTest.java b/test/net/sf/briar/crypto/KeyDerivationTest.java index cc4335c9ae36e2843451b7d1fd8f28516da4abb0..49dda92c8e4625052c11e759d91a0a723b83fc29 100644 --- a/test/net/sf/briar/crypto/KeyDerivationTest.java +++ b/test/net/sf/briar/crypto/KeyDerivationTest.java @@ -8,7 +8,6 @@ import java.util.Random; import net.sf.briar.BriarTestCase; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.ErasableKey; -import net.sf.briar.api.protocol.ProtocolConstants; import org.junit.Test; @@ -27,8 +26,10 @@ public class KeyDerivationTest extends BriarTestCase { @Test public void testKeysAreDistinct() { List<ErasableKey> keys = new ArrayList<ErasableKey>(); - keys.add(crypto.deriveFrameKey(secret, true)); - keys.add(crypto.deriveFrameKey(secret, false)); + keys.add(crypto.deriveFrameKey(secret, 0, false, false)); + keys.add(crypto.deriveFrameKey(secret, 0, false, true)); + keys.add(crypto.deriveFrameKey(secret, 0, true, false)); + keys.add(crypto.deriveFrameKey(secret, 0, true, true)); keys.add(crypto.deriveTagKey(secret, true)); keys.add(crypto.deriveTagKey(secret, false)); for(int i = 0; i < 4; i++) { @@ -47,7 +48,7 @@ public class KeyDerivationTest extends BriarTestCase { for(int i = 0; i < 20; i++) { byte[] b = new byte[32]; r.nextBytes(b); - secrets.add(crypto.deriveNextSecret(b, 0, 0)); + secrets.add(crypto.deriveNextSecret(b, 0)); } for(int i = 0; i < 20; i++) { byte[] secretI = secrets.get(i); @@ -58,26 +59,11 @@ public class KeyDerivationTest extends BriarTestCase { } } - @Test - public void testTransportIndexAffectsDerivation() { - List<byte[]> secrets = new ArrayList<byte[]>(); - for(int i = 0; i < ProtocolConstants.MAX_TRANSPORTS; i++) { - secrets.add(crypto.deriveNextSecret(secret, i, 0)); - } - for(int i = 0; i < ProtocolConstants.MAX_TRANSPORTS; i++) { - byte[] secretI = secrets.get(i); - for(int j = 0; j < ProtocolConstants.MAX_TRANSPORTS; j++) { - byte[] secretJ = secrets.get(j); - assertEquals(i == j, Arrays.equals(secretI, secretJ)); - } - } - } - @Test public void testConnectionNumberAffectsDerivation() { List<byte[]> secrets = new ArrayList<byte[]>(); for(int i = 0; i < 20; i++) { - secrets.add(crypto.deriveNextSecret(secret, 0, i)); + secrets.add(crypto.deriveNextSecret(secret, i)); } for(int i = 0; i < 20; i++) { byte[] secretI = secrets.get(i); diff --git a/test/net/sf/briar/db/KeyRotatorImplTest.java b/test/net/sf/briar/crypto/KeyRotatorImplTest.java similarity index 92% rename from test/net/sf/briar/db/KeyRotatorImplTest.java rename to test/net/sf/briar/crypto/KeyRotatorImplTest.java index 2470e534e6c1fb7da634552a375888451a96e12e..3e124b2e8e4bd8c123d31c7182a86568dde253d1 100644 --- a/test/net/sf/briar/db/KeyRotatorImplTest.java +++ b/test/net/sf/briar/crypto/KeyRotatorImplTest.java @@ -1,11 +1,12 @@ -package net.sf.briar.db; +package net.sf.briar.crypto; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import net.sf.briar.BriarTestCase; import net.sf.briar.api.db.DbException; -import net.sf.briar.db.KeyRotator.Callback; +import net.sf.briar.crypto.KeyRotatorImpl; +import net.sf.briar.crypto.KeyRotator.Callback; import org.junit.Test; diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java index 013fcf2c080a966ab0092b88718c89bb371d189f..3918650be8e6fd957612e5cb18b31366995394c6 100644 --- a/test/net/sf/briar/db/DatabaseComponentTest.java +++ b/test/net/sf/briar/db/DatabaseComponentTest.java @@ -23,7 +23,6 @@ import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.db.event.RatingChangedEvent; 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; @@ -40,9 +39,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.ConnectionWindow; import org.jmock.Expectations; import org.jmock.Mockery; @@ -63,7 +60,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { private final Message message, privateMessage; private final Group group; private final TransportId transportId; - private final TransportIndex localIndex, remoteIndex; private final Collection<Transport> transports; private final Map<ContactId, TransportProperties> remoteProperties; private final byte[] inSecret, outSecret; @@ -72,7 +68,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { super(); authorId = new AuthorId(TestUtils.getRandomId()); batchId = new BatchId(TestUtils.getRandomId()); - contactId = new ContactId(123); + contactId = new ContactId(234); groupId = new GroupId(TestUtils.getRandomId()); messageId = new MessageId(TestUtils.getRandomId()); parentId = new MessageId(TestUtils.getRandomId()); @@ -86,13 +82,10 @@ public abstract class DatabaseComponentTest extends BriarTestCase { timestamp, raw); group = new TestGroup(groupId, "The really exciting group", null); transportId = new TransportId(TestUtils.getRandomId()); - localIndex = new TransportIndex(0); - remoteIndex = new TransportIndex(13); TransportProperties properties = new TransportProperties( Collections.singletonMap("foo", "bar")); remoteProperties = Collections.singletonMap(contactId, properties); - Transport transport = new Transport(transportId, localIndex, - properties); + Transport transport = new Transport(transportId, properties); transports = Collections.singletonList(transport); Random r = new Random(); inSecret = new byte[32]; @@ -108,14 +101,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test @SuppressWarnings("unchecked") public void testSimpleCalls() throws Exception { + // FIXME: Test new methods final int shutdownHandle = 12345; Mockery context = new Mockery(); final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); final PacketFactory packetFactory = context.mock(PacketFactory.class); - final ConnectionWindow connectionWindow = - context.mock(ConnectionWindow.class); final Group group = context.mock(Group.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ @@ -142,18 +134,12 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).setRating(txn, authorId, Rating.GOOD); will(returnValue(Rating.GOOD)); // addContact() - oneOf(database).addContact(with(txn), with(inSecret), - with(outSecret), with(any(Collection.class))); + oneOf(database).addContact(txn); will(returnValue(contactId)); oneOf(listener).eventOccurred(with(any(ContactAddedEvent.class))); // getContacts() oneOf(database).getContacts(txn); will(returnValue(Collections.singletonList(contactId))); - // getConnectionWindow(contactId, 13) - oneOf(database).containsContact(txn, contactId); - will(returnValue(true)); - oneOf(database).getConnectionWindow(txn, contactId, remoteIndex); - will(returnValue(connectionWindow)); // getTransportProperties(transportId) oneOf(database).getRemoteProperties(txn, transportId); will(returnValue(remoteProperties)); @@ -183,11 +169,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // unsubscribe(groupId) again oneOf(database).containsSubscription(txn, groupId); will(returnValue(false)); - // setConnectionWindow(contactId, 13, connectionWindow) - oneOf(database).containsContact(txn, contactId); - will(returnValue(true)); - oneOf(database).setConnectionWindow(txn, contactId, remoteIndex, - connectionWindow); // removeContact(contactId) oneOf(database).containsContact(txn, contactId); will(returnValue(true)); @@ -206,10 +187,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase { assertEquals(Rating.UNRATED, db.getRating(authorId)); db.setRating(authorId, Rating.GOOD); // First time - listeners called db.setRating(authorId, Rating.GOOD); // Second time - not called - assertEquals(contactId, db.addContact(inSecret, outSecret)); + assertEquals(contactId, db.addContact()); assertEquals(Collections.singletonList(contactId), db.getContacts()); - assertEquals(connectionWindow, - db.getConnectionWindow(contactId, remoteIndex)); assertEquals(remoteProperties, db.getRemoteProperties(transportId)); db.subscribe(group); // First time - listeners called db.subscribe(group); // Second time - not called @@ -217,7 +196,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { assertEquals(Collections.singletonList(groupId), db.getSubscriptions()); db.unsubscribe(groupId); // First time - listeners called db.unsubscribe(groupId); // Second time - not called - db.setConnectionWindow(contactId, remoteIndex, connectionWindow); db.removeContact(contactId); db.removeListener(listener); db.close(); @@ -297,7 +275,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testAffectedParentContinuesBackwardInclusion() - throws Exception { + throws Exception { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -338,7 +316,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testGroupMessagesAreNotStoredUnlessSubscribed() - throws Exception { + throws Exception { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -424,7 +402,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testAddingSendableMessageTriggersBackwardInclusion() - throws Exception { + throws Exception { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -516,7 +494,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testVariousMethodsThrowExceptionIfContactIsMissing() - throws Exception { + throws Exception { + // FIXME: Test new methods Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -527,16 +506,16 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Batch batch = context.mock(Batch.class); final Offer offer = context.mock(Offer.class); final SubscriptionUpdate subscriptionUpdate = - context.mock(SubscriptionUpdate.class); + context.mock(SubscriptionUpdate.class); final TransportUpdate transportUpdate = - context.mock(TransportUpdate.class); + context.mock(TransportUpdate.class); context.checking(new Expectations() {{ // Check whether the contact is still in the DB (which it's not) - exactly(19).of(database).startTransaction(); + exactly(15).of(database).startTransaction(); will(returnValue(txn)); - exactly(19).of(database).containsContact(txn, contactId); + exactly(15).of(database).containsContact(txn, contactId); will(returnValue(false)); - exactly(19).of(database).commitTransaction(txn); + exactly(15).of(database).abortTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, shutdown, packetFactory); @@ -577,21 +556,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { fail(); } catch(NoSuchContactException expected) {} - try { - db.getConnectionContext(contactId, remoteIndex); - fail(); - } catch(NoSuchContactException expected) {} - - try { - db.getConnectionWindow(contactId, remoteIndex); - fail(); - } catch(NoSuchContactException expected) {} - - try { - db.getRemoteIndex(contactId, transportId); - fail(); - } catch(NoSuchContactException expected) {} - try { db.hasSendableMessages(contactId); fail(); @@ -627,11 +591,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { fail(); } catch(NoSuchContactException expected) {} - try { - db.setConnectionWindow(contactId, remoteIndex, null); - fail(); - } catch(NoSuchContactException expected) {} - try { db.setSeen(contactId, Collections.singletonList(messageId)); fail(); @@ -811,7 +770,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final ShutdownManager shutdown = context.mock(ShutdownManager.class); final PacketFactory packetFactory = context.mock(PacketFactory.class); final SubscriptionUpdate subscriptionUpdate = - context.mock(SubscriptionUpdate.class); + context.mock(SubscriptionUpdate.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -883,7 +842,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final ShutdownManager shutdown = context.mock(ShutdownManager.class); final PacketFactory packetFactory = context.mock(PacketFactory.class); final TransportUpdate transportUpdate = - context.mock(TransportUpdate.class); + context.mock(TransportUpdate.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1015,7 +974,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testReceiveBatchDoesNotStoreGroupMessageUnlessSubscribed() - throws Exception { + throws Exception { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -1050,7 +1009,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testReceiveBatchDoesNotCalculateSendabilityForDuplicates() - throws Exception { + throws Exception { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -1241,7 +1200,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final ShutdownManager shutdown = context.mock(ShutdownManager.class); final PacketFactory packetFactory = context.mock(PacketFactory.class); final SubscriptionUpdate subscriptionUpdate = - context.mock(SubscriptionUpdate.class); + context.mock(SubscriptionUpdate.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1281,7 +1240,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final ShutdownManager shutdown = context.mock(ShutdownManager.class); final PacketFactory packetFactory = context.mock(PacketFactory.class); final TransportUpdate transportUpdate = - context.mock(TransportUpdate.class); + context.mock(TransportUpdate.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1375,7 +1334,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testAddingDuplicateGroupMessageDoesNotCallListeners() - throws Exception { + throws Exception { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -1405,7 +1364,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testAddingDuplicatePrivateMessageDoesNotCallListeners() - throws Exception { + throws Exception { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -1435,16 +1394,15 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testTransportPropertiesChangedCallsListeners() - throws Exception { + throws Exception { final TransportProperties properties = - new TransportProperties(Collections.singletonMap("bar", "baz")); + new TransportProperties(Collections.singletonMap("bar", "baz")); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); final PacketFactory packetFactory = context.mock(PacketFactory.class); - final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ oneOf(database).startTransaction(); will(returnValue(txn)); @@ -1454,13 +1412,10 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).setTransportsModified(with(txn), with(any(long.class))); oneOf(database).commitTransaction(txn); - oneOf(listener).eventOccurred(with(any( - TransportAddedEvent.class))); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, shutdown, packetFactory); - db.addListener(listener); db.setLocalProperties(transportId, properties); context.assertIsSatisfied(); @@ -1468,9 +1423,9 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testTransportPropertiesUnchangedDoesNotCallListeners() - throws Exception { + throws Exception { final TransportProperties properties = - new TransportProperties(Collections.singletonMap("bar", "baz")); + new TransportProperties(Collections.singletonMap("bar", "baz")); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -1521,7 +1476,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testVisibilityChangedCallsListeners() throws Exception { - final ContactId contactId1 = new ContactId(234); + final ContactId contactId1 = new ContactId(123); final Collection<ContactId> both = Arrays.asList(new ContactId[] {contactId, contactId1}); Mockery context = new Mockery(); diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java index 0451fe9f5b688ac0046e4f964632e73160f1a7df..783487e262e3f621897be9fb3e773954ff2f4c78 100644 --- a/test/net/sf/briar/db/H2DatabaseTest.java +++ b/test/net/sf/briar/db/H2DatabaseTest.java @@ -3,6 +3,7 @@ package net.sf.briar.db; import static org.junit.Assert.assertArrayEquals; import java.io.File; +import java.io.IOException; import java.sql.Connection; import java.util.ArrayList; import java.util.Arrays; @@ -18,7 +19,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import net.sf.briar.BriarTestCase; -import net.sf.briar.TestDatabaseModule; import net.sf.briar.TestUtils; import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; @@ -38,28 +38,12 @@ 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.ConnectionContextFactory; -import net.sf.briar.api.transport.ConnectionWindow; -import net.sf.briar.api.transport.ConnectionWindowFactory; -import net.sf.briar.api.transport.TransportConstants; -import net.sf.briar.clock.ClockModule; -import net.sf.briar.crypto.CryptoModule; -import net.sf.briar.lifecycle.LifecycleModule; -import net.sf.briar.protocol.ProtocolModule; -import net.sf.briar.protocol.duplex.DuplexProtocolModule; -import net.sf.briar.protocol.simplex.SimplexProtocolModule; -import net.sf.briar.serial.SerialModule; -import net.sf.briar.transport.TransportModule; import org.apache.commons.io.FileSystemUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; -import com.google.inject.Guice; -import com.google.inject.Injector; - public class H2DatabaseTest extends BriarTestCase { private static final int ONE_MEGABYTE = 1024 * 1024; @@ -70,10 +54,8 @@ public class H2DatabaseTest extends BriarTestCase { private final String passwordString = "foo bar"; private final Password password = new TestPassword(); private final Random random = new Random(); - private final ConnectionContextFactory connectionContextFactory; - private final ConnectionWindowFactory connectionWindowFactory; private final GroupFactory groupFactory; - + private final Group group; private final AuthorId authorId; private final BatchId batchId; private final ContactId contactId; @@ -84,33 +66,21 @@ public class H2DatabaseTest extends BriarTestCase { private final int size; private final byte[] raw; private final Message message, privateMessage; - private final Group group; private final TransportId transportId; - private final TransportIndex localIndex, remoteIndex; private final TransportProperties properties; private final Map<ContactId, TransportProperties> remoteProperties; private final Collection<Transport> remoteTransports; - private final byte[] inSecret, outSecret; - private final Collection<byte[]> erase; public H2DatabaseTest() throws Exception { super(); - // FIXME: Use mocks for the factories rather than building the whole app - Injector i = Guice.createInjector(new ClockModule(), new CryptoModule(), - new DatabaseModule(), new LifecycleModule(), - new ProtocolModule(), new SerialModule(), - new SimplexProtocolModule(), new TransportModule(), - new DuplexProtocolModule(), new TestDatabaseModule(testDir)); - connectionContextFactory = - i.getInstance(ConnectionContextFactory.class); - connectionWindowFactory = i.getInstance(ConnectionWindowFactory.class); - groupFactory = i.getInstance(GroupFactory.class); + groupFactory = new TestGroupFactory(); authorId = new AuthorId(TestUtils.getRandomId()); batchId = new BatchId(TestUtils.getRandomId()); contactId = new ContactId(1); groupId = new GroupId(TestUtils.getRandomId()); messageId = new MessageId(TestUtils.getRandomId()); privateMessageId = new MessageId(TestUtils.getRandomId()); + group = new TestGroup(groupId, "Foo", null); subject = "Foo"; timestamp = System.currentTimeMillis(); size = 1234; @@ -120,22 +90,12 @@ public class H2DatabaseTest extends BriarTestCase { timestamp, raw); privateMessage = new TestMessage(privateMessageId, null, null, null, subject, timestamp, raw); - group = groupFactory.createGroup(groupId, "Group name", null); transportId = new TransportId(TestUtils.getRandomId()); - localIndex = new TransportIndex(1); - remoteIndex = new TransportIndex(13); properties = new TransportProperties( Collections.singletonMap("foo", "bar")); remoteProperties = Collections.singletonMap(contactId, properties); - Transport remoteTransport = new Transport(transportId, remoteIndex, - properties); + Transport remoteTransport = new Transport(transportId, properties); remoteTransports = Collections.singletonList(remoteTransport); - Random r = new Random(); - inSecret = new byte[32]; - r.nextBytes(inSecret); - outSecret = new byte[32]; - r.nextBytes(outSecret); - erase = new ArrayList<byte[]>(); } @Before @@ -149,7 +109,7 @@ public class H2DatabaseTest extends BriarTestCase { Database<Connection> db = open(false); Connection txn = db.startTransaction(); assertFalse(db.containsContact(txn, contactId)); - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); assertTrue(db.containsContact(txn, contactId)); assertFalse(db.containsSubscription(txn, groupId)); db.addSubscription(txn, group); @@ -205,23 +165,20 @@ public class H2DatabaseTest extends BriarTestCase { // Create three contacts assertFalse(db.containsContact(txn, contactId)); - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); assertTrue(db.containsContact(txn, contactId)); assertFalse(db.containsContact(txn, contactId1)); - assertEquals(contactId1, - db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId1, db.addContact(txn)); assertTrue(db.containsContact(txn, contactId1)); assertFalse(db.containsContact(txn, contactId2)); - assertEquals(contactId2, - db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId2, db.addContact(txn)); assertTrue(db.containsContact(txn, contactId2)); // Delete the contact with the highest ID db.removeContact(txn, contactId2); assertFalse(db.containsContact(txn, contactId2)); // Add another contact - a new ID should be created assertFalse(db.containsContact(txn, contactId3)); - assertEquals(contactId3, - db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId3, db.addContact(txn)); assertTrue(db.containsContact(txn, contactId3)); db.commitTransaction(txn); @@ -268,7 +225,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact and store a private message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addPrivateMessage(txn, privateMessage, contactId); // Removing the contact should remove the message @@ -282,18 +239,18 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSendablePrivateMessagesMustHaveStatusNew() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact and store a private message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addPrivateMessage(txn, privateMessage, contactId); // The message has no status yet, so it should not be sendable assertFalse(db.hasSendableMessages(txn, contactId)); Iterator<MessageId> it = - db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertFalse(it.hasNext()); // Changing the status to NEW should make the message sendable @@ -321,19 +278,19 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSendablePrivateMessagesMustFitCapacity() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact and store a private message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addPrivateMessage(txn, privateMessage, contactId); db.setStatus(txn, contactId, privateMessageId, Status.NEW); // The message is sendable, but too large to send assertTrue(db.hasSendableMessages(txn, contactId)); Iterator<MessageId> it = - db.getSendableMessages(txn, contactId, size - 1).iterator(); + db.getSendableMessages(txn, contactId, size - 1).iterator(); assertFalse(it.hasNext()); // The message is just the right size to send @@ -349,12 +306,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSendableGroupMessagesMustHavePositiveSendability() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addSubscription(txn, contactId, group, 0L); @@ -364,7 +321,7 @@ public class H2DatabaseTest extends BriarTestCase { // The message should not be sendable assertFalse(db.hasSendableMessages(txn, contactId)); Iterator<MessageId> it = - db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertFalse(it.hasNext()); // Changing the sendability to > 0 should make the message sendable @@ -387,12 +344,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSendableGroupMessagesMustHaveStatusNew() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addSubscription(txn, contactId, group, 0L); @@ -402,7 +359,7 @@ public class H2DatabaseTest extends BriarTestCase { // The message has no status yet, so it should not be sendable assertFalse(db.hasSendableMessages(txn, contactId)); Iterator<MessageId> it = - db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertFalse(it.hasNext()); // Changing the status to Status.NEW should make the message sendable @@ -434,7 +391,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addGroupMessage(txn, message); @@ -444,7 +401,7 @@ public class H2DatabaseTest extends BriarTestCase { // The contact is not subscribed, so the message should not be sendable assertFalse(db.hasSendableMessages(txn, contactId)); Iterator<MessageId> it = - db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertFalse(it.hasNext()); // The contact subscribing should make the message sendable @@ -467,12 +424,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSendableGroupMessagesMustBeNewerThanSubscriptions() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addGroupMessage(txn, message); @@ -484,7 +441,7 @@ public class H2DatabaseTest extends BriarTestCase { db.addSubscription(txn, contactId, group, timestamp + 1); assertFalse(db.hasSendableMessages(txn, contactId)); Iterator<MessageId> it = - db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertFalse(it.hasNext()); // Changing the contact's subscription should make the message sendable @@ -506,7 +463,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addSubscription(txn, contactId, group, 0L); @@ -517,7 +474,7 @@ public class H2DatabaseTest extends BriarTestCase { // The message is sendable, but too large to send assertTrue(db.hasSendableMessages(txn, contactId)); Iterator<MessageId> it = - db.getSendableMessages(txn, contactId, size - 1).iterator(); + db.getSendableMessages(txn, contactId, size - 1).iterator(); assertFalse(it.hasNext()); // The message is just the right size to send @@ -537,7 +494,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addSubscription(txn, contactId, group, 0L); db.addGroupMessage(txn, message); @@ -548,7 +505,7 @@ public class H2DatabaseTest extends BriarTestCase { // should not be sendable assertFalse(db.hasSendableMessages(txn, contactId)); Iterator<MessageId> it = - db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertFalse(it.hasNext()); // Making the subscription visible should make the message sendable @@ -570,7 +527,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact and some batches to ack - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addBatchToAck(txn, contactId, batchId); db.addBatchToAck(txn, contactId, batchId1); @@ -597,7 +554,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact and receive the same batch twice - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addBatchToAck(txn, contactId, batchId); db.addBatchToAck(txn, contactId, batchId); @@ -623,7 +580,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addGroupMessage(txn, message); @@ -648,8 +605,8 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add two contacts, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); - ContactId contactId1 = db.addContact(txn, inSecret, outSecret, erase); + assertEquals(contactId, db.addContact(txn)); + ContactId contactId1 = db.addContact(txn); db.addSubscription(txn, group); db.addGroupMessage(txn, message); @@ -671,7 +628,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addSubscription(txn, contactId, group, 0L); @@ -681,7 +638,7 @@ public class H2DatabaseTest extends BriarTestCase { // Retrieve the message from the database and mark it as sent Iterator<MessageId> it = - db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertTrue(it.hasNext()); assertEquals(messageId, it.next()); assertFalse(it.hasNext()); @@ -710,7 +667,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addSubscription(txn, contactId, group, 0L); @@ -720,7 +677,7 @@ public class H2DatabaseTest extends BriarTestCase { // Get the message and mark it as sent Iterator<MessageId> it = - db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertTrue(it.hasNext()); assertEquals(messageId, it.next()); assertFalse(it.hasNext()); @@ -755,7 +712,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); // Add some outstanding batches, a few ms apart for(int i = 0; i < ids.length; i++) { @@ -795,7 +752,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); // Add some outstanding batches, a few ms apart for(int i = 0; i < ids.length; i++) { @@ -832,7 +789,7 @@ public class H2DatabaseTest extends BriarTestCase { // Check that each message is retrievable via its author Iterator<MessageId> it = - db.getMessagesByAuthor(txn, authorId).iterator(); + db.getMessagesByAuthor(txn, authorId).iterator(); assertTrue(it.hasNext()); assertEquals(messageId, it.next()); assertFalse(it.hasNext()); @@ -1021,35 +978,29 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact with a transport - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.setTransports(txn, contactId, remoteTransports, 1); assertEquals(remoteProperties, db.getRemoteProperties(txn, transportId)); // Replace the transport properties TransportProperties properties1 = - new TransportProperties(Collections.singletonMap("baz", "bam")); - Transport remoteTransport1 = - new Transport(transportId, remoteIndex, properties1); + new TransportProperties(Collections.singletonMap("baz", "bam")); + Transport remoteTransport1 = new Transport(transportId, properties1); Collection<Transport> remoteTransports1 = - Collections.singletonList(remoteTransport1); + Collections.singletonList(remoteTransport1); Map<ContactId, TransportProperties> remoteProperties1 = - Collections.singletonMap(contactId, properties1); + Collections.singletonMap(contactId, properties1); db.setTransports(txn, contactId, remoteTransports1, 2); assertEquals(remoteProperties1, db.getRemoteProperties(txn, transportId)); - // Remove the transport properties but leave the transport + // Remove the transport properties properties1 = new TransportProperties(); - remoteTransport1 = new Transport(transportId, remoteIndex, properties1); + remoteTransport1 = new Transport(transportId, properties1); remoteTransports1 = Collections.singletonList(remoteTransport1); remoteProperties1 = Collections.singletonMap(contactId, properties1); db.setTransports(txn, contactId, remoteTransports1, 3); - assertEquals(remoteProperties1, - db.getRemoteProperties(txn, transportId)); - - // Remove the transport - db.setTransports(txn, contactId, Collections.<Transport>emptyList(), 4); assertEquals(Collections.emptyMap(), db.getRemoteProperties(txn, transportId)); @@ -1062,9 +1013,6 @@ public class H2DatabaseTest extends BriarTestCase { Database<Connection> db = open(false); Connection txn = db.startTransaction(); - // Allocate a transport index - assertEquals(localIndex, db.addTransport(txn, transportId)); - // Set the transport properties db.setLocalProperties(txn, transportId, properties); assertEquals(Collections.singletonList(properties), @@ -1072,8 +1020,7 @@ public class H2DatabaseTest extends BriarTestCase { // Remove the transport properties but leave the transport db.setLocalProperties(txn, transportId, new TransportProperties()); - assertEquals(Collections.singletonList(Collections.emptyMap()), - db.getLocalTransports(txn)); + assertEquals(Collections.emptyList(), db.getLocalTransports(txn)); db.commitTransaction(txn); db.close(); @@ -1082,16 +1029,13 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testUpdateTransportConfig() throws Exception { TransportConfig config = - new TransportConfig(Collections.singletonMap("foo", "bar")); + new TransportConfig(Collections.singletonMap("foo", "bar")); TransportConfig config1 = - new TransportConfig(Collections.singletonMap("baz", "bam")); + new TransportConfig(Collections.singletonMap("baz", "bam")); Database<Connection> db = open(false); Connection txn = db.startTransaction(); - // Allocate a transport index - assertEquals(localIndex, db.addTransport(txn, transportId)); - // Set the transport config db.setConfig(txn, transportId, config); assertEquals(config, db.getConfig(txn, transportId)); @@ -1114,31 +1058,29 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact with a transport - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.setTransports(txn, contactId, remoteTransports, 1); assertEquals(remoteProperties, db.getRemoteProperties(txn, transportId)); // Replace the transport properties using a timestamp of 2 TransportProperties properties1 = - new TransportProperties(Collections.singletonMap("baz", "bam")); - Transport remoteTransport1 = - new Transport(transportId, remoteIndex, properties1); + new TransportProperties(Collections.singletonMap("baz", "bam")); + Transport remoteTransport1 = new Transport(transportId, properties1); Collection<Transport> remoteTransports1 = - Collections.singletonList(remoteTransport1); + Collections.singletonList(remoteTransport1); Map<ContactId, TransportProperties> remoteProperties1 = - Collections.singletonMap(contactId, properties1); + Collections.singletonMap(contactId, properties1); db.setTransports(txn, contactId, remoteTransports1, 2); assertEquals(remoteProperties1, db.getRemoteProperties(txn, transportId)); // Try to replace the transport properties using a timestamp of 1 TransportProperties properties2 = - new TransportProperties(Collections.singletonMap("quux", "etc")); - Transport remoteTransport2 = - new Transport(transportId, remoteIndex, properties2); + new TransportProperties(Collections.singletonMap("quux", "etc")); + Transport remoteTransport2 = new Transport(transportId, properties2); Collection<Transport> remoteTransports2 = - Collections.singletonList(remoteTransport2); + Collections.singletonList(remoteTransport2); db.setTransports(txn, contactId, remoteTransports2, 1); // The old properties should still be there @@ -1151,12 +1093,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testGetMessageIfSendableReturnsNullIfNotInDatabase() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addSubscription(txn, contactId, group, 0L); @@ -1169,12 +1111,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testGetMessageIfSendableReturnsNullIfSeen() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addSubscription(txn, contactId, group, 0L); db.addGroupMessage(txn, message); @@ -1192,12 +1134,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testGetMessageIfSendableReturnsNullIfNotSendable() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addSubscription(txn, contactId, group, 0L); db.addGroupMessage(txn, message); @@ -1220,7 +1162,7 @@ public class H2DatabaseTest extends BriarTestCase { // Add a contact, subscribe to a group and store a message - // the message is older than the contact's subscription - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addSubscription(txn, contactId, group, timestamp + 1); @@ -1243,7 +1185,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addSubscription(txn, contactId, group, 0L); @@ -1263,12 +1205,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSetStatusSeenIfVisibleRequiresMessageInDatabase() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addSubscription(txn, contactId, group, 0L); @@ -1282,12 +1224,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSetStatusSeenIfVisibleRequiresLocalSubscription() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact with a subscription - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, contactId, group, 0L); // There's no local subscription for the group @@ -1299,12 +1241,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSetStatusSeenIfVisibleRequiresContactSubscription() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addGroupMessage(txn, message); db.setStatus(txn, contactId, messageId, Status.NEW); @@ -1318,12 +1260,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSetStatusSeenIfVisibleRequiresVisibility() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addGroupMessage(txn, message); db.addSubscription(txn, contactId, group, 0L); @@ -1338,12 +1280,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSetStatusSeenIfVisibleReturnsTrueIfAlreadySeen() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addSubscription(txn, contactId, group, 0L); @@ -1360,12 +1302,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testSetStatusSeenIfVisibleReturnsTrueIfNew() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addVisibility(txn, contactId, groupId); db.addSubscription(txn, contactId, group, 0L); @@ -1386,7 +1328,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); // The group should not be visible to the contact assertEquals(Collections.emptyList(), db.getVisibility(txn, groupId)); @@ -1402,58 +1344,6 @@ public class H2DatabaseTest extends BriarTestCase { db.close(); } - @Test - public void testGettingUnknownConnectionWindowReturnsDefault() - throws Exception { - Database<Connection> db = open(false); - Connection txn = db.startTransaction(); - - // Add a contact - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); - // Get the connection window for a new index - ConnectionWindow w = db.getConnectionWindow(txn, contactId, - remoteIndex); - // The connection window should exist and be in the initial state - assertNotNull(w); - long top = TransportConstants.CONNECTION_WINDOW_SIZE / 2 - 1; - for(long l = 0; l <= top; l++) assertFalse(w.isSeen(l)); - - db.commitTransaction(txn); - db.close(); - } - - @Test - public void testConnectionWindow() throws Exception { - Database<Connection> db = open(false); - Connection txn = db.startTransaction(); - - // Add a contact - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); - // Get the connection window for a new index - ConnectionWindow w = db.getConnectionWindow(txn, contactId, - remoteIndex); - // The connection window should exist and be in the initial state - assertNotNull(w); - Map<Long, byte[]> unseen = w.getUnseen(); - long top = TransportConstants.CONNECTION_WINDOW_SIZE / 2 - 1; - assertEquals(top + 1, unseen.size()); - for(long l = 0; l <= top; l++) { - assertFalse(w.isSeen(l)); - assertTrue(unseen.containsKey(l)); - } - // Update the connection window and store it - w.setSeen(5); - db.setConnectionWindow(txn, contactId, remoteIndex, w); - // Check that the connection window was stored - w = db.getConnectionWindow(txn, contactId, remoteIndex); - assertNotNull(w); - top += 5; - for(long l = 0; l <= top; l++) assertEquals(l == 5, w.isSeen(l)); - - db.commitTransaction(txn); - db.close(); - } - @Test public void testGetGroupMessageParentWithNoParent() throws Exception { Database<Connection> db = open(false); @@ -1498,7 +1388,7 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testGetGroupMessageParentWithParentInAnotherGroup() - throws Exception { + throws Exception { GroupId groupId1 = new GroupId(TestUtils.getRandomId()); Group group1 = groupFactory.createGroup(groupId1, "Group name", null); Database<Connection> db = open(false); @@ -1527,12 +1417,12 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testGetGroupMessageParentWithPrivateParent() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); // A message with a private parent should return null @@ -1551,7 +1441,7 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testGetGroupMessageParentWithParentInSameGroup() - throws Exception { + throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); @@ -1581,7 +1471,7 @@ public class H2DatabaseTest extends BriarTestCase { Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); // Store a couple of messages @@ -1813,7 +1703,7 @@ public class H2DatabaseTest extends BriarTestCase { // Subscribe to the groups and add a contact for(Group g : groups) db.addSubscription(txn, g); - assertEquals(contactId, db.addContact(txn, inSecret, outSecret, erase)); + assertEquals(contactId, db.addContact(txn)); // Make the groups visible to the contact Collections.shuffle(groups); @@ -1849,7 +1739,6 @@ public class H2DatabaseTest extends BriarTestCase { private Database<Connection> open(boolean resume) throws Exception { Database<Connection> db = new H2Database(testDir, password, MAX_SIZE, - connectionContextFactory, connectionWindowFactory, groupFactory, new SystemClock()); db.open(resume); return db; @@ -1857,7 +1746,6 @@ public class H2DatabaseTest extends BriarTestCase { @After public void tearDown() { - erase.clear(); TestUtils.deleteTestDirectory(testDir); } @@ -1867,4 +1755,17 @@ public class H2DatabaseTest extends BriarTestCase { return passwordString.toCharArray(); } } + + private class TestGroupFactory implements GroupFactory { + + public Group createGroup(String name, byte[] publicKey) + throws IOException { + GroupId id = new GroupId(TestUtils.getRandomId()); + return new TestGroup(id, name, publicKey); + } + + public Group createGroup(GroupId id, String name, byte[] publicKey) { + return new TestGroup(id, name, publicKey); + } + } } diff --git a/test/net/sf/briar/plugins/DuplexTest.java b/test/net/sf/briar/plugins/DuplexTest.java index c247415e46079e5c59f91d570d6d08f0c8a8f1cd..cd87fca94b049c2e427f465bf6b551c1192b940c 100644 --- a/test/net/sf/briar/plugins/DuplexTest.java +++ b/test/net/sf/briar/plugins/DuplexTest.java @@ -16,7 +16,7 @@ abstract class DuplexTest { protected static final String RESPONSE = "Potatoes!"; protected static final long INVITATION_TIMEOUT = 30 * 1000; - protected final ContactId contactId = new ContactId(0); + protected final ContactId contactId = new ContactId(234); protected DuplexPlugin plugin = null; diff --git a/test/net/sf/briar/plugins/InvitationStarterImplTest.java b/test/net/sf/briar/plugins/InvitationStarterImplTest.java index 8dfe5dace6d5dbae98f9798538d0283302c98068..b64c42b5d6d1fa82a22dcc9c791ef537c124c57e 100644 --- a/test/net/sf/briar/plugins/InvitationStarterImplTest.java +++ b/test/net/sf/briar/plugins/InvitationStarterImplTest.java @@ -16,6 +16,8 @@ import com.google.inject.Injector; public class InvitationStarterImplTest extends BriarTestCase { + // FIXME: This is actually a test of CryptoComponent + private final CryptoComponent crypto; public InvitationStarterImplTest() { @@ -32,13 +34,8 @@ public class InvitationStarterImplTest extends BriarTestCase { KeyPair b = crypto.generateAgreementKeyPair(); byte[] bPub = b.getPublic().getEncoded(); PrivateKey bPriv = b.getPrivate(); - byte[][] aSecrets = crypto.deriveInitialSecrets(aPub, bPub, aPriv, 123, - true); - byte[][] bSecrets = crypto.deriveInitialSecrets(bPub, aPub, bPriv, 123, - false); - assertEquals(2, aSecrets.length); - assertEquals(2, bSecrets.length); - assertArrayEquals(aSecrets[0], bSecrets[0]); - assertArrayEquals(aSecrets[1], bSecrets[1]); + byte[] aSecret = crypto.deriveInitialSecret(aPub, bPub, aPriv, true); + byte[] bSecret = crypto.deriveInitialSecret(bPub, aPub, bPriv, false); + assertArrayEquals(aSecret, bSecret); } } diff --git a/test/net/sf/briar/plugins/PluginManagerImplTest.java b/test/net/sf/briar/plugins/PluginManagerImplTest.java index 8c734e1ae391e56d7967f007816f31a87ce071c5..79fcee1b1a161465ebb03c09739d1d701b69dca3 100644 --- a/test/net/sf/briar/plugins/PluginManagerImplTest.java +++ b/test/net/sf/briar/plugins/PluginManagerImplTest.java @@ -3,14 +3,12 @@ package net.sf.briar.plugins; import java.util.Collection; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicInteger; import net.sf.briar.BriarTestCase; import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DatabaseComponent; 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; @@ -29,13 +27,8 @@ public class PluginManagerImplTest extends BriarTestCase { final ConnectionDispatcher dispatcher = context.mock(ConnectionDispatcher.class); final UiCallback uiCallback = context.mock(UiCallback.class); - final AtomicInteger index = new AtomicInteger(0); context.checking(new Expectations() {{ oneOf(poller).start(with(any(Collection.class))); - allowing(db).getLocalIndex(with(any(TransportId.class))); - will(returnValue(null)); - allowing(db).addTransport(with(any(TransportId.class))); - will(returnValue(new TransportIndex(index.getAndIncrement()))); allowing(db).getConfig(with(any(TransportId.class))); will(returnValue(new TransportConfig())); allowing(db).getLocalProperties(with(any(TransportId.class))); diff --git a/test/net/sf/briar/plugins/email/GmailPluginTest.java b/test/net/sf/briar/plugins/email/GmailPluginTest.java index cf5ddc8d5529b01a376388704369cae8b7b3b2f1..8d20fc3dbce1c0132c79e397ffbb990a24e41e4b 100644 --- a/test/net/sf/briar/plugins/email/GmailPluginTest.java +++ b/test/net/sf/briar/plugins/email/GmailPluginTest.java @@ -1,17 +1,14 @@ package net.sf.briar.plugins.email; -import static org.junit.Assert.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import java.io.IOException; -import java.io.OutputStream; -import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; import java.util.concurrent.Executors; -import net.sf.briar.BriarTestCase; import net.sf.briar.api.ContactId; import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportProperties; @@ -19,7 +16,6 @@ import net.sf.briar.api.plugins.simplex.SimplexPluginCallback; import net.sf.briar.api.plugins.simplex.SimplexTransportReader; import net.sf.briar.api.plugins.simplex.SimplexTransportWriter; -import org.jmock.Mockery; import org.junit.Before; import org.junit.Test; @@ -47,7 +43,7 @@ public class GmailPluginTest { props1 = new TransportProperties(); props1.put("email", System.getenv("CONTACT1_EMAIL")); - test1 = new ContactId(12); + test1 = new ContactId(234); map.put(test1, props1); assertEquals(1, map.size()); @@ -120,7 +116,7 @@ public class GmailPluginTest { GmailPlugin pluginTest = new GmailPlugin( Executors.newSingleThreadExecutor(), callback); assertEquals(true, pluginTest.connectSMTP(test1)); - assertEquals(false, pluginTest.connectSMTP(new ContactId(7))); + assertEquals(false, pluginTest.connectSMTP(new ContactId(123))); pluginTest.stop(); } diff --git a/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java b/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java index 8de3042bc620c8545358bc7aa146a6f083ce07b2..58ac2f5cf871039c8b6e6bc74e77b86428cf1ed9 100644 --- a/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java +++ b/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java @@ -28,7 +28,7 @@ import org.junit.Test; public class RemovableDrivePluginTest extends BriarTestCase { private final File testDir = TestUtils.getTestDirectory(); - private final ContactId contactId = new ContactId(0); + private final ContactId contactId = new ContactId(234); @Before public void setUp() { diff --git a/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java b/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java index 3b160df85c3f85c9bc7451d70740bb65518fe8c2..af06bcbeadab203cb75d3c6e6fd0da90faf026be 100644 --- a/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java +++ b/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java @@ -23,7 +23,7 @@ import org.junit.Test; public class SimpleSocketPluginTest extends BriarTestCase { - private final ContactId contactId = new ContactId(0); + private final ContactId contactId = new ContactId(234); @Test public void testIncomingConnection() throws Exception { diff --git a/test/net/sf/briar/plugins/tor/TorPluginTest.java b/test/net/sf/briar/plugins/tor/TorPluginTest.java index cfc2884fe5e255ce8d52061bdfe3f7ab8cfa0e36..76d691fb67ad1a2fd5fba7ab4d402fd78d937af1 100644 --- a/test/net/sf/briar/plugins/tor/TorPluginTest.java +++ b/test/net/sf/briar/plugins/tor/TorPluginTest.java @@ -22,7 +22,7 @@ import org.junit.Test; public class TorPluginTest extends BriarTestCase { - private final ContactId contactId = new ContactId(1); + private final ContactId contactId = new ContactId(234); @Test public void testHiddenService() throws Exception { diff --git a/test/net/sf/briar/protocol/ConstantsTest.java b/test/net/sf/briar/protocol/ConstantsTest.java index 784ecb610c0c5d477725059791fdc96665900bb5..e4c6af3a1a9a52e32c31819b974701b0521addd4 100644 --- a/test/net/sf/briar/protocol/ConstantsTest.java +++ b/test/net/sf/briar/protocol/ConstantsTest.java @@ -35,7 +35,6 @@ import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.RawBatch; 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.UniqueId; import net.sf.briar.crypto.CryptoModule; @@ -158,8 +157,7 @@ public class ConstantsTest extends BriarTestCase { Collection<Transport> transports = new ArrayList<Transport>(); for(int i = 0; i < MAX_TRANSPORTS; i++) { TransportId id = new TransportId(TestUtils.getRandomId()); - TransportIndex index = new TransportIndex(i); - Transport t = new Transport(id, index); + Transport t = new Transport(id); for(int j = 0; j < MAX_PROPERTIES_PER_TRANSPORT; j++) { String key = createRandomString(MAX_PROPERTY_LENGTH); String value = createRandomString(MAX_PROPERTY_LENGTH); diff --git a/test/net/sf/briar/protocol/ProtocolReadWriteTest.java b/test/net/sf/briar/protocol/ProtocolReadWriteTest.java index 54434c6b5c1d24bd2a5a221d579abb6032dec702..51c15ea2055018960e3935a8b2d83df6e063e9c5 100644 --- a/test/net/sf/briar/protocol/ProtocolReadWriteTest.java +++ b/test/net/sf/briar/protocol/ProtocolReadWriteTest.java @@ -28,7 +28,6 @@ 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.crypto.CryptoModule; import net.sf.briar.serial.SerialModule; @@ -71,8 +70,7 @@ public class ProtocolReadWriteTest extends BriarTestCase { bitSet.set(7); subscriptions = Collections.singletonMap(group, 123L); TransportId transportId = new TransportId(TestUtils.getRandomId()); - TransportIndex transportIndex = new TransportIndex(13); - Transport transport = new Transport(transportId, transportIndex, + Transport transport = new Transport(transportId, Collections.singletonMap("bar", "baz")); transports = Collections.singletonList(transport); } diff --git a/test/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java b/test/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java index 8706ad15c53ac9e5bba577f928a142b2a53cc408..09109ee844531caa2bdbfd7f939cb3fa122f5373 100644 --- a/test/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java +++ b/test/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java @@ -1,7 +1,9 @@ package net.sf.briar.protocol.simplex; +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.MIN_CONNECTION_LENGTH; import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import java.io.ByteArrayOutputStream; @@ -12,20 +14,19 @@ import java.util.concurrent.Executors; import net.sf.briar.BriarTestCase; import net.sf.briar.TestUtils; 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.protocol.Ack; import net.sf.briar.api.protocol.BatchId; -import net.sf.briar.api.protocol.ProtocolConstants; import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.RawBatch; import net.sf.briar.api.protocol.TransportId; -import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.transport.ConnectionContext; +import net.sf.briar.api.transport.ConnectionRecogniser; import net.sf.briar.api.transport.ConnectionRegistry; import net.sf.briar.api.transport.ConnectionWriterFactory; -import net.sf.briar.api.transport.TransportConstants; import net.sf.briar.crypto.CryptoModule; import net.sf.briar.protocol.ProtocolModule; import net.sf.briar.protocol.duplex.DuplexProtocolModule; @@ -43,24 +44,31 @@ import com.google.inject.Module; public class OutgoingSimplexConnectionTest extends BriarTestCase { + // FIXME: This is an integration test, not a unit test + private final Mockery context; private final DatabaseComponent db; + private final KeyManager keyManager; + private final ConnectionRecogniser connRecogniser; private final ConnectionRegistry connRegistry; private final ConnectionWriterFactory connFactory; private final ProtocolWriterFactory protoFactory; private final ContactId contactId; private final TransportId transportId; - private final TransportIndex transportIndex; private final byte[] secret; public OutgoingSimplexConnectionTest() { super(); context = new Mockery(); db = context.mock(DatabaseComponent.class); + keyManager = context.mock(KeyManager.class); + connRecogniser = context.mock(ConnectionRecogniser.class); Module testModule = new AbstractModule() { @Override public void configure() { bind(DatabaseComponent.class).toInstance(db); + bind(KeyManager.class).toInstance(keyManager); + bind(ConnectionRecogniser.class).toInstance(connRecogniser); bind(Executor.class).annotatedWith( DatabaseExecutor.class).toInstance( Executors.newCachedThreadPool()); @@ -73,9 +81,8 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase { connRegistry = i.getInstance(ConnectionRegistry.class); connFactory = i.getInstance(ConnectionWriterFactory.class); protoFactory = i.getInstance(ProtocolWriterFactory.class); - contactId = new ContactId(1); + contactId = new ContactId(234); transportId = new TransportId(TestUtils.getRandomId()); - transportIndex = new TransportIndex(13); secret = new byte[32]; } @@ -83,40 +90,31 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase { public void testConnectionTooShort() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); TestSimplexTransportWriter transport = new TestSimplexTransportWriter( - out, ProtocolConstants.MAX_PACKET_LENGTH, true); + out, MAX_PACKET_LENGTH, true); + byte[] tag = new byte[TAG_LENGTH]; + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + tag, secret, 0L, true); OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db, - connRegistry, connFactory, protoFactory, contactId, transportId, - transportIndex, transport); - final ConnectionContext ctx = context.mock(ConnectionContext.class); - context.checking(new Expectations() {{ - oneOf(db).getConnectionContext(contactId, transportIndex); - will(returnValue(ctx)); - oneOf(ctx).getSecret(); - will(returnValue(secret)); - }}); + connRegistry, connFactory, protoFactory, ctx, transport); connection.write(); // Nothing should have been written assertEquals(0, out.size()); // The transport should have been disposed with exception == true assertTrue(transport.getDisposed()); assertTrue(transport.getException()); - context.assertIsSatisfied(); } @Test public void testNothingToSend() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); TestSimplexTransportWriter transport = new TestSimplexTransportWriter( - out, TransportConstants.MIN_CONNECTION_LENGTH, true); + out, MIN_CONNECTION_LENGTH, true); + byte[] tag = new byte[TAG_LENGTH]; + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + tag, secret, 0L, true); OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db, - connRegistry, connFactory, protoFactory, contactId, transportId, - transportIndex, transport); - final ConnectionContext ctx = context.mock(ConnectionContext.class); + connRegistry, connFactory, protoFactory, ctx, transport); context.checking(new Expectations() {{ - oneOf(db).getConnectionContext(contactId, transportIndex); - will(returnValue(ctx)); - oneOf(ctx).getSecret(); - will(returnValue(secret)); // No transports to send oneOf(db).generateTransportUpdate(contactId); will(returnValue(null)); @@ -143,20 +141,17 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase { public void testSomethingToSend() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); TestSimplexTransportWriter transport = new TestSimplexTransportWriter( - out, TransportConstants.MIN_CONNECTION_LENGTH, true); + out, MIN_CONNECTION_LENGTH, true); + byte[] tag = new byte[TAG_LENGTH]; + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + tag, secret, 0L, true); OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db, - connRegistry, connFactory, protoFactory, contactId, transportId, - transportIndex, transport); - final ConnectionContext ctx = context.mock(ConnectionContext.class); + connRegistry, connFactory, protoFactory, ctx, transport); final Ack ack = context.mock(Ack.class); final BatchId batchId = new BatchId(TestUtils.getRandomId()); final RawBatch batch = context.mock(RawBatch.class); final byte[] message = new byte[1234]; context.checking(new Expectations() {{ - oneOf(db).getConnectionContext(contactId, transportIndex); - will(returnValue(ctx)); - oneOf(ctx).getSecret(); - will(returnValue(secret)); // No transports to send oneOf(db).generateTransportUpdate(contactId); will(returnValue(null)); diff --git a/test/net/sf/briar/protocol/simplex/SimplexConnectionReadWriteTest.java b/test/net/sf/briar/protocol/simplex/SimplexConnectionReadWriteTest.java index 036392a974491c7e1d48735a15f05e94948d38b4..7628ec6322931d2d516ea8cd7343bd5e8ff52112 100644 --- a/test/net/sf/briar/protocol/simplex/SimplexConnectionReadWriteTest.java +++ b/test/net/sf/briar/protocol/simplex/SimplexConnectionReadWriteTest.java @@ -23,7 +23,6 @@ import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory; 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.ConnectionReaderFactory; @@ -49,11 +48,12 @@ import com.google.inject.Injector; public class SimplexConnectionReadWriteTest extends BriarTestCase { + // FIXME: This is an integration test, not a unit test + private final File testDir = TestUtils.getTestDirectory(); private final File aliceDir = new File(testDir, "alice"); private final File bobDir = new File(testDir, "bob"); private final TransportId transportId; - private final TransportIndex transportIndex; private final byte[] aliceToBobSecret, bobToAliceSecret; private Injector alice, bob; @@ -61,7 +61,6 @@ public class SimplexConnectionReadWriteTest extends BriarTestCase { public SimplexConnectionReadWriteTest() throws Exception { super(); transportId = new TransportId(TestUtils.getRandomId()); - transportIndex = new TransportIndex(1); // Create matching secrets for Alice and Bob Random r = new Random(); aliceToBobSecret = new byte[32]; @@ -102,7 +101,7 @@ public class SimplexConnectionReadWriteTest extends BriarTestCase { DatabaseComponent db = alice.getInstance(DatabaseComponent.class); db.open(false); // Add Bob as a contact and send him a message - ContactId contactId = db.addContact(bobToAliceSecret, aliceToBobSecret); + ContactId contactId = db.addContact(); String subject = "Hello"; byte[] body = "Hi Bob!".getBytes("UTF-8"); MessageFactory messageFactory = alice.getInstance(MessageFactory.class); @@ -111,16 +110,19 @@ public class SimplexConnectionReadWriteTest extends BriarTestCase { // Create an outgoing batch connection ByteArrayOutputStream out = new ByteArrayOutputStream(); ConnectionRegistry connRegistry = - alice.getInstance(ConnectionRegistry.class); + alice.getInstance(ConnectionRegistry.class); ConnectionWriterFactory connFactory = - alice.getInstance(ConnectionWriterFactory.class); + alice.getInstance(ConnectionWriterFactory.class); ProtocolWriterFactory protoFactory = - alice.getInstance(ProtocolWriterFactory.class); + alice.getInstance(ProtocolWriterFactory.class); TestSimplexTransportWriter transport = new TestSimplexTransportWriter( out, Long.MAX_VALUE, false); + // FIXME: Encode the tag + byte[] tag = new byte[TAG_LENGTH]; + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + tag, aliceToBobSecret, 0L, true); OutgoingSimplexConnection simplex = new OutgoingSimplexConnection(db, - connRegistry, connFactory, protoFactory, contactId, transportId, - transportIndex, transport); + connRegistry, connFactory, protoFactory, ctx, transport); // Write whatever needs to be written simplex.write(); assertTrue(transport.getDisposed()); @@ -139,14 +141,12 @@ public class SimplexConnectionReadWriteTest extends BriarTestCase { MessageListener listener = new MessageListener(); db.addListener(listener); // Add Alice as a contact - ContactId contactId = db.addContact(aliceToBobSecret, bobToAliceSecret); - // Add the transport - assertEquals(transportIndex, db.addTransport(transportId)); + ContactId contactId = db.addContact(); // Fake a transport update from Alice TransportUpdate transportUpdate = new TransportUpdate() { public Collection<Transport> getTransports() { - Transport t = new Transport(transportId, transportIndex); + Transport t = new Transport(transportId); return Collections.singletonList(t); } @@ -164,19 +164,17 @@ public class SimplexConnectionReadWriteTest extends BriarTestCase { ConnectionContext ctx = rec.acceptConnection(transportId, tag); assertNotNull(ctx); assertEquals(contactId, ctx.getContactId()); - assertEquals(transportIndex, ctx.getTransportIndex()); // Create an incoming batch connection ConnectionRegistry connRegistry = - bob.getInstance(ConnectionRegistry.class); + bob.getInstance(ConnectionRegistry.class); ConnectionReaderFactory connFactory = - bob.getInstance(ConnectionReaderFactory.class); + bob.getInstance(ConnectionReaderFactory.class); ProtocolReaderFactory protoFactory = - bob.getInstance(ProtocolReaderFactory.class); + bob.getInstance(ProtocolReaderFactory.class); TestSimplexTransportReader transport = new TestSimplexTransportReader(in); IncomingSimplexConnection simplex = new IncomingSimplexConnection( new ImmediateExecutor(), new ImmediateExecutor(), db, - connRegistry, connFactory, protoFactory, ctx, transportId, - transport); + connRegistry, connFactory, protoFactory, ctx, transport); // No messages should have been added yet assertFalse(listener.messagesAdded); // Read whatever needs to be read diff --git a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java deleted file mode 100644 index e4cd00d3cab2249b2ef3aec8c25e094c352969c6..0000000000000000000000000000000000000000 --- a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java +++ /dev/null @@ -1,624 +0,0 @@ -package net.sf.briar.transport; - -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.Executor; - -import javax.crypto.Cipher; - -import net.sf.briar.BriarTestCase; -import net.sf.briar.TestUtils; -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.event.ContactRemovedEvent; -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.ConnectionWindow; -import net.sf.briar.crypto.CryptoModule; -import net.sf.briar.plugins.ImmediateExecutor; - -import org.jmock.Expectations; -import org.jmock.Mockery; -import org.junit.Test; - -import com.google.inject.Guice; -import com.google.inject.Injector; - -public class ConnectionRecogniserImplTest extends BriarTestCase { - - private final CryptoComponent crypto; - private final ContactId contactId; - private final byte[] inSecret; - private final TransportId transportId; - private final TransportIndex localIndex, remoteIndex; - private final Collection<Transport> localTransports, remoteTransports; - - public ConnectionRecogniserImplTest() { - super(); - Injector i = Guice.createInjector(new CryptoModule()); - crypto = i.getInstance(CryptoComponent.class); - contactId = new ContactId(1); - inSecret = new byte[32]; - new Random().nextBytes(inSecret); - transportId = new TransportId(TestUtils.getRandomId()); - localIndex = new TransportIndex(13); - remoteIndex = new TransportIndex(7); - Map<String, String> properties = Collections.singletonMap("foo", "bar"); - Transport localTransport = new Transport(transportId, localIndex, - properties); - localTransports = Collections.singletonList(localTransport); - Transport remoteTransport = new Transport(transportId, remoteIndex, - properties); - remoteTransports = Collections.singletonList(remoteTransport); - } - - @Test - public void testUnexpectedIv() throws Exception { - final ConnectionWindow window = createConnectionWindow(remoteIndex); - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports)); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(remoteIndex)); - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - assertNull(c.acceptConnection(transportId, new byte[TAG_LENGTH])); - context.assertIsSatisfied(); - } - - @Test - public void testExpectedIv() throws Exception { - final ConnectionWindow window = createConnectionWindow(remoteIndex); - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports)); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(remoteIndex)); - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - // Update the window - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - oneOf(db).setConnectionWindow(contactId, remoteIndex, window); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // The tag should not be expected by the wrong transport - TransportId wrong = new TransportId(TestUtils.getRandomId()); - assertNull(c.acceptConnection(wrong, tag)); - // The tag should be expected by the right transport - ConnectionContext ctx = c.acceptConnection(transportId, tag); - assertNotNull(ctx); - assertEquals(contactId, ctx.getContactId()); - assertEquals(remoteIndex, ctx.getTransportIndex()); - assertEquals(3, ctx.getConnectionNumber()); - // The tag should no longer be expected - assertNull(c.acceptConnection(transportId, tag)); - // The window should have advanced - Map<Long, byte[]> unseen = window.getUnseen(); - assertEquals(19, unseen.size()); - for(int i = 0; i < 19; i++) { - assertEquals(i != 3, unseen.containsKey(Long.valueOf(i))); - } - context.assertIsSatisfied(); - } - - @Test - public void testContactRemovedAfterInit() throws Exception { - final ConnectionWindow window = createConnectionWindow(remoteIndex); - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise before removing contact - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports)); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(remoteIndex)); - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // Ensure the recogniser is initialised - assertFalse(c.isInitialised()); - assertNull(c.acceptConnection(transportId, new byte[TAG_LENGTH])); - assertTrue(c.isInitialised()); - // Remove the contact - c.eventOccurred(new ContactRemovedEvent(contactId)); - // The tag should not be expected - assertNull(c.acceptConnection(transportId, tag)); - context.assertIsSatisfied(); - } - - @Test - public void testContactRemovedBeforeInit() throws Exception { - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise after removing contact - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports)); - oneOf(db).getContacts(); - will(returnValue(Collections.emptyList())); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // Remove the contact - c.eventOccurred(new ContactRemovedEvent(contactId)); - // The tag should not be expected - assertFalse(c.isInitialised()); - assertNull(c.acceptConnection(transportId, tag)); - assertTrue(c.isInitialised()); - context.assertIsSatisfied(); - } - - @Test - public void testLocalTransportAddedAfterInit() throws Exception { - final ConnectionWindow window = createConnectionWindow(remoteIndex); - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise before adding transport - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(Collections.emptyList())); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - // Add the transport - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(remoteIndex)); - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - // Update the window - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - oneOf(db).setConnectionWindow(contactId, remoteIndex, window); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // The tag should not be expected - assertFalse(c.isInitialised()); - assertNull(c.acceptConnection(transportId, tag)); - assertTrue(c.isInitialised()); - // Add the transport - c.eventOccurred(new TransportAddedEvent(transportId)); - // The tag should be expected - ConnectionContext ctx = c.acceptConnection(transportId, tag); - assertNotNull(ctx); - assertEquals(contactId, ctx.getContactId()); - assertEquals(remoteIndex, ctx.getTransportIndex()); - assertEquals(3, ctx.getConnectionNumber()); - // The tag should no longer be expected - assertNull(c.acceptConnection(transportId, tag)); - // The window should have advanced - Map<Long, byte[]> unseen = window.getUnseen(); - assertEquals(19, unseen.size()); - for(int i = 0; i < 19; i++) { - assertEquals(i != 3, unseen.containsKey(Long.valueOf(i))); - } - context.assertIsSatisfied(); - } - - @Test - public void testLocalTransportAddedBeforeInit() throws Exception { - final ConnectionWindow window = createConnectionWindow(remoteIndex); - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise after adding transport - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports)); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(remoteIndex)); - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - // Update the window - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - oneOf(db).setConnectionWindow(contactId, remoteIndex, window); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // Add the transport - c.eventOccurred(new TransportAddedEvent(transportId)); - // The tag should be expected - assertFalse(c.isInitialised()); - ConnectionContext ctx = c.acceptConnection(transportId, tag); - assertTrue(c.isInitialised()); - assertNotNull(ctx); - assertEquals(contactId, ctx.getContactId()); - assertEquals(remoteIndex, ctx.getTransportIndex()); - assertEquals(3, ctx.getConnectionNumber()); - // The tag should no longer be expected - assertNull(c.acceptConnection(transportId, tag)); - // The window should have advanced - Map<Long, byte[]> unseen = window.getUnseen(); - assertEquals(19, unseen.size()); - for(int i = 0; i < 19; i++) { - assertEquals(i != 3, unseen.containsKey(Long.valueOf(i))); - } - context.assertIsSatisfied(); - } - - @Test - public void testRemoteTransportAddedAfterInit() throws Exception { - final ConnectionWindow window = createConnectionWindow(remoteIndex); - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise before updating the contact - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports)); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(null)); - // Update the contact - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - // Update the window - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - oneOf(db).setConnectionWindow(contactId, remoteIndex, window); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // The tag should not be expected - assertFalse(c.isInitialised()); - assertNull(c.acceptConnection(transportId, tag)); - assertTrue(c.isInitialised()); - // Update the contact - c.eventOccurred(new RemoteTransportsUpdatedEvent(contactId, - remoteTransports)); - // The tag should be expected - ConnectionContext ctx = c.acceptConnection(transportId, tag); - assertNotNull(ctx); - assertEquals(contactId, ctx.getContactId()); - assertEquals(remoteIndex, ctx.getTransportIndex()); - assertEquals(3, ctx.getConnectionNumber()); - // The tag should no longer be expected - assertNull(c.acceptConnection(transportId, tag)); - // The window should have advanced - Map<Long, byte[]> unseen = window.getUnseen(); - assertEquals(19, unseen.size()); - for(int i = 0; i < 19; i++) { - assertEquals(i != 3, unseen.containsKey(Long.valueOf(i))); - } - context.assertIsSatisfied(); - } - - @Test - public void testRemoteTransportAddedBeforeInit() throws Exception { - final ConnectionWindow window = createConnectionWindow(remoteIndex); - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise after updating the contact - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports)); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(remoteIndex)); - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - // Update the window - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - oneOf(db).setConnectionWindow(contactId, remoteIndex, window); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // Update the contact - c.eventOccurred(new RemoteTransportsUpdatedEvent(contactId, - remoteTransports)); - // The tag should be expected - assertFalse(c.isInitialised()); - ConnectionContext ctx = c.acceptConnection(transportId, tag); - assertTrue(c.isInitialised()); - assertNotNull(ctx); - assertEquals(contactId, ctx.getContactId()); - assertEquals(remoteIndex, ctx.getTransportIndex()); - assertEquals(3, ctx.getConnectionNumber()); - // The tag should no longer be expected - assertNull(c.acceptConnection(transportId, tag)); - // The window should have advanced - Map<Long, byte[]> unseen = window.getUnseen(); - assertEquals(19, unseen.size()); - for(int i = 0; i < 19; i++) { - assertEquals(i != 3, unseen.containsKey(Long.valueOf(i))); - } - context.assertIsSatisfied(); - } - - @Test - public void testRemoteTransportRemovedAfterInit() throws Exception { - final ConnectionWindow window = createConnectionWindow(remoteIndex); - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise before updating the contact - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports)); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(remoteIndex)); - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // Ensure the recogniser is initialised - assertFalse(c.isInitialised()); - assertNull(c.acceptConnection(transportId, new byte[TAG_LENGTH])); - assertTrue(c.isInitialised()); - // Update the contact - c.eventOccurred(new RemoteTransportsUpdatedEvent(contactId, - Collections.<Transport>emptyList())); - // The tag should not be expected - assertNull(c.acceptConnection(transportId, tag)); - context.assertIsSatisfied(); - } - - @Test - public void testRemoteTransportRemovedBeforeInit() throws Exception { - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise after updating the contact - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports)); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(null)); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // Update the contact - c.eventOccurred(new RemoteTransportsUpdatedEvent(contactId, - Collections.<Transport>emptyList())); - // The tag should not be expected - assertFalse(c.isInitialised()); - assertNull(c.acceptConnection(transportId, tag)); - assertTrue(c.isInitialised()); - context.assertIsSatisfied(); - } - - @Test - public void testRemoteTransportIndexChangedAfterInit() throws Exception { - // The contact changes the transport ID <-> index relationships - final TransportId transportId1 = - new TransportId(TestUtils.getRandomId()); - final TransportIndex remoteIndex1 = new TransportIndex(11); - Map<String, String> properties = Collections.singletonMap("foo", "bar"); - Transport remoteTransport = new Transport(transportId, remoteIndex1, - properties); - Transport remoteTransport1 = new Transport(transportId1, remoteIndex, - properties); - Collection<Transport> remoteTransports1 = Arrays.asList( - new Transport[] {remoteTransport, remoteTransport1}); - // Use two local transports for this test - TransportIndex localIndex1 = new TransportIndex(17); - Transport localTransport = new Transport(transportId, localIndex, - properties); - Transport localTransport1 = new Transport(transportId1, localIndex1, - properties); - final Collection<Transport> localTransports1 = Arrays.asList( - new Transport[] {localTransport, localTransport1}); - - final ConnectionWindow window = createConnectionWindow(remoteIndex); - final ConnectionWindow window1 = createConnectionWindow(remoteIndex1); - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise before updating the contact - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports1)); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - // First, transportId <-> remoteIndex, transportId1 <-> remoteIndex - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(remoteIndex)); - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - oneOf(db).getRemoteIndex(contactId, transportId1); - will(returnValue(remoteIndex1)); - oneOf(db).getConnectionWindow(contactId, remoteIndex1); - will(returnValue(window1)); - // Later, transportId <-> remoteIndex1, transportId1 <-> remoteIndex - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - oneOf(db).getConnectionWindow(contactId, remoteIndex1); - will(returnValue(window1)); - // Update the window - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - oneOf(db).setConnectionWindow(contactId, remoteIndex, window); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // Ensure the recogniser is initialised - assertFalse(c.isInitialised()); - assertNull(c.acceptConnection(transportId, new byte[TAG_LENGTH])); - assertTrue(c.isInitialised()); - // Update the contact - c.eventOccurred(new RemoteTransportsUpdatedEvent(contactId, - remoteTransports1)); - // The tag should not be expected by the old transport - assertNull(c.acceptConnection(transportId, tag)); - // The tag should be expected by the new transport - ConnectionContext ctx = c.acceptConnection(transportId1, tag); - assertNotNull(ctx); - assertEquals(contactId, ctx.getContactId()); - assertEquals(remoteIndex, ctx.getTransportIndex()); - assertEquals(3, ctx.getConnectionNumber()); - // The tag should no longer be expected - assertNull(c.acceptConnection(transportId1, tag)); - // The window should have advanced - Map<Long, byte[]> unseen = window.getUnseen(); - assertEquals(19, unseen.size()); - for(int i = 0; i < 19; i++) { - assertEquals(i != 3, unseen.containsKey(Long.valueOf(i))); - } - context.assertIsSatisfied(); - } - - @Test - public void testRemoteTransportIndexChangedBeforeInit() throws Exception { - // The contact changes the transport ID <-> index relationships - final TransportId transportId1 = - new TransportId(TestUtils.getRandomId()); - final TransportIndex remoteIndex1 = new TransportIndex(11); - Map<String, String> properties = Collections.singletonMap("foo", "bar"); - Transport remoteTransport = new Transport(transportId, remoteIndex1, - properties); - Transport remoteTransport1 = new Transport(transportId1, remoteIndex, - properties); - Collection<Transport> remoteTransports1 = Arrays.asList( - new Transport[] {remoteTransport, remoteTransport1}); - // Use two local transports for this test - TransportIndex localIndex1 = new TransportIndex(17); - Transport localTransport = new Transport(transportId, localIndex, - properties); - Transport localTransport1 = new Transport(transportId1, localIndex1, - properties); - final Collection<Transport> localTransports1 = Arrays.asList( - new Transport[] {localTransport, localTransport1}); - - final ConnectionWindow window = createConnectionWindow(remoteIndex); - final ConnectionWindow window1 = createConnectionWindow(remoteIndex1); - Mockery context = new Mockery(); - final DatabaseComponent db = context.mock(DatabaseComponent.class); - context.checking(new Expectations() {{ - // Initialise after updating the contact - oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); - oneOf(db).getLocalTransports(); - will(returnValue(localTransports1)); - oneOf(db).getContacts(); - will(returnValue(Collections.singletonList(contactId))); - // First, transportId <-> remoteIndex1, transportId1 <-> remoteIndex - oneOf(db).getRemoteIndex(contactId, transportId); - will(returnValue(remoteIndex1)); - oneOf(db).getConnectionWindow(contactId, remoteIndex1); - will(returnValue(window1)); - oneOf(db).getRemoteIndex(contactId, transportId1); - will(returnValue(remoteIndex)); - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - // Update the window - oneOf(db).getConnectionWindow(contactId, remoteIndex); - will(returnValue(window)); - oneOf(db).setConnectionWindow(contactId, remoteIndex, window); - }}); - Executor executor = new ImmediateExecutor(); - ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(executor, db, - crypto); - byte[] tag = calculateTag(); - // Update the contact - c.eventOccurred(new RemoteTransportsUpdatedEvent(contactId, - remoteTransports1)); - // The tag should not be expected by the old transport - assertFalse(c.isInitialised()); - assertNull(c.acceptConnection(transportId, tag)); - assertTrue(c.isInitialised()); - // The tag should be expected by the new transport - ConnectionContext ctx = c.acceptConnection(transportId1, tag); - assertNotNull(ctx); - assertEquals(contactId, ctx.getContactId()); - assertEquals(remoteIndex, ctx.getTransportIndex()); - assertEquals(3, ctx.getConnectionNumber()); - // The tag should no longer be expected - assertNull(c.acceptConnection(transportId1, tag)); - // The window should have advanced - Map<Long, byte[]> unseen = window.getUnseen(); - assertEquals(19, unseen.size()); - for(int i = 0; i < 19; i++) { - assertEquals(i != 3, unseen.containsKey(Long.valueOf(i))); - } - context.assertIsSatisfied(); - } - - private ConnectionWindow createConnectionWindow(TransportIndex index) { - return new ConnectionWindowImpl(crypto, index, inSecret) { - @Override - public void erase() {} - }; - } - - private byte[] calculateTag() throws Exception { - // Calculate the shared secret for connection number 3 - byte[] secret = inSecret; - for(int i = 0; i < 4; i++) { - secret = crypto.deriveNextSecret(secret, remoteIndex.getInt(), i); - } - // Calculate the expected tag for connection number 3 - ErasableKey tagKey = crypto.deriveTagKey(secret, true); - Cipher tagCipher = crypto.getTagCipher(); - byte[] tag = new byte[TAG_LENGTH]; - TagEncoder.encodeTag(tag, tagCipher, tagKey); - return tag; - } -} diff --git a/test/net/sf/briar/transport/ConnectionWindowImplTest.java b/test/net/sf/briar/transport/ConnectionWindowImplTest.java index 61e253ca912143e5dd9004ef15208d8d649373c1..359945e468403a47eb99ce7a01199c3709ff42b1 100644 --- a/test/net/sf/briar/transport/ConnectionWindowImplTest.java +++ b/test/net/sf/briar/transport/ConnectionWindowImplTest.java @@ -1,39 +1,19 @@ package net.sf.briar.transport; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; +import java.util.HashSet; +import java.util.Set; import net.sf.briar.BriarTestCase; -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.crypto.CryptoModule; import net.sf.briar.util.ByteUtils; import org.junit.Test; -import com.google.inject.Guice; -import com.google.inject.Injector; - public class ConnectionWindowImplTest extends BriarTestCase { - private final CryptoComponent crypto; - private final byte[] secret; - private final TransportIndex transportIndex = new TransportIndex(13); - - public ConnectionWindowImplTest() { - super(); - Injector i = Guice.createInjector(new CryptoModule()); - crypto = i.getInstance(CryptoComponent.class); - secret = new byte[32]; - new Random().nextBytes(secret); - } - @Test public void testWindowSliding() { - ConnectionWindow w = new ConnectionWindowImpl(crypto, - transportIndex, secret); + ConnectionWindow w = new ConnectionWindowImpl(); for(int i = 0; i < 100; i++) { assertFalse(w.isSeen(i)); w.setSeen(i); @@ -43,8 +23,7 @@ public class ConnectionWindowImplTest extends BriarTestCase { @Test public void testWindowJumping() { - ConnectionWindow w = new ConnectionWindowImpl(crypto, transportIndex, - secret); + ConnectionWindow w = new ConnectionWindowImpl(); for(int i = 0; i < 100; i += 13) { assertFalse(w.isSeen(i)); w.setSeen(i); @@ -54,8 +33,7 @@ public class ConnectionWindowImplTest extends BriarTestCase { @Test public void testWindowUpperLimit() { - ConnectionWindow w = new ConnectionWindowImpl(crypto, transportIndex, - secret); + ConnectionWindow w = new ConnectionWindowImpl(); // Centre is 0, highest value in window is 15 w.setSeen(15); // Centre is 16, highest value in window is 31 @@ -66,11 +44,11 @@ public class ConnectionWindowImplTest extends BriarTestCase { fail(); } catch(IllegalArgumentException expected) {} // Values greater than 2^32 - 1 should never be allowed - Map<Long, byte[]> unseen = new HashMap<Long, byte[]>(); + Set<Long> unseen = new HashSet<Long>(); for(int i = 0; i < 32; i++) { - unseen.put(ByteUtils.MAX_32_BIT_UNSIGNED - i, secret); + unseen.add(ByteUtils.MAX_32_BIT_UNSIGNED - i); } - w = new ConnectionWindowImpl(crypto, transportIndex, unseen); + w = new ConnectionWindowImpl(unseen); w.setSeen(ByteUtils.MAX_32_BIT_UNSIGNED); try { w.setSeen(ByteUtils.MAX_32_BIT_UNSIGNED + 1); @@ -80,8 +58,7 @@ public class ConnectionWindowImplTest extends BriarTestCase { @Test public void testWindowLowerLimit() { - ConnectionWindow w = new ConnectionWindowImpl(crypto, transportIndex, - secret); + ConnectionWindow w = new ConnectionWindowImpl(); // Centre is 0, negative values should never be allowed try { w.setSeen(-1); @@ -111,8 +88,7 @@ public class ConnectionWindowImplTest extends BriarTestCase { @Test public void testCannotSetSeenTwice() { - ConnectionWindow w = new ConnectionWindowImpl(crypto, transportIndex, - secret); + ConnectionWindow w = new ConnectionWindowImpl(); w.setSeen(15); try { w.setSeen(15); @@ -122,13 +98,12 @@ public class ConnectionWindowImplTest extends BriarTestCase { @Test public void testGetUnseenConnectionNumbers() { - ConnectionWindow w = new ConnectionWindowImpl(crypto, transportIndex, - secret); + ConnectionWindow w = new ConnectionWindowImpl(); // Centre is 0; window should cover 0 to 15, inclusive, with none seen - Map<Long, byte[]> unseen = w.getUnseen(); + Set<Long> unseen = w.getUnseen(); assertEquals(16, unseen.size()); for(int i = 0; i < 16; i++) { - assertTrue(unseen.containsKey(Long.valueOf(i))); + assertTrue(unseen.contains(Long.valueOf(i))); assertFalse(w.isSeen(i)); } w.setSeen(3); @@ -138,10 +113,10 @@ public class ConnectionWindowImplTest extends BriarTestCase { assertEquals(19, unseen.size()); for(int i = 0; i < 21; i++) { if(i == 3 || i == 4) { - assertFalse(unseen.containsKey(Long.valueOf(i))); + assertFalse(unseen.contains(Long.valueOf(i))); assertTrue(w.isSeen(i)); } else { - assertTrue(unseen.containsKey(Long.valueOf(i))); + assertTrue(unseen.contains(Long.valueOf(i))); assertFalse(w.isSeen(i)); } } @@ -151,10 +126,10 @@ public class ConnectionWindowImplTest extends BriarTestCase { assertEquals(30, unseen.size()); for(int i = 4; i < 36; i++) { if(i == 4 || i == 19) { - assertFalse(unseen.containsKey(Long.valueOf(i))); + assertFalse(unseen.contains(Long.valueOf(i))); assertTrue(w.isSeen(i)); } else { - assertTrue(unseen.containsKey(Long.valueOf(i))); + assertTrue(unseen.contains(Long.valueOf(i))); assertFalse(w.isSeen(i)); } } diff --git a/test/net/sf/briar/transport/ConnectionWriterTest.java b/test/net/sf/briar/transport/ConnectionWriterTest.java index 615944366c870e6a8975975bd0a5051e34785eb6..cbfc46b44a2dcc026c2b2b1f8bd7c4e8abd6d875 100644 --- a/test/net/sf/briar/transport/ConnectionWriterTest.java +++ b/test/net/sf/briar/transport/ConnectionWriterTest.java @@ -2,12 +2,17 @@ package net.sf.briar.transport; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MIN_CONNECTION_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import java.io.ByteArrayOutputStream; import java.util.Random; import net.sf.briar.BriarTestCase; import net.sf.briar.TestDatabaseModule; +import net.sf.briar.TestUtils; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.protocol.TransportId; +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.clock.ClockModule; @@ -27,6 +32,8 @@ import com.google.inject.Injector; public class ConnectionWriterTest extends BriarTestCase { private final ConnectionWriterFactory connectionWriterFactory; + private final ContactId contactId; + private final TransportId transportId; private final byte[] secret; public ConnectionWriterTest() throws Exception { @@ -37,6 +44,8 @@ public class ConnectionWriterTest extends BriarTestCase { new TestDatabaseModule(), new SimplexProtocolModule(), new TransportModule(), new DuplexProtocolModule()); connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class); + contactId = new ContactId(234); + transportId = new TransportId(TestUtils.getRandomId()); secret = new byte[32]; new Random().nextBytes(secret); } @@ -44,9 +53,12 @@ public class ConnectionWriterTest extends BriarTestCase { @Test public void testOverheadWithTag() throws Exception { ByteArrayOutputStream out = - new ByteArrayOutputStream(MIN_CONNECTION_LENGTH); + new ByteArrayOutputStream(MIN_CONNECTION_LENGTH); + byte[] tag = new byte[TAG_LENGTH]; + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + tag, secret, 0L, true); ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out, - MIN_CONNECTION_LENGTH, secret, true); + MIN_CONNECTION_LENGTH, ctx, true); // Check that the connection writer thinks there's room for a packet long capacity = w.getRemainingCapacity(); assertTrue(capacity > MAX_PACKET_LENGTH); @@ -63,9 +75,11 @@ public class ConnectionWriterTest extends BriarTestCase { @Test public void testOverheadWithoutTag() throws Exception { ByteArrayOutputStream out = - new ByteArrayOutputStream(MIN_CONNECTION_LENGTH); + new ByteArrayOutputStream(MIN_CONNECTION_LENGTH); + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + null, secret, 0L, true); ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out, - MIN_CONNECTION_LENGTH, secret, false); + MIN_CONNECTION_LENGTH, ctx, false); // Check that the connection writer thinks there's room for a packet long capacity = w.getRemainingCapacity(); assertTrue(capacity > MAX_PACKET_LENGTH); diff --git a/test/net/sf/briar/transport/FrameReadWriteTest.java b/test/net/sf/briar/transport/FrameReadWriteTest.java index a9b7361e75604551ac9257e2cc65de8c7c94bf26..6443b973f1bbbd8946198bd6a4675062f2983553 100644 --- a/test/net/sf/briar/transport/FrameReadWriteTest.java +++ b/test/net/sf/briar/transport/FrameReadWriteTest.java @@ -1,6 +1,5 @@ package net.sf.briar.transport; -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import static org.junit.Assert.assertArrayEquals; import java.io.ByteArrayInputStream; @@ -9,8 +8,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Random; -import javax.crypto.Cipher; - import net.sf.briar.BriarTestCase; import net.sf.briar.api.crypto.AuthenticatedCipher; import net.sf.briar.api.crypto.CryptoComponent; @@ -29,24 +26,21 @@ public class FrameReadWriteTest extends BriarTestCase { private final int FRAME_LENGTH = 2048; private final CryptoComponent crypto; - private final Cipher tagCipher; private final AuthenticatedCipher frameCipher; private final Random random; private final byte[] outSecret; - private final ErasableKey tagKey, frameKey; + private final ErasableKey frameKey; public FrameReadWriteTest() { super(); Injector i = Guice.createInjector(new CryptoModule()); crypto = i.getInstance(CryptoComponent.class); - tagCipher = crypto.getTagCipher(); frameCipher = crypto.getFrameCipher(); random = new Random(); // Since we're sending frames to ourselves, we only need outgoing keys outSecret = new byte[32]; random.nextBytes(outSecret); - tagKey = crypto.deriveTagKey(outSecret, true); - frameKey = crypto.deriveFrameKey(outSecret, true); + frameKey = crypto.deriveFrameKey(outSecret, 0L, true, true); } @Test @@ -60,22 +54,17 @@ public class FrameReadWriteTest extends BriarTestCase { } private void testWriteAndRead(boolean initiator) throws Exception { - // Encode the tag - byte[] tag = new byte[TAG_LENGTH]; - TagEncoder.encodeTag(tag, tagCipher, tagKey); // Generate two random frames byte[] frame = new byte[1234]; random.nextBytes(frame); byte[] frame1 = new byte[321]; random.nextBytes(frame1); - // Copy the keys - the copies will be erased - ErasableKey tagCopy = tagKey.copy(); + // Copy the frame key - the copy will be erased ErasableKey frameCopy = frameKey.copy(); // Write the frames ByteArrayOutputStream out = new ByteArrayOutputStream(); FrameWriter encryptionOut = new OutgoingEncryptionLayer(out, - Long.MAX_VALUE, tagCipher, frameCipher, tagCopy, frameCopy, - FRAME_LENGTH); + Long.MAX_VALUE, frameCipher, frameCopy, FRAME_LENGTH); ConnectionWriter writer = new ConnectionWriterImpl(encryptionOut, FRAME_LENGTH); OutputStream out1 = writer.getOutputStream(); @@ -84,11 +73,11 @@ public class FrameReadWriteTest extends BriarTestCase { out1.write(frame1); out1.flush(); byte[] output = out.toByteArray(); - assertEquals(TAG_LENGTH + FRAME_LENGTH * 2, output.length); + assertEquals(FRAME_LENGTH * 2, output.length); // Read the tag and the frames back ByteArrayInputStream in = new ByteArrayInputStream(output); - FrameReader encryptionIn = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, frameKey, FRAME_LENGTH); + FrameReader encryptionIn = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); ConnectionReader reader = new ConnectionReaderImpl(encryptionIn, FRAME_LENGTH); InputStream in1 = reader.getInputStream(); diff --git a/test/net/sf/briar/transport/IncomingEncryptionLayerTest.java b/test/net/sf/briar/transport/IncomingEncryptionLayerTest.java index 34f94a3617f03922d17c2edcba070b5338188986..4eb9dc02aeb6c5cc217bf312cd69e5e010ed8337 100644 --- a/test/net/sf/briar/transport/IncomingEncryptionLayerTest.java +++ b/test/net/sf/briar/transport/IncomingEncryptionLayerTest.java @@ -5,12 +5,8 @@ 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.ByteArrayInputStream; -import java.io.EOFException; - -import javax.crypto.Cipher; import net.sf.briar.BriarTestCase; import net.sf.briar.api.FormatException; @@ -19,7 +15,6 @@ import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.ErasableKey; import net.sf.briar.crypto.CryptoModule; -import org.junit.Before; import org.junit.Test; import com.google.inject.Guice; @@ -32,99 +27,46 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; private final CryptoComponent crypto; - private final Cipher tagCipher; private final AuthenticatedCipher frameCipher; - - private ErasableKey tagKey = null, frameKey = null; + private final ErasableKey frameKey; public IncomingEncryptionLayerTest() { super(); Injector i = Guice.createInjector(new CryptoModule()); crypto = i.getInstance(CryptoComponent.class); - tagCipher = crypto.getTagCipher(); frameCipher = crypto.getFrameCipher(); - } - - @Before - public void setUp() { - tagKey = crypto.generateTestKey(); frameKey = crypto.generateTestKey(); } @Test - public void testReadValidTagAndFrames() throws Exception { - // Generate a valid tag - byte[] tag = generateTag(tagKey); + public void testReadValidFrames() throws Exception { // Generate two valid frames byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, false); byte[] frame1 = generateFrame(1L, FRAME_LENGTH, 123, false, false); - // Concatenate the tag and the frames - byte[] valid = new byte[TAG_LENGTH + FRAME_LENGTH * 2]; - System.arraycopy(tag, 0, valid, 0, TAG_LENGTH); - System.arraycopy(frame, 0, valid, TAG_LENGTH, FRAME_LENGTH); - System.arraycopy(frame1, 0, valid, TAG_LENGTH + FRAME_LENGTH, - FRAME_LENGTH); - // Read the frames, which should first read the tag + // Concatenate the frames + byte[] valid = new byte[FRAME_LENGTH * 2]; + System.arraycopy(frame, 0, valid, 0, FRAME_LENGTH); + System.arraycopy(frame1, 0, valid, FRAME_LENGTH, FRAME_LENGTH); + // Read the frames ByteArrayInputStream in = new ByteArrayInputStream(valid); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, frameKey, FRAME_LENGTH); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); byte[] buf = new byte[FRAME_LENGTH - MAC_LENGTH]; assertEquals(123, i.readFrame(buf)); assertEquals(123, i.readFrame(buf)); } - @Test - public void testTruncatedTagThrowsException() throws Exception { - // Generate a valid tag - byte[] tag = generateTag(tagKey); - // Chop off the last byte - byte[] truncated = new byte[TAG_LENGTH - 1]; - System.arraycopy(tag, 0, truncated, 0, TAG_LENGTH - 1); - // Try to read the frame, which should first try to read the tag - ByteArrayInputStream in = new ByteArrayInputStream(truncated); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, crypto.generateTestKey(), FRAME_LENGTH); - try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); - fail(); - } catch(EOFException expected) {} - } - @Test public void testTruncatedFrameThrowsException() throws Exception { - // Generate a valid tag - byte[] tag = generateTag(tagKey); // Generate a valid frame byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, false); // Chop off the last byte - byte[] truncated = new byte[TAG_LENGTH + FRAME_LENGTH - 1]; - System.arraycopy(tag, 0, truncated, 0, TAG_LENGTH); - System.arraycopy(frame, 0, truncated, TAG_LENGTH, FRAME_LENGTH - 1); + byte[] truncated = new byte[FRAME_LENGTH - 1]; + System.arraycopy(frame, 0, truncated, 0, FRAME_LENGTH - 1); // Try to read the frame, which should fail due to truncation ByteArrayInputStream in = new ByteArrayInputStream(truncated); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, frameKey, FRAME_LENGTH); - try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); - fail(); - } catch(FormatException expected) {} - } - - @Test - public void testModifiedTagThrowsException() throws Exception { - // Generate a valid tag - byte[] tag = generateTag(tagKey); - // Generate a valid frame - byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, false); - // Modify a randomly chosen byte of the tag - byte[] modified = new byte[TAG_LENGTH + FRAME_LENGTH]; - System.arraycopy(tag, 0, modified, 0, TAG_LENGTH); - System.arraycopy(frame, 0, modified, TAG_LENGTH, FRAME_LENGTH); - modified[(int) (Math.random() * TAG_LENGTH)] ^= 1; - // Try to read the frame, which should fail due to modification - ByteArrayInputStream in = new ByteArrayInputStream(modified); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, frameKey, FRAME_LENGTH); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); try { i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); fail(); @@ -133,19 +75,14 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { @Test public void testModifiedFrameThrowsException() throws Exception { - // Generate a valid tag - byte[] tag = generateTag(tagKey); // Generate a valid frame byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, false); // Modify a randomly chosen byte of the frame - byte[] modified = new byte[TAG_LENGTH + FRAME_LENGTH]; - System.arraycopy(tag, 0, modified, 0, TAG_LENGTH); - System.arraycopy(frame, 0, modified, TAG_LENGTH, FRAME_LENGTH); - modified[TAG_LENGTH + (int) (Math.random() * FRAME_LENGTH)] ^= 1; + frame[(int) (Math.random() * FRAME_LENGTH)] ^= 1; // Try to read the frame, which should fail due to modification - ByteArrayInputStream in = new ByteArrayInputStream(modified); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, frameKey, FRAME_LENGTH); + ByteArrayInputStream in = new ByteArrayInputStream(frame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); try { i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); fail(); @@ -154,18 +91,12 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { @Test public void testShortNonFinalFrameThrowsException() throws Exception { - // Generate a valid tag - byte[] tag = generateTag(tagKey); // Generate a short non-final frame byte[] frame = generateFrame(0L, FRAME_LENGTH - 1, 123, false, false); - // Concatenate the tag and the frame - byte[] tooShort = new byte[TAG_LENGTH + FRAME_LENGTH - 1]; - System.arraycopy(tag, 0, tooShort, 0, TAG_LENGTH); - System.arraycopy(frame, 0, tooShort, TAG_LENGTH, FRAME_LENGTH - 1); // Try to read the frame, which should fail due to invalid length - ByteArrayInputStream in = new ByteArrayInputStream(tooShort); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, frameKey, FRAME_LENGTH); + ByteArrayInputStream in = new ByteArrayInputStream(frame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); try { i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); fail(); @@ -174,37 +105,25 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { @Test public void testShortFinalFrameDoesNotThrowException() throws Exception { - // Generate a valid tag - byte[] tag = generateTag(tagKey); // Generate a short final frame byte[] frame = generateFrame(0L, FRAME_LENGTH - 1, 123, true, false); - // Concatenate the tag and the frame - byte[] valid = new byte[TAG_LENGTH + FRAME_LENGTH - 1]; - System.arraycopy(tag, 0, valid, 0, TAG_LENGTH); - System.arraycopy(frame, 0, valid, TAG_LENGTH, FRAME_LENGTH - 1); - // Read the frame, which should first read the tag - ByteArrayInputStream in = new ByteArrayInputStream(valid); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, frameKey, FRAME_LENGTH); + // Read the frame + ByteArrayInputStream in = new ByteArrayInputStream(frame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); int length = i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); assertEquals(123, length); } @Test public void testInvalidPayloadLengthThrowsException() throws Exception { - // Generate a valid tag - byte[] tag = generateTag(tagKey); // Generate a frame with an invalid payload length byte[] frame = generateFrame(0L, FRAME_LENGTH, MAX_PAYLOAD_LENGTH + 1, false, false); - // Concatenate the tag and the frame - byte[] tooLong = new byte[TAG_LENGTH + FRAME_LENGTH]; - System.arraycopy(tag, 0, tooLong, 0, TAG_LENGTH); - System.arraycopy(frame, 0, tooLong, TAG_LENGTH, FRAME_LENGTH); // Try to read the frame, which should fail due to invalid length - ByteArrayInputStream in = new ByteArrayInputStream(tooLong); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, frameKey, FRAME_LENGTH); + ByteArrayInputStream in = new ByteArrayInputStream(frame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); try { i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); fail(); @@ -213,18 +132,12 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { @Test public void testNonZeroPaddingThrowsException() throws Exception { - // Generate a valid tag - byte[] tag = generateTag(tagKey); - // Generate a frame with pad padding + // Generate a frame with bad padding byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, true); - // Concatenate the tag and the frame - byte[] badPadding = new byte[TAG_LENGTH + FRAME_LENGTH]; - System.arraycopy(tag, 0, badPadding, 0, TAG_LENGTH); - System.arraycopy(frame, 0, badPadding, TAG_LENGTH, FRAME_LENGTH); // Try to read the frame, which should fail due to bad padding - ByteArrayInputStream in = new ByteArrayInputStream(badPadding); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, frameKey, FRAME_LENGTH); + ByteArrayInputStream in = new ByteArrayInputStream(frame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); try { i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); fail(); @@ -233,34 +146,24 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { @Test public void testCannotReadBeyondFinalFrame() throws Exception { - // Generate a valid tag - byte[] tag = generateTag(tagKey); // Generate a valid final frame and another valid final frame after it byte[] frame = generateFrame(0L, FRAME_LENGTH, MAX_PAYLOAD_LENGTH, true, false); byte[] frame1 = generateFrame(1L, FRAME_LENGTH, 123, true, false); - // Concatenate the tag and the frames - byte[] extraFrame = new byte[TAG_LENGTH + FRAME_LENGTH * 2]; - System.arraycopy(tag, 0, extraFrame, 0, TAG_LENGTH); - System.arraycopy(frame, 0, extraFrame, TAG_LENGTH, FRAME_LENGTH); - System.arraycopy(frame1, 0, extraFrame, TAG_LENGTH + FRAME_LENGTH, - FRAME_LENGTH); + // Concatenate the frames + byte[] extraFrame = new byte[FRAME_LENGTH * 2]; + System.arraycopy(frame, 0, extraFrame, 0, FRAME_LENGTH); + System.arraycopy(frame1, 0, extraFrame, FRAME_LENGTH, FRAME_LENGTH); // Read the final frame, which should first read the tag ByteArrayInputStream in = new ByteArrayInputStream(extraFrame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, tagCipher, - frameCipher, tagKey, frameKey, FRAME_LENGTH); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); byte[] buf = new byte[FRAME_LENGTH - MAC_LENGTH]; assertEquals(MAX_PAYLOAD_LENGTH, i.readFrame(buf)); // The frame after the final frame should not be read assertEquals(-1, i.readFrame(buf)); } - private byte[] generateTag(ErasableKey tagKey) { - byte[] tag = new byte[TAG_LENGTH]; - TagEncoder.encodeTag(tag, tagCipher, tagKey); - return tag; - } - private byte[] generateFrame(long frameNumber, int frameLength, int payloadLength, boolean finalFrame, boolean badPadding) throws Exception { diff --git a/test/net/sf/briar/transport/OutgoingEncryptionLayerTest.java b/test/net/sf/briar/transport/OutgoingEncryptionLayerTest.java index 68c3f1f9343acceca7911e3b094a828d6db3c71e..f4eba234864abc61d0e2af08f645e76b8345204c 100644 --- a/test/net/sf/briar/transport/OutgoingEncryptionLayerTest.java +++ b/test/net/sf/briar/transport/OutgoingEncryptionLayerTest.java @@ -9,8 +9,6 @@ import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import java.io.ByteArrayOutputStream; -import javax.crypto.Cipher; - import net.sf.briar.BriarTestCase; import net.sf.briar.api.crypto.AuthenticatedCipher; import net.sf.briar.api.crypto.CryptoComponent; @@ -29,28 +27,24 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; private final CryptoComponent crypto; - private final Cipher tagCipher; private final AuthenticatedCipher frameCipher; + private final byte[] tag; public OutgoingEncryptionLayerTest() { super(); Injector i = Guice.createInjector(new CryptoModule()); crypto = i.getInstance(CryptoComponent.class); - tagCipher = crypto.getTagCipher(); frameCipher = crypto.getFrameCipher(); + tag = new byte[TAG_LENGTH]; } @Test public void testEncryption() throws Exception { int payloadLength = 123; - byte[] tag = new byte[TAG_LENGTH]; byte[] iv = new byte[IV_LENGTH], aad = new byte[AAD_LENGTH]; byte[] plaintext = new byte[FRAME_LENGTH - MAC_LENGTH]; byte[] ciphertext = new byte[FRAME_LENGTH]; - ErasableKey tagKey = crypto.generateTestKey(); ErasableKey frameKey = crypto.generateTestKey(); - // Calculate the expected tag - TagEncoder.encodeTag(tag, tagCipher, tagKey); // Calculate the expected ciphertext FrameEncoder.encodeIv(iv, 0); FrameEncoder.encodeAad(aad, 0, plaintext.length); @@ -60,14 +54,11 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { // Check that the actual tag and ciphertext match what's expected ByteArrayOutputStream out = new ByteArrayOutputStream(); OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - 10 * FRAME_LENGTH, tagCipher, frameCipher, tagKey, frameKey, - FRAME_LENGTH); + 10 * FRAME_LENGTH, frameCipher, frameKey, FRAME_LENGTH, tag); o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], payloadLength, false); byte[] actual = out.toByteArray(); assertEquals(TAG_LENGTH + FRAME_LENGTH, actual.length); - for(int i = 0; i < TAG_LENGTH; i++) { - assertEquals(tag[i], actual[i]); - } + for(int i = 0; i < TAG_LENGTH; i++) assertEquals(tag[i], actual[i]); for(int i = 0; i < FRAME_LENGTH; i++) { assertEquals("" + i, ciphertext[i], actual[TAG_LENGTH + i]); } @@ -78,9 +69,8 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { ByteArrayOutputStream out = new ByteArrayOutputStream(); // Initiator's constructor OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - 10 * FRAME_LENGTH, tagCipher, frameCipher, - crypto.generateTestKey(), crypto.generateTestKey(), - FRAME_LENGTH); + 10 * FRAME_LENGTH, frameCipher, crypto.generateTestKey(), + FRAME_LENGTH, tag); // Write an empty final frame without having written any other frames o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true); // Nothing should be written to the output stream @@ -106,9 +96,8 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { ByteArrayOutputStream out = new ByteArrayOutputStream(); // Initiator's constructor OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - 10 * FRAME_LENGTH, tagCipher, frameCipher, - crypto.generateTestKey(), crypto.generateTestKey(), - FRAME_LENGTH); + 10 * FRAME_LENGTH, frameCipher, crypto.generateTestKey(), + FRAME_LENGTH, tag); // There should be space for nine full frames and one partial frame byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH]; assertEquals(10 * MAX_PAYLOAD_LENGTH - TAG_LENGTH,