From cacb04eca0c3344797ea8dc6c57ae52a262fa551 Mon Sep 17 00:00:00 2001 From: goapunk <goapunk@riseup.net> Date: Thu, 20 Sep 2018 13:12:56 +0200 Subject: [PATCH] fix --- .../bramble/api/contact/ContactManager.java | 8 + .../bramble/contact/ContactManagerImpl.java | 32 ++ .../mailbox/MailboxIntroductionManager.java | 12 +- .../briarproject/briar/api/mailbox/Role.java | 29 ++ .../briar/mailbox/AbstractProtocolEngine.java | 284 +++++++------- .../briar/mailbox/IntroduceeSession.java | 361 +++++++----------- .../briar/mailbox/IntroductionConstants.java | 1 + .../mailbox/MailboxIntroductionCrypto.java | 101 +++++ .../MailboxIntroductionCryptoImpl.java | 237 ++++++++++++ .../MailboxIntroductionManagerImpl.java | 147 ++++++- .../mailbox/MailboxIntroductionModule.java | 48 +++ .../mailbox/MailboxIntroductionValidator.java | 172 +++++++++ .../briar/mailbox/MailboxProtocolEngine.java | 4 - .../briar/mailbox/MailboxSession.java | 161 ++++++++ .../briar/mailbox/MessageEncoder.java | 39 +- .../briar/mailbox/MessageEncoderImpl.java | 248 ++++++------ .../briar/mailbox/MessageParser.java | 17 +- .../briar/mailbox/MessageParserImpl.java | 67 ++-- .../briar/mailbox/OwnerProtocolEngine.java | 10 +- .../briar/mailbox/OwnerSession.java | 62 +-- .../briarproject/briar/mailbox/Session.java | 2 +- .../briar/mailbox/SessionEncoder.java | 18 + .../briar/mailbox/SessionEncoderImpl.java | 155 ++++++++ .../briar/mailbox/SessionParser.java | 23 ++ .../briar/mailbox/SessionParserImpl.java | 210 ++++++++++ .../IntroductionIntegrationTestComponent.java | 2 + .../MailboxIntroductionIntegrationTest.java | 352 +++++++++++++++++ ...xIntroductionIntegrationTestComponent.java | 78 ++++ .../briar/test/BriarIntegrationTest.java | 99 ++++- .../test/BriarIntegrationTestComponent.java | 7 + 30 files changed, 2351 insertions(+), 635 deletions(-) create mode 100644 briar-api/src/main/java/org/briarproject/briar/api/mailbox/Role.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionCrypto.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionCryptoImpl.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionValidator.java delete mode 100644 briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxProtocolEngine.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxSession.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/mailbox/SessionEncoder.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/mailbox/SessionEncoderImpl.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/mailbox/SessionParser.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/mailbox/SessionParserImpl.java create mode 100644 briar-core/src/test/java/org/briarproject/briar/mailbox/MailboxIntroductionIntegrationTest.java create mode 100644 briar-core/src/test/java/org/briarproject/briar/mailbox/MailboxIntroductionIntegrationTestComponent.java diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java index d8693d79f..e2b3299e1 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java @@ -49,6 +49,10 @@ public interface ContactManager { long timestamp, boolean alice, boolean verified, boolean active) throws DbException; + ContactId addPrivateMailbox(Author remote, AuthorId local, SecretKey master, + long timestamp, boolean alice) + throws DbException; + /** * Add a private Mailbox */ @@ -58,6 +62,10 @@ public interface ContactManager { ContactId addMailboxOwner(Transaction txn, Author remote, AuthorId local, SecretKey master, long timestamp, boolean alice) throws DbException; + ContactId addMailboxOwner(Author remote, AuthorId local, + SecretKey secretKey, long currentTimeMillis, boolean alice) + throws DbException; + /** * Returns the contact with the given ID. */ diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java index 976f270f8..f6b8fb515 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java @@ -91,6 +91,22 @@ class ContactManagerImpl implements ContactManager { return c; } + @Override + public ContactId addPrivateMailbox(Author remote, AuthorId local, + SecretKey master, + long timestamp, boolean alice) + throws DbException { + ContactId c; + Transaction txn = db.startTransaction(false); + try { + c = addPrivateMailbox(txn, remote, local, master, timestamp, alice); + db.commitTransaction(txn); + } finally { + db.endTransaction(txn); + } + return c; + } + @Override public ContactId addPrivateMailbox(Transaction txn, Author remote, AuthorId local, SecretKey master, long timestamp, boolean alice) @@ -106,6 +122,22 @@ class ContactManagerImpl implements ContactManager { return c; } + + @Override + public ContactId addMailboxOwner(Author remote, AuthorId local, + SecretKey master, long timestamp, boolean alice) + throws DbException { + ContactId c; + Transaction txn = db.startTransaction(false); + try { + c = addMailboxOwner(txn, remote, local, master, timestamp, alice); + db.commitTransaction(txn); + } finally { + db.endTransaction(txn); + } + return c; + } + @Override public ContactId addMailboxOwner(Transaction txn, Author remote, AuthorId local, SecretKey master, long timestamp, boolean alice) diff --git a/briar-api/src/main/java/org/briarproject/briar/api/mailbox/MailboxIntroductionManager.java b/briar-api/src/main/java/org/briarproject/briar/api/mailbox/MailboxIntroductionManager.java index 3edb9ab0b..709be9340 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/mailbox/MailboxIntroductionManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/mailbox/MailboxIntroductionManager.java @@ -5,6 +5,7 @@ import org.briarproject.bramble.api.contact.PrivateMailbox; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.sync.ClientId; +import org.briarproject.bramble.api.sync.Group; public interface MailboxIntroductionManager { /** @@ -25,6 +26,15 @@ public interface MailboxIntroductionManager { void contactAdded(Transaction txn, Contact contact) throws DbException; - void privateMailboxAdded(Transaction txn, PrivateMailbox privateMailbox) throws DbException; + void privateMailboxAdded(Transaction txn, PrivateMailbox privateMailbox) + throws DbException; + /** + * Sends two initial introduction messages. + */ + void makeIntroduction(PrivateMailbox privateMailbox, Contact contact, long timestamp) + throws DbException; + + + Group getContactGroup(Contact c); } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/mailbox/Role.java b/briar-api/src/main/java/org/briarproject/briar/api/mailbox/Role.java new file mode 100644 index 000000000..4ed40c1d0 --- /dev/null +++ b/briar-api/src/main/java/org/briarproject/briar/api/mailbox/Role.java @@ -0,0 +1,29 @@ +package org.briarproject.briar.api.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +public enum Role { + + OWNER(0), MAILBOX(1), INTRODUCEE(2); + + private final int value; + + Role(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Role fromValue(int value) throws FormatException { + for (Role r : values()) if (r.value == value) return r; + throw new FormatException(); + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/AbstractProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/AbstractProtocolEngine.java index 07231a624..ad3cd17b2 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/AbstractProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/AbstractProtocolEngine.java @@ -25,153 +25,157 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import static org.briarproject.briar.mailbox.MessageType.ABORT; -import static org.briarproject.briar.mailbox.MessageType.CONTACT_ACCEPT; import static org.briarproject.briar.mailbox.MessageType.CONTACT_REQUEST; import static org.briarproject.briar.mailbox.MessageType.DECLINE; import static org.briarproject.briar.mailbox.MessageType.MAILBOX_ACCEPT; -import static org.briarproject.briar.mailbox.MessageType.MAILBOX_INFO; import static org.briarproject.briar.mailbox.MessageType.MAILBOX_REQUEST; @Immutable @NotNullByDefault abstract class AbstractProtocolEngine<S extends Session> - implements ProtocolEngine<S> { - - protected final DatabaseComponent db; - protected final ClientHelper clientHelper; - protected final ContactManager contactManager; - protected final ContactGroupFactory contactGroupFactory; - protected final MessageTracker messageTracker; - protected final IdentityManager identityManager; - protected final MessageParser messageParser; - protected final MessageEncoder messageEncoder; - protected final Clock clock; - - AbstractProtocolEngine( - DatabaseComponent db, - ClientHelper clientHelper, - ContactManager contactManager, - ContactGroupFactory contactGroupFactory, - MessageTracker messageTracker, - IdentityManager identityManager, - MessageParser messageParser, - MessageEncoder messageEncoder, - Clock clock) { - this.db = db; - this.clientHelper = clientHelper; - this.contactManager = contactManager; - this.contactGroupFactory = contactGroupFactory; - this.messageTracker = messageTracker; - this.identityManager = identityManager; - this.messageParser = messageParser; - this.messageEncoder = messageEncoder; - this.clock = clock; - } - - Message sendMailboxRequestMessage(Transaction txn, PeerSession s, long timestamp) throws DbException { - Message m = messageEncoder - .encodeRequestMessage(s.getContactGroupId(), timestamp, s.getLastLocalMessageId()); - sendMessage(txn, MAILBOX_REQUEST, s.getSessionId(), m); - return m; - } - - Message sendMailboxAcceptMessage(Transaction txn, PeerSession s, - long timestamp, - byte[] ephemeralPublicKey, long acceptTimestamp, - Map<TransportId, TransportProperties> transportProperties, - boolean visible) throws DbException { - Message m = messageEncoder - .encodeAcceptMessage(s.getContactGroupId(), timestamp, - s.getLastLocalMessageId(), s.getSessionId(), - ephemeralPublicKey, acceptTimestamp, - transportProperties); - sendMessage(txn, MAILBOX_ACCEPT, s.getSessionId(), m); - return m; - } - - Message sendContactRequestMessage(Transaction txn, PeerSession s, - long timestamp, Author author, @Nullable String message) - throws DbException { - Message m = messageEncoder - .encodeRequestMessage(s.getContactGroupId(), timestamp, - s.getLastLocalMessageId(), author, message); - sendMessage(txn, CONTACT_REQUEST, s.getSessionId(), m); - return m; - } - - Message sendContactAcceptMessage(Transaction txn, PeerSession s, - long timestamp, - byte[] ephemeralPublicKey, long acceptTimestamp, - Map<TransportId, TransportProperties> transportProperties, - boolean visible) throws DbException { - Message m = messageEncoder - .encodeAcceptMessage(s.getContactGroupId(), timestamp, - s.getLastLocalMessageId(), s.getSessionId(), - ephemeralPublicKey, acceptTimestamp, - transportProperties); - sendMessage(txn, CONTACT_ACCEPT, s.getSessionId(), m); - return m; - } - - Message sendMailboxInfotMessage(Transaction txn, PeerSession s, - long timestamp, - byte[] ephemeralPublicKey, long acceptTimestamp, - Map<TransportId, TransportProperties> transportProperties, - boolean visible) throws DbException { - Message m = messageEncoder - .encodeAcceptMessage(s.getContactGroupId(), timestamp, - s.getLastLocalMessageId(), s.getSessionId(), - ephemeralPublicKey, acceptTimestamp, - transportProperties); - sendMessage(txn, MAILBOX_INFO, s.getSessionId(), m); - return m; - } - - Message sendDeclineMessage(Transaction txn, PeerSession s, long timestamp, - boolean visible) throws DbException { - Message m = messageEncoder - .encodeDeclineMessage(s.getContactGroupId(), timestamp, - s.getLastLocalMessageId(), s.getSessionId()); - sendMessage(txn, DECLINE, s.getSessionId(), m); - return m; - } - - Message sendAbortMessage(Transaction txn, PeerSession s, long timestamp) - throws DbException { - Message m = messageEncoder - .encodeAbortMessage(s.getContactGroupId(), timestamp, - s.getLastLocalMessageId(), s.getSessionId()); - sendMessage(txn, ABORT, s.getSessionId(), m); - return m; - } - - private void sendMessage(Transaction txn, MessageType type, - SessionId sessionId, Message m) - throws DbException { - BdfDictionary meta = messageEncoder - .encodeMetadata(type, sessionId, m.getTimestamp(), true, true); - try { - clientHelper.addLocalMessage(txn, m, meta, true); - } catch (FormatException e) { - throw new AssertionError(e); - } - } - - boolean isInvalidDependency(@Nullable MessageId lastRemoteMessageId, - @Nullable MessageId dependency) { - if (dependency == null) return lastRemoteMessageId != null; - return lastRemoteMessageId == null || - !dependency.equals(lastRemoteMessageId); - } - - long getLocalTimestamp(long localTimestamp, long requestTimestamp) { - return Math.max( - clock.currentTimeMillis(), - Math.max( - localTimestamp, - requestTimestamp - ) + 1 - ); - } + implements ProtocolEngine<S> { + + protected final DatabaseComponent db; + protected final ClientHelper clientHelper; + protected final ContactManager contactManager; + protected final ContactGroupFactory contactGroupFactory; + protected final MessageTracker messageTracker; + protected final IdentityManager identityManager; + protected final MessageParser messageParser; + protected final MessageEncoder messageEncoder; + protected final Clock clock; + + AbstractProtocolEngine( + DatabaseComponent db, + ClientHelper clientHelper, + ContactManager contactManager, + ContactGroupFactory contactGroupFactory, + MessageTracker messageTracker, + IdentityManager identityManager, + MessageParser messageParser, + MessageEncoder messageEncoder, + Clock clock) { + this.db = db; + this.clientHelper = clientHelper; + this.contactManager = contactManager; + this.contactGroupFactory = contactGroupFactory; + this.messageTracker = messageTracker; + this.identityManager = identityManager; + this.messageParser = messageParser; + this.messageEncoder = messageEncoder; + this.clock = clock; + } + + Message sendMailboxRequestMessage(Transaction txn, PeerSession s, + long timestamp) throws DbException { + Message m = messageEncoder + .encodeRequestMessage(s.getContactGroupId(), timestamp, + s.getLastLocalMessageId()); + sendMessage(txn, MAILBOX_REQUEST, s.getSessionId(), m); + return m; + } + + Message sendMailboxAcceptMessage(Transaction txn, PeerSession s, + long timestamp, + byte[] ephemeralPublicKey, long acceptTimestamp, + Map<TransportId, TransportProperties> transportProperties, + boolean visible) throws DbException { + Message m = messageEncoder + .encodeMailboxAcceptMessage(s.getContactGroupId(), timestamp, + s.getLastLocalMessageId(), s.getSessionId(), + ephemeralPublicKey, acceptTimestamp, + transportProperties); + sendMessage(txn, MAILBOX_ACCEPT, s.getSessionId(), m); + return m; + } + + Message sendContactRequestMessage(Transaction txn, PeerSession s, + long timestamp, Author author, @Nullable String message) + throws DbException { + Message m = messageEncoder + .encodeRequestMessage(s.getContactGroupId(), timestamp, + s.getLastLocalMessageId()); + sendMessage(txn, CONTACT_REQUEST, s.getSessionId(), m); + return m; + } + + /* + Message sendContactAcceptMessage(Transaction txn, PeerSession s, + long timestamp, + byte[] ephemeralPublicKey, long acceptTimestamp, + Map<TransportId, TransportProperties> transportProperties, + boolean visible) throws DbException { + Message m = messageEncoder + .encodeAcceptMessage(s.getContactGroupId(), timestamp, + s.getLastLocalMessageId(), s.getSessionId(), + ephemeralPublicKey, acceptTimestamp, + transportProperties); + sendMessage(txn, CONTACT_ACCEPT, s.getSessionId(), m); + return m; + } + */ + + /* + Message sendMailboxInfotMessage(Transaction txn, PeerSession s, + long timestamp, + byte[] ephemeralPublicKey, long acceptTimestamp, + Map<TransportId, TransportProperties> transportProperties, + boolean visible) throws DbException { + Message m = messageEncoder + .encodeMaiAcceptMessage(s.getContactGroupId(), timestamp, + s.getLastLocalMessageId(), s.getSessionId(), + ephemeralPublicKey, acceptTimestamp, + transportProperties); + sendMessage(txn, MAILBOX_AUTH, s.getSessionId(), m); + return m; + } + */ + + Message sendDeclineMessage(Transaction txn, PeerSession s, long timestamp, + boolean visible) throws DbException { + Message m = messageEncoder + .encodeDeclineMessage(s.getContactGroupId(), timestamp, + s.getLastLocalMessageId(), s.getSessionId()); + sendMessage(txn, DECLINE, s.getSessionId(), m); + return m; + } + + Message sendAbortMessage(Transaction txn, PeerSession s, long timestamp) + throws DbException { + Message m = messageEncoder + .encodeAbortMessage(s.getContactGroupId(), timestamp, + s.getLastLocalMessageId(), s.getSessionId()); + sendMessage(txn, ABORT, s.getSessionId(), m); + return m; + } + + private void sendMessage(Transaction txn, MessageType type, + SessionId sessionId, Message m) + throws DbException { + BdfDictionary meta = messageEncoder + .encodeMetadata(type, sessionId, m.getTimestamp(), true); + try { + clientHelper.addLocalMessage(txn, m, meta, true); + } catch (FormatException e) { + throw new AssertionError(e); + } + } + + boolean isInvalidDependency(@Nullable MessageId lastRemoteMessageId, + @Nullable MessageId dependency) { + if (dependency == null) return lastRemoteMessageId != null; + return lastRemoteMessageId == null || + !dependency.equals(lastRemoteMessageId); + } + + long getLocalTimestamp(long localTimestamp, long requestTimestamp) { + return Math.max( + clock.currentTimeMillis(), + Math.max( + localTimestamp, + requestTimestamp + ) + 1 + ); + } } diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroduceeSession.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroduceeSession.java index 7f8b5b60a..0ae5cf1f1 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroduceeSession.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroduceeSession.java @@ -1,244 +1,157 @@ package org.briarproject.briar.mailbox; -import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.sync.GroupId; -import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.briar.api.client.SessionId; -import org.briarproject.briar.api.introduction.Role; import java.util.Map; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -import static org.briarproject.briar.api.introduction.Role.INTRODUCEE; -import static org.briarproject.briar.mailbox.IntroduceeState.AWAIT_REMOTE_RESPONSE; -import static org.briarproject.briar.mailbox.IntroduceeState.START; - @Immutable @NotNullByDefault -class IntroduceeSession extends Session<IntroduceeState> implements PeerSession { - - private final GroupId contactGroupId; - private final Author introducer; - private final Local local; - private final Remote remote; - @Nullable - private final byte[] masterKey; - @Nullable - private final Map<TransportId, KeySetId> transportKeys; - - IntroduceeSession(SessionId sessionId, IntroduceeState state, - long requestTimestamp, GroupId contactGroupId, Author introducer, - Local local, Remote remote, @Nullable byte[] masterKey, - @Nullable Map<TransportId, KeySetId> transportKeys, - long sessionCounter) { - super(sessionId, state, requestTimestamp, sessionCounter); - this.contactGroupId = contactGroupId; - this.introducer = introducer; - this.local = local; - this.remote = remote; - this.masterKey = masterKey; - this.transportKeys = transportKeys; - } - - static IntroduceeSession getInitial(GroupId contactGroupId, - SessionId sessionId, Author introducer, boolean localIsAlice, - Author remoteAuthor) { - Local local = - new Local(localIsAlice, null, -1, null, null, -1, null); - Remote remote = - new Remote(!localIsAlice, remoteAuthor, null, null, null, -1, - null); - return new IntroduceeSession(sessionId, START, -1, contactGroupId, - introducer, local, remote, null, null, 0); - } - - static IntroduceeSession addRemoteRequest( - IntroduceeSession s, - IntroduceeState state, - ContactRequestMessage m, byte[] ephemeralPublicKey, - byte[] ephemeralPrivateKey, - long acceptTimestamp, boolean alice) { - Local local = new Local(alice, m.getMessageId(), - m.getTimestamp(), ephemeralPublicKey, ephemeralPrivateKey, - acceptTimestamp, null); - Remote remote = - new Remote(!alice, s.remote.author, m.getMessageId(), - m.getEphemeralPublicKey(), null, m.getAcceptTimestamp(), - null); - return new IntroduceeSession(s.getSessionId(), state, m.getTimestamp(), - s.contactGroupId, s.introducer, local, remote, s.masterKey, - s.transportKeys, s.getSessionCounter()); - } - - static IntroduceeSession addLocalAuth( - IntroduceeSession s, - IntroduceeState state, Message m, SecretKey masterKey, - SecretKey aliceMacKey, SecretKey bobMacKey) { - // add mac key and sent message - Local local = new Local(s.local.alice, m.getId(), m.getTimestamp(), - s.local.ephemeralPublicKey, s.local.ephemeralPrivateKey, - s.local.acceptTimestamp, - s.local.alice ? aliceMacKey.getBytes() : bobMacKey.getBytes()); - // just add the mac key - Remote remote = new Remote(s.remote.alice, s.remote.author, - s.remote.lastMessageId, s.remote.ephemeralPublicKey, - s.remote.transportProperties, s.remote.acceptTimestamp, - s.remote.alice ? aliceMacKey.getBytes() : bobMacKey.getBytes()); - // add master key - return new IntroduceeSession(s.getSessionId(), state, - s.getRequestTimestamp(), s.contactGroupId, s.introducer, local, - remote, masterKey.getBytes(), s.transportKeys, - s.getSessionCounter()); - } - - static IntroduceeSession awaitAuth( - IntroduceeSession s, MailboxAuthMessage m, - Message sent, @Nullable Map<TransportId, KeySetId> transportKeys) { - Local local = new Local(s.local, sent.getId(), sent.getTimestamp()); - Remote remote = new Remote(s.remote, m.getMessageId()); - return new IntroduceeSession(s.getSessionId(), AWAIT_REMOTE_RESPONSE, - s.getRequestTimestamp(), s.contactGroupId, s.introducer, local, - remote, null, transportKeys, s.getSessionCounter()); - } - - static IntroduceeSession clear( - IntroduceeSession s, IntroduceeState state, - @Nullable MessageId lastLocalMessageId, long localTimestamp, - @Nullable MessageId lastRemoteMessageId) { - Local local = - new Local(s.local.alice, lastLocalMessageId, localTimestamp, - null, null, -1, null); - Remote remote = - new Remote(s.remote.alice, s.remote.author, lastRemoteMessageId, - null, null, -1, null); - return new IntroduceeSession(s.getSessionId(), state, - s.getRequestTimestamp(), s.contactGroupId, s.introducer, local, - remote, null, null, s.getSessionCounter()); - } - - @Override - Role getRole() { - return INTRODUCEE; - } - - @Override - public GroupId getContactGroupId() { - return contactGroupId; - } - - @Override - public long getLocalTimestamp() { - return local.lastMessageTimestamp; - } - - @Nullable - @Override - public MessageId getLastLocalMessageId() { - return local.lastMessageId; - } - - @Nullable - @Override - public MessageId getLastRemoteMessageId() { - return remote.lastMessageId; - } - - Author getIntroducer() { - return introducer; - } - - public Local getLocal() { - return local; - } - - public Remote getRemote() { - return remote; - } - - @Nullable - byte[] getMasterKey() { - return masterKey; - } - - @Nullable - Map<TransportId, KeySetId> getTransportKeys() { - return transportKeys; - } - - abstract static class Common { - final boolean alice; - @Nullable - final MessageId lastMessageId; - @Nullable - final byte[] ephemeralPublicKey; - - final long acceptTimestamp; - @Nullable - final byte[] macKey; - - private Common(boolean alice, @Nullable MessageId lastMessageId, - @Nullable byte[] ephemeralPublicKey, @Nullable - long acceptTimestamp, @Nullable byte[] macKey) { - this.alice = alice; - this.lastMessageId = lastMessageId; - this.ephemeralPublicKey = ephemeralPublicKey; - this.acceptTimestamp = acceptTimestamp; - this.macKey = macKey; - } - } - - static class Local extends Common { - final long lastMessageTimestamp; - @Nullable - final byte[] ephemeralPrivateKey; - - Local(boolean alice, @Nullable MessageId lastMessageId, - long lastMessageTimestamp, @Nullable byte[] ephemeralPublicKey, - @Nullable byte[] ephemeralPrivateKey, long acceptTimestamp, - @Nullable byte[] macKey) { - super(alice, lastMessageId, ephemeralPublicKey, acceptTimestamp, - macKey); - this.lastMessageTimestamp = lastMessageTimestamp; - this.ephemeralPrivateKey = ephemeralPrivateKey; - } - - private Local(Local s, @Nullable MessageId lastMessageId, - long lastMessageTimestamp) { - this(s.alice, lastMessageId, lastMessageTimestamp, - s.ephemeralPublicKey, s.ephemeralPrivateKey, - s.acceptTimestamp, s.macKey); - } - } - - static class Remote extends Common { - final Author author; - @Nullable - final Map<TransportId, TransportProperties> transportProperties; - - Remote(boolean alice, Author author, - @Nullable MessageId lastMessageId, - @Nullable byte[] ephemeralPublicKey, @Nullable - Map<TransportId, TransportProperties> transportProperties, - long acceptTimestamp, @Nullable byte[] macKey) { - super(alice, lastMessageId, ephemeralPublicKey, acceptTimestamp, - macKey); - this.author = author; - this.transportProperties = transportProperties; - } - - private Remote(Remote s, @Nullable MessageId lastMessageId) { - this(s.alice, s.author, lastMessageId, s.ephemeralPublicKey, - s.transportProperties, s.acceptTimestamp, s.macKey); - } - - } +abstract class IntroduceeSession extends Session<IntroduceeState> + implements PeerSession { + + final GroupId contactGroupId; + final Author introducer; + final Local local; + final Remote remote; + @Nullable + final byte[] masterKey; + @Nullable + final Map<TransportId, KeySetId> transportKeys; + + IntroduceeSession(SessionId sessionId, IntroduceeState state, + long requestTimestamp, GroupId contactGroupId, Author introducer, + Local local, Remote remote, @Nullable byte[] masterKey, + @Nullable Map<TransportId, KeySetId> transportKeys, + long sessionCounter) { + super(sessionId, state, requestTimestamp, sessionCounter); + this.contactGroupId = contactGroupId; + this.introducer = introducer; + this.local = local; + this.remote = remote; + this.masterKey = masterKey; + this.transportKeys = transportKeys; + } + + @Override + public GroupId getContactGroupId() { + return contactGroupId; + } + + @Override + public long getLocalTimestamp() { + return local.lastMessageTimestamp; + } + + @Nullable + @Override + public MessageId getLastLocalMessageId() { + return local.lastMessageId; + } + + @Nullable + @Override + public MessageId getLastRemoteMessageId() { + return remote.lastMessageId; + } + + Author getIntroducer() { + return introducer; + } + + public Local getLocal() { + return local; + } + + public Remote getRemote() { + return remote; + } + + @Nullable + byte[] getMasterKey() { + return masterKey; + } + + @Nullable + Map<TransportId, KeySetId> getTransportKeys() { + return transportKeys; + } + + abstract static class Common { + final boolean alice; + @Nullable + final MessageId lastMessageId; + @Nullable + final byte[] ephemeralPublicKey; + + final long acceptTimestamp; + @Nullable + final byte[] macKey; + + private Common(boolean alice, @Nullable MessageId lastMessageId, + @Nullable byte[] ephemeralPublicKey, @Nullable + long acceptTimestamp, @Nullable byte[] macKey) { + this.alice = alice; + this.lastMessageId = lastMessageId; + this.ephemeralPublicKey = ephemeralPublicKey; + this.acceptTimestamp = acceptTimestamp; + this.macKey = macKey; + } + } + + static class Local extends Common { + final long lastMessageTimestamp; + @Nullable + final byte[] ephemeralPrivateKey; + + Local(boolean alice, @Nullable MessageId lastMessageId, + long lastMessageTimestamp, @Nullable byte[] ephemeralPublicKey, + @Nullable byte[] ephemeralPrivateKey, long acceptTimestamp, + @Nullable byte[] macKey) { + super(alice, lastMessageId, ephemeralPublicKey, acceptTimestamp, + macKey); + this.lastMessageTimestamp = lastMessageTimestamp; + this.ephemeralPrivateKey = ephemeralPrivateKey; + } + + Local(Local s, @Nullable MessageId lastMessageId, + long lastMessageTimestamp) { + this(s.alice, lastMessageId, lastMessageTimestamp, + s.ephemeralPublicKey, s.ephemeralPrivateKey, + s.acceptTimestamp, s.macKey); + } + } + + static class Remote extends Common { + final Author author; + @Nullable + final Map<TransportId, TransportProperties> transportProperties; + + Remote(boolean alice, Author author, + @Nullable MessageId lastMessageId, + @Nullable byte[] ephemeralPublicKey, @Nullable + Map<TransportId, TransportProperties> transportProperties, + long acceptTimestamp, @Nullable byte[] macKey) { + super(alice, lastMessageId, ephemeralPublicKey, acceptTimestamp, + macKey); + this.author = author; + this.transportProperties = transportProperties; + } + + Remote(Remote s, @Nullable MessageId lastMessageId) { + this(s.alice, s.author, lastMessageId, s.ephemeralPublicKey, + s.transportProperties, s.acceptTimestamp, s.macKey); + } + + } } diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroductionConstants.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroductionConstants.java index 940295983..fa6bc0624 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroductionConstants.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroductionConstants.java @@ -15,6 +15,7 @@ interface IntroductionConstants { // Session Keys String SESSION_KEY_SESSION_ID = "sessionId"; + String SESSION_KEY_COUNTER = "counter"; String SESSION_KEY_ROLE = "role"; String SESSION_KEY_STATE = "state"; String SESSION_KEY_REQUEST_TIMESTAMP = "requestTimestamp"; diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionCrypto.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionCrypto.java new file mode 100644 index 000000000..e4eb75246 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionCrypto.java @@ -0,0 +1,101 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.crypto.KeyPair; +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.identity.AuthorId; +import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.briar.api.client.SessionId; + +import java.security.GeneralSecurityException; + +interface MailboxIntroductionCrypto { + + /** + * Returns the {@link SessionId} based on the introducer + * and the two introducees. + */ + SessionId getSessionId(Author introducer, Author local, Author remote); + + /** + * Returns true if the local author is alice + * <p> + * Alice is the Author whose unique ID has the lower ID, + * comparing the IDs as byte strings. + */ + boolean isAlice(AuthorId local, AuthorId remote); + + /** + * Generates an agreement key pair. + */ + KeyPair generateKeyPair(); + + /** + * Derives a session master key for Alice or Bob. + * + * @return The secret master key + */ + SecretKey deriveMasterKey(IntroduceeSession s) + throws GeneralSecurityException; + + /** + * Derives a MAC key from the session's master key for Alice or Bob. + * + * @param masterKey The key returned by {@link #deriveMasterKey(IntroduceeSession)} + * @param alice true for Alice's MAC key, false for Bob's + * @return The MAC key + */ + SecretKey deriveMacKey(SecretKey masterKey, boolean alice); + + /** + * Generates a MAC that covers both introducee's ephemeral public keys, + * transport properties, Author IDs and timestamps of the accept message. + */ + byte[] authMac(SecretKey macKey, IntroduceeSession s, + AuthorId localAuthorId); + + /** + * Verifies a received MAC + * + * @param mac The MAC to verify + * as returned by {@link #deriveMasterKey(IntroduceeSession)} + * @throws GeneralSecurityException if the verification fails + */ + void verifyAuthMac(byte[] mac, IntroduceeSession s, + AuthorId localAuthorId) + throws GeneralSecurityException; + + /** + * Signs a nonce derived from the macKey + * with the local introducee's identity private key. + * + * @param macKey The corresponding MAC key for the signer's role + * @param privateKey The identity private key + * (from {@link LocalAuthor#getPrivateKey()}) + * @return The signature as a byte array + */ + byte[] sign(SecretKey macKey, byte[] privateKey) + throws GeneralSecurityException; + + /** + * Verifies the signature on a nonce derived from the MAC key. + * + * @throws GeneralSecurityException if the signature is invalid + */ + void verifySignature(byte[] signature, IntroduceeSession s) + throws GeneralSecurityException; + + /** + * Generates a MAC using the local MAC key. + */ + byte[] activateMac(IntroduceeSession s); + + /** + * Verifies a MAC from an ACTIVATE message. + * + * @throws GeneralSecurityException if the verification fails + */ + void verifyActivateMac(byte[] mac, IntroduceeSession s) + throws GeneralSecurityException; + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionCryptoImpl.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionCryptoImpl.java new file mode 100644 index 000000000..8e117c980 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionCryptoImpl.java @@ -0,0 +1,237 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.client.ClientHelper; +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.KeyPair; +import org.briarproject.bramble.api.crypto.KeyParser; +import org.briarproject.bramble.api.crypto.PrivateKey; +import org.briarproject.bramble.api.crypto.PublicKey; +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.data.BdfList; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.identity.AuthorId; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.briar.api.client.SessionId; + +import java.security.GeneralSecurityException; + +import javax.annotation.concurrent.Immutable; +import javax.inject.Inject; + +import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_ACTIVATE_MAC; +import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_ALICE_MAC_KEY; +import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_AUTH_MAC; +import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_AUTH_NONCE; +import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_AUTH_SIGN; +import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_BOB_MAC_KEY; +import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_MASTER_KEY; +import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_SESSION_ID; +import static org.briarproject.briar.api.introduction.IntroductionManager.MAJOR_VERSION; +import static org.briarproject.briar.mailbox.IntroduceeSession.Common; +import static org.briarproject.briar.mailbox.IntroduceeSession.Local; +import static org.briarproject.briar.mailbox.IntroduceeSession.Remote; + +@Immutable +@NotNullByDefault +class MailboxIntroductionCryptoImpl implements MailboxIntroductionCrypto { + + private final CryptoComponent crypto; + private final ClientHelper clientHelper; + + @Inject + MailboxIntroductionCryptoImpl( + CryptoComponent crypto, + ClientHelper clientHelper) { + this.crypto = crypto; + this.clientHelper = clientHelper; + } + + @Override + public SessionId getSessionId(Author introducer, Author local, + Author remote) { + boolean isAlice = isAlice(local.getId(), remote.getId()); + byte[] hash = crypto.hash( + LABEL_SESSION_ID, + introducer.getId().getBytes(), + isAlice ? local.getId().getBytes() : remote.getId().getBytes(), + isAlice ? remote.getId().getBytes() : local.getId().getBytes() + ); + return new SessionId(hash); + } + + @Override + public KeyPair generateKeyPair() { + return crypto.generateAgreementKeyPair(); + } + + @Override + public boolean isAlice(AuthorId local, AuthorId remote) { + return local.compareTo(remote) < 0; + } + + @Override + @SuppressWarnings("ConstantConditions") + public SecretKey deriveMasterKey(IntroduceeSession s) + throws GeneralSecurityException { + return deriveMasterKey( + s.getLocal().ephemeralPublicKey, + s.getLocal().ephemeralPrivateKey, + s.getRemote().ephemeralPublicKey, + s.getLocal().alice + ); + } + + SecretKey deriveMasterKey(byte[] publicKey, byte[] privateKey, + byte[] remotePublicKey, boolean alice) + throws GeneralSecurityException { + KeyParser kp = crypto.getAgreementKeyParser(); + PublicKey remoteEphemeralPublicKey = kp.parsePublicKey(remotePublicKey); + PublicKey ephemeralPublicKey = kp.parsePublicKey(publicKey); + PrivateKey ephemeralPrivateKey = kp.parsePrivateKey(privateKey); + KeyPair keyPair = new KeyPair(ephemeralPublicKey, ephemeralPrivateKey); + return crypto.deriveSharedSecret( + LABEL_MASTER_KEY, + remoteEphemeralPublicKey, + keyPair, + new byte[] {MAJOR_VERSION}, + alice ? publicKey : remotePublicKey, + alice ? remotePublicKey : publicKey + ); + } + + @Override + public SecretKey deriveMacKey(SecretKey masterKey, boolean alice) { + return crypto.deriveKey( + alice ? LABEL_ALICE_MAC_KEY : LABEL_BOB_MAC_KEY, + masterKey + ); + } + + @Override + @SuppressWarnings("ConstantConditions") + public byte[] authMac(SecretKey macKey, IntroduceeSession s, + AuthorId localAuthorId) { + // the macKey is not yet available in the session at this point + return authMac(macKey, s.getIntroducer().getId(), localAuthorId, + s.getLocal(), s.getRemote()); + } + + byte[] authMac(SecretKey macKey, AuthorId introducerId, + AuthorId localAuthorId, Local local, Remote remote) { + byte[] inputs = getAuthMacInputs(introducerId, localAuthorId, local, + remote.author.getId(), remote); + return crypto.mac( + LABEL_AUTH_MAC, + macKey, + inputs + ); + } + + @Override + @SuppressWarnings("ConstantConditions") + public void verifyAuthMac(byte[] mac, IntroduceeSession s, + AuthorId localAuthorId) throws GeneralSecurityException { + verifyAuthMac(mac, new SecretKey(s.getRemote().macKey), + s.getIntroducer().getId(), localAuthorId, s.getLocal(), + s.getRemote().author.getId(), s.getRemote()); + } + + void verifyAuthMac(byte[] mac, SecretKey macKey, AuthorId introducerId, + AuthorId localAuthorId, Common local, AuthorId remoteAuthorId, + Common remote) throws GeneralSecurityException { + // switch input for verification + byte[] inputs = getAuthMacInputs(introducerId, remoteAuthorId, remote, + localAuthorId, local); + if (!crypto.verifyMac(mac, LABEL_AUTH_MAC, macKey, inputs)) { + throw new GeneralSecurityException(); + } + } + + @SuppressWarnings("ConstantConditions") + private byte[] getAuthMacInputs(AuthorId introducerId, + AuthorId localAuthorId, Common local, AuthorId remoteAuthorId, + Common remote) { + BdfList localInfo = BdfList.of( + localAuthorId, + local.acceptTimestamp, + local.ephemeralPublicKey + ); + BdfList remoteInfo = BdfList.of( + remoteAuthorId, + remote.acceptTimestamp, + remote.ephemeralPublicKey + ); + BdfList macList = BdfList.of( + introducerId, + localInfo, + remoteInfo + ); + try { + return clientHelper.toByteArray(macList); + } catch (FormatException e) { + throw new AssertionError(); + } + } + + @Override + public byte[] sign(SecretKey macKey, byte[] privateKey) + throws GeneralSecurityException { + return crypto.sign( + LABEL_AUTH_SIGN, + getNonce(macKey), + privateKey + ); + } + + @Override + @SuppressWarnings("ConstantConditions") + public void verifySignature(byte[] signature, IntroduceeSession s) + throws GeneralSecurityException { + SecretKey macKey = new SecretKey(s.getRemote().macKey); + verifySignature(macKey, s.getRemote().author.getPublicKey(), signature); + } + + void verifySignature(SecretKey macKey, byte[] publicKey, + byte[] signature) throws GeneralSecurityException { + byte[] nonce = getNonce(macKey); + if (!crypto.verifySignature(signature, LABEL_AUTH_SIGN, nonce, + publicKey)) { + throw new GeneralSecurityException(); + } + } + + private byte[] getNonce(SecretKey macKey) { + return crypto.mac(LABEL_AUTH_NONCE, macKey); + } + + @Override + public byte[] activateMac(IntroduceeSession s) { + if (s.getLocal().macKey == null) + throw new AssertionError("Local MAC key is null"); + return activateMac(new SecretKey(s.getLocal().macKey)); + } + + byte[] activateMac(SecretKey macKey) { + return crypto.mac( + LABEL_ACTIVATE_MAC, + macKey + ); + } + + @Override + public void verifyActivateMac(byte[] mac, IntroduceeSession s) + throws GeneralSecurityException { + if (s.getRemote().macKey == null) + throw new AssertionError("Remote MAC key is null"); + verifyActivateMac(mac, new SecretKey(s.getRemote().macKey)); + } + + void verifyActivateMac(byte[] mac, SecretKey macKey) + throws GeneralSecurityException { + if (!crypto.verifyMac(mac, LABEL_ACTIVATE_MAC, macKey)) { + throw new GeneralSecurityException(); + } + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionManagerImpl.java index d68950aa4..69258f5ad 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionManagerImpl.java @@ -2,26 +2,39 @@ package org.briarproject.briar.mailbox; import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.client.ClientHelper; +import org.briarproject.bramble.api.client.ContactGroupFactory; import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.PrivateMailbox; import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.MetadataParser; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Transaction; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.sync.Client; import org.briarproject.bramble.api.sync.Group; +import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.Message; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.bramble.api.versioning.ClientVersioningManager; +import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.mailbox.MailboxIntroductionManager; import org.briarproject.briar.client.BdfIncomingMessageHook; +import java.util.Map; import java.util.logging.Logger; +import javax.annotation.Nullable; import javax.inject.Inject; import static org.briarproject.bramble.api.contact.ContactManager.ContactHook; import static org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook; +import static org.briarproject.briar.api.mailbox.Role.MAILBOX; +import static org.briarproject.briar.api.mailbox.Role.OWNER; class MailboxIntroductionManagerImpl extends BdfIncomingMessageHook implements MailboxIntroductionManager, Client, ClientVersioningHook, @@ -29,13 +42,48 @@ class MailboxIntroductionManagerImpl extends BdfIncomingMessageHook private static final Logger LOG = Logger.getLogger(MailboxIntroductionManagerImpl.class.getName()); + private final ClientVersioningManager clientVersioningManager; + private final ContactGroupFactory contactGroupFactory; + private final ContactManager contactManager; + private final MessageParser messageParser; + // private final SessionEncoder sessionEncoder; + private final SessionParser sessionParser; + private final OwnerProtocolEngine ownerProtocolEngine; + private final SessionEncoder sessionEncoder; + private final MailboxIntroductionCrypto crypto; + //private final MailboxProtocolEngine mailboxProtocolEngine; + private final IdentityManager identityManager; + + private final Group localGroup; @Inject MailboxIntroductionManagerImpl( DatabaseComponent db, ClientHelper clientHelper, - MetadataParser metadataParser) { + ClientVersioningManager clientVersioningManager, + MetadataParser metadataParser, + ContactGroupFactory contactGroupFactory, + ContactManager contactManager, + MessageParser messageParser, + SessionParser sessionParser, + OwnerProtocolEngine ownerProtocolEngine, + SessionEncoder sessionEncoder, + MailboxIntroductionCrypto crypto, + // MailboxProtocolEngine mailboxProtocolEngine, + IdentityManager identityManager) { super(db, clientHelper, metadataParser); + this.clientVersioningManager = clientVersioningManager; + this.contactGroupFactory = contactGroupFactory; + this.contactManager = contactManager; + this.messageParser = messageParser; + this.sessionParser = sessionParser; + this.ownerProtocolEngine = ownerProtocolEngine; + this.sessionEncoder = sessionEncoder; + this.crypto = crypto; +// this.mailboxProtocolEngine = mailboxProtocolEngine; + this.identityManager = identityManager; + this.localGroup = + contactGroupFactory.createLocalGroup(CLIENT_ID, MAJOR_VERSION); } @Override @@ -72,6 +120,93 @@ class MailboxIntroductionManagerImpl extends BdfIncomingMessageHook LOG.info("Private mailbox added"); } + @Nullable + private StoredSession getSession(Transaction txn, + @Nullable SessionId sessionId) throws DbException, FormatException { + if (sessionId == null) return null; + BdfDictionary query = sessionParser.getSessionQuery(sessionId); + Map<MessageId, BdfDictionary> results = clientHelper + .getMessageMetadataAsDictionary(txn, localGroup.getId(), query); + if (results.size() > 1) throw new DbException(); + if (results.isEmpty()) return null; + return new StoredSession(results.keySet().iterator().next(), + results.values().iterator().next()); + } + + + @Override + public void makeIntroduction(PrivateMailbox privateMailbox, Contact contact, + long timestamp) throws DbException { + Transaction txn = db.startTransaction(false); + try { + // Look up the session, if there is one + Author introducer = identityManager.getLocalAuthor(txn); + SessionId sessionId = + crypto.getSessionId(introducer, privateMailbox.getAuthor(), + contact.getAuthor()); + StoredSession ss = getSession(txn, sessionId); + // Create or parse the session + OwnerSession session; + MessageId storageId; + if (ss == null) { + // This is the first request - create a new session + GroupId groupId1 = getContactGroup(privateMailbox).getId(); + GroupId groupId2 = getContactGroup(contact).getId(); + // use fixed deterministic roles for the introducees + session = new OwnerSession(sessionId, groupId1, + privateMailbox.getAuthor(), groupId2, + contact.getAuthor(), 0); + storageId = createStorageId(txn); + } else { + // An earlier request exists, so we already have a session + session = sessionParser.parseOwnerSession(ss.bdfSession); + storageId = ss.storageId; + } + // Handle the request action + session = ownerProtocolEngine + .onRequestAction(txn, session, timestamp); + // Store the updated session + storeSession(txn, storageId, session); + db.commitTransaction(txn); + } catch (FormatException e) { + throw new DbException(e); + } finally { + db.endTransaction(txn); + } + } + + private void storeSession(Transaction txn, MessageId storageId, + Session session) throws DbException { + BdfDictionary d; + if (session.getRole() == OWNER) { + d = sessionEncoder + .encodeIntroducerSession((OwnerSession) session); + } else if (session.getRole() == MAILBOX) { + d = sessionEncoder + .encodeIntroduceeSession((IntroduceeSession) session); + } else { + throw new AssertionError(); + } + try { + clientHelper.mergeMessageMetadata(txn, storageId, d); + } catch (FormatException e) { + throw new AssertionError(); + } + } + + @Override + public Group getContactGroup(Contact c) { + return contactGroupFactory + .createContactGroup(CLIENT_ID, MAJOR_VERSION, c); + } + + private MessageId createStorageId(Transaction txn) throws DbException { + Message m = clientHelper + .createMessageForStoringMetadata(localGroup.getId()); + db.addLocalMessage(txn, m, new Metadata(), false); + return m.getId(); + } + @Override public void removingContact(Transaction txn, Contact c) throws DbException { LOG.info("contact removed"); @@ -88,4 +223,14 @@ class MailboxIntroductionManagerImpl extends BdfIncomingMessageHook } + private static class StoredSession { + + private final MessageId storageId; + private final BdfDictionary bdfSession; + + private StoredSession(MessageId storageId, BdfDictionary bdfSession) { + this.storageId = storageId; + this.bdfSession = bdfSession; + } + } } diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionModule.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionModule.java index 967b3d6e8..14420f789 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionModule.java @@ -1,9 +1,13 @@ package org.briarproject.briar.mailbox; +import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.contact.ContactManager; +import org.briarproject.bramble.api.data.MetadataEncoder; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.sync.ValidationManager; +import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.versioning.ClientVersioningManager; +import org.briarproject.briar.api.introduction.IntroductionManager; import org.briarproject.briar.api.mailbox.MailboxIntroductionManager; import javax.inject.Inject; @@ -20,10 +24,28 @@ import static org.briarproject.briar.api.mailbox.MailboxIntroductionManager.MINO public class MailboxIntroductionModule { public static class EagerSingletons { + @Inject + MailboxIntroductionValidator mailboxIntroductionValidator; @Inject MailboxIntroductionManager mailboxIntroductionManager; } + @Provides + @Singleton + MailboxIntroductionValidator provideValidator( + ValidationManager validationManager, + MessageEncoder messageEncoder, MetadataEncoder metadataEncoder, + ClientHelper clientHelper, Clock clock) { + MailboxIntroductionValidator mailboxIntroductionValidator = + new MailboxIntroductionValidator(messageEncoder, clientHelper, + metadataEncoder, clock); + validationManager + .registerMessageValidator(IntroductionManager.CLIENT_ID, + IntroductionManager.MAJOR_VERSION, + mailboxIntroductionValidator); + return mailboxIntroductionValidator; + } + @Provides @Singleton MailboxIntroductionManager provideMailboxIntroductionManager( @@ -39,4 +61,30 @@ public class MailboxIntroductionModule { MINOR_VERSION, mailboxIntroductionManager); return mailboxIntroductionManager; } + + @Provides + MessageParser provideMessageParser(MessageParserImpl messageParser) { + return messageParser; + } + + @Provides + MessageEncoder provideMessageEncoder(MessageEncoderImpl messageEncoder) { + return messageEncoder; + } + + @Provides + SessionParser provideSessionParser(SessionParserImpl sessionParser) { + return sessionParser; + } + + @Provides + SessionEncoder provideSessionEncoder(SessionEncoderImpl sessionEncoder) { + return sessionEncoder; + } + + @Provides + MailboxIntroductionCrypto provideMailboxIntroductionCrypto( + MailboxIntroductionCryptoImpl mailboxIntroductionCrypto) { + return mailboxIntroductionCrypto; + } } diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionValidator.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionValidator.java new file mode 100644 index 000000000..46cf6776f --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionValidator.java @@ -0,0 +1,172 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.UniqueId; +import org.briarproject.bramble.api.client.BdfMessageContext; +import org.briarproject.bramble.api.client.BdfMessageValidator; +import org.briarproject.bramble.api.client.ClientHelper; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.data.BdfList; +import org.briarproject.bramble.api.data.MetadataEncoder; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.Group; +import org.briarproject.bramble.api.sync.Message; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.briar.api.client.SessionId; + +import java.util.Collections; + +import javax.annotation.concurrent.Immutable; + +import static org.briarproject.bramble.api.crypto.CryptoConstants.MAC_BYTES; +import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_BYTES; +import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; +import static org.briarproject.bramble.util.ValidationUtils.checkLength; +import static org.briarproject.bramble.util.ValidationUtils.checkSize; +import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH; +import static org.briarproject.briar.mailbox.MessageType.MAILBOX_ACCEPT; +import static org.briarproject.briar.mailbox.MessageType.MAILBOX_AUTH; + + +@Immutable +@NotNullByDefault +class MailboxIntroductionValidator extends BdfMessageValidator { + + private final MessageEncoder messageEncoder; + + MailboxIntroductionValidator( + MessageEncoder messageEncoder, + ClientHelper clientHelper, MetadataEncoder metadataEncoder, + Clock clock) { + super(clientHelper, metadataEncoder, clock); + this.messageEncoder = messageEncoder; + } + + @Override + protected BdfMessageContext validateMessage(Message m, Group g, + BdfList body) throws FormatException { + MessageType type = MessageType.fromValue(body.getLong(0).intValue()); + + switch (type) { + case MAILBOX_REQUEST: + return validateRequestMessage(m, body); + case MAILBOX_ACCEPT: + return validateAcceptMessage(m, body); + case MAILBOX_AUTH: + return validateAuthMessage(m, body); + case DECLINE: + case ABORT: + return validateOtherMessage(type, m, body); + default: + throw new FormatException(); + } + } + + private BdfMessageContext validateRequestMessage(Message m, BdfList body) + throws FormatException { + checkSize(body, 4); + + byte[] previousMessageId = body.getOptionalRaw(1); + checkLength(previousMessageId, UniqueId.LENGTH); + + BdfList authorList = body.getList(2); + clientHelper.parseAndValidateAuthor(authorList); + + String msg = body.getOptionalString(3); + checkLength(msg, 1, MAX_REQUEST_MESSAGE_LENGTH); + + BdfDictionary meta = + messageEncoder.encodeRequestMetadata(m.getTimestamp()); + if (previousMessageId == null) { + return new BdfMessageContext(meta); + } else { + MessageId dependency = new MessageId(previousMessageId); + return new BdfMessageContext(meta, + Collections.singletonList(dependency)); + } + } + + private BdfMessageContext validateAcceptMessage(Message m, BdfList body) + throws FormatException { + checkSize(body, 6); + + byte[] sessionIdBytes = body.getRaw(1); + checkLength(sessionIdBytes, UniqueId.LENGTH); + + byte[] previousMessageId = body.getOptionalRaw(2); + checkLength(previousMessageId, UniqueId.LENGTH); + + byte[] ephemeralPublicKey = body.getRaw(3); + checkLength(ephemeralPublicKey, 0, MAX_PUBLIC_KEY_LENGTH); + + long timestamp = body.getLong(4); + if (timestamp < 0) throw new FormatException(); + + BdfDictionary transportProperties = body.getDictionary(5); + if (transportProperties.size() < 1) throw new FormatException(); + clientHelper + .parseAndValidateTransportPropertiesMap(transportProperties); + + SessionId sessionId = new SessionId(sessionIdBytes); + BdfDictionary meta = messageEncoder + .encodeMetadata(MAILBOX_ACCEPT, sessionId, m.getTimestamp(), + false); + if (previousMessageId == null) { + return new BdfMessageContext(meta); + } else { + MessageId dependency = new MessageId(previousMessageId); + return new BdfMessageContext(meta, + Collections.singletonList(dependency)); + } + } + + private BdfMessageContext validateAuthMessage(Message m, BdfList body) + throws FormatException { + checkSize(body, 5); + + byte[] sessionIdBytes = body.getRaw(1); + checkLength(sessionIdBytes, UniqueId.LENGTH); + + byte[] previousMessageId = body.getRaw(2); + checkLength(previousMessageId, UniqueId.LENGTH); + + byte[] mac = body.getRaw(3); + checkLength(mac, MAC_BYTES); + + byte[] signature = body.getRaw(4); + checkLength(signature, 1, MAX_SIGNATURE_BYTES); + + SessionId sessionId = new SessionId(sessionIdBytes); + BdfDictionary meta = messageEncoder + .encodeMetadata(MAILBOX_AUTH, sessionId, m.getTimestamp(), + false); + MessageId dependency = new MessageId(previousMessageId); + return new BdfMessageContext(meta, + Collections.singletonList(dependency)); + } + + private BdfMessageContext validateOtherMessage( + MessageType type, + Message m, BdfList body) throws FormatException { + checkSize(body, 3); + + byte[] sessionIdBytes = body.getRaw(1); + checkLength(sessionIdBytes, UniqueId.LENGTH); + + byte[] previousMessageId = body.getOptionalRaw(2); + checkLength(previousMessageId, UniqueId.LENGTH); + + SessionId sessionId = new SessionId(sessionIdBytes); + BdfDictionary meta = messageEncoder + .encodeMetadata(type, sessionId, m.getTimestamp(), false); + if (previousMessageId == null) { + return new BdfMessageContext(meta); + } else { + MessageId dependency = new MessageId(previousMessageId); + return new BdfMessageContext(meta, + Collections.singletonList(dependency)); + } + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxProtocolEngine.java deleted file mode 100644 index 511250dbe..000000000 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxProtocolEngine.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.briarproject.briar.mailbox; - -class MailboxProtocolEngine extends AbstractProtocolEngine { -} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxSession.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxSession.java new file mode 100644 index 000000000..edf1f0957 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxSession.java @@ -0,0 +1,161 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.Message; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.bramble.api.transport.KeySetId; +import org.briarproject.briar.api.client.SessionId; +import org.briarproject.briar.api.mailbox.Role; + +import java.util.Map; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +import static org.briarproject.briar.mailbox.IntroduceeState.AWAIT_REMOTE_RESPONSE; +import static org.briarproject.briar.mailbox.IntroduceeState.START; + +@Immutable +@NotNullByDefault +class MailboxSession extends IntroduceeSession { + + + MailboxSession(SessionId sessionId, IntroduceeState state, + long requestTimestamp, GroupId contactGroupId, Author introducer, + Local local, Remote remote, @Nullable byte[] masterKey, + @Nullable Map<TransportId, KeySetId> transportKeys, + long sessionCounter) { + super(sessionId, state, requestTimestamp, contactGroupId, introducer, + local, remote, masterKey, transportKeys, sessionCounter); + } + + static MailboxSession getInitial(GroupId contactGroupId, + SessionId sessionId, Author introducer, boolean localIsAlice, + Author remoteAuthor) { + Local local = + new Local(localIsAlice, null, -1, null, null, -1, null); + Remote remote = + new Remote(!localIsAlice, remoteAuthor, null, null, null, -1, + null); + return new MailboxSession(sessionId, START, -1, contactGroupId, + introducer, local, remote, null, null, 0); + } + + static MailboxSession addRemoteRequest( + MailboxSession s, + IntroduceeState state, + ContactRequestMessage m, byte[] ephemeralPublicKey, + byte[] ephemeralPrivateKey, + long acceptTimestamp, boolean alice) { + Local local = new Local(alice, m.getMessageId(), + m.getTimestamp(), ephemeralPublicKey, ephemeralPrivateKey, + acceptTimestamp, null); + Remote remote = + new Remote(!alice, s.remote.author, m.getMessageId(), + m.getEphemeralPublicKey(), null, m.getAcceptTimestamp(), + null); + return new MailboxSession(s.getSessionId(), state, m.getTimestamp(), + s.contactGroupId, s.introducer, local, remote, s.masterKey, + s.transportKeys, s.getSessionCounter()); + } + + static MailboxSession addLocalAuth( + MailboxSession s, + IntroduceeState state, Message m, SecretKey masterKey, + SecretKey aliceMacKey, SecretKey bobMacKey) { + // add mac key and sent message + Local local = new Local(s.local.alice, m.getId(), m.getTimestamp(), + s.local.ephemeralPublicKey, s.local.ephemeralPrivateKey, + s.local.acceptTimestamp, + s.local.alice ? aliceMacKey.getBytes() : bobMacKey.getBytes()); + // just add the mac key + Remote remote = new Remote(s.remote.alice, s.remote.author, + s.remote.lastMessageId, s.remote.ephemeralPublicKey, + s.remote.transportProperties, s.remote.acceptTimestamp, + s.remote.alice ? aliceMacKey.getBytes() : bobMacKey.getBytes()); + // add master key + return new MailboxSession(s.getSessionId(), state, + s.getRequestTimestamp(), s.contactGroupId, s.introducer, local, + remote, masterKey.getBytes(), s.transportKeys, + s.getSessionCounter()); + } + + static MailboxSession awaitAuth( + MailboxSession s, MailboxAuthMessage m, + Message sent, @Nullable Map<TransportId, KeySetId> transportKeys) { + Local local = new Local(s.local, sent.getId(), sent.getTimestamp()); + Remote remote = new Remote(s.remote, m.getMessageId()); + return new MailboxSession(s.getSessionId(), AWAIT_REMOTE_RESPONSE, + s.getRequestTimestamp(), s.contactGroupId, s.introducer, local, + remote, null, transportKeys, s.getSessionCounter()); + } + + static MailboxSession clear( + MailboxSession s, IntroduceeState state, + @Nullable MessageId lastLocalMessageId, long localTimestamp, + @Nullable MessageId lastRemoteMessageId) { + Local local = + new Local(s.local.alice, lastLocalMessageId, localTimestamp, + null, null, -1, null); + Remote remote = + new Remote(s.remote.alice, s.remote.author, lastRemoteMessageId, + null, null, -1, null); + return new MailboxSession(s.getSessionId(), state, + s.getRequestTimestamp(), s.contactGroupId, s.introducer, local, + remote, null, null, s.getSessionCounter()); + } + + @Override + Role getRole() { + return Role.MAILBOX; + } + + @Override + public GroupId getContactGroupId() { + return contactGroupId; + } + + @Override + public long getLocalTimestamp() { + return local.lastMessageTimestamp; + } + + @Nullable + @Override + public MessageId getLastLocalMessageId() { + return local.lastMessageId; + } + + @Nullable + @Override + public MessageId getLastRemoteMessageId() { + return remote.lastMessageId; + } + + Author getIntroducer() { + return introducer; + } + + public Local getLocal() { + return local; + } + + public Remote getRemote() { + return remote; + } + + @Nullable + byte[] getMasterKey() { + return masterKey; + } + + @Nullable + Map<TransportId, KeySetId> getTransportKeys() { + return transportKeys; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageEncoder.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageEncoder.java index 28ac43793..cc44d68c2 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageEncoder.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageEncoder.java @@ -16,36 +16,29 @@ import javax.annotation.Nullable; @NotNullByDefault interface MessageEncoder { - BdfDictionary encodeRequestMetadata(long timestamp); + BdfDictionary encodeRequestMetadata(long timestamp); - BdfDictionary encodeMetadata(MessageType type, @Nullable SessionId sessionId, long timestamp, boolean local); + BdfDictionary encodeMetadata(MessageType type, + @Nullable SessionId sessionId, long timestamp, boolean local); - void addSessionId(BdfDictionary meta, SessionId sessionId); + void addSessionId(BdfDictionary meta, SessionId sessionId); - void setVisibleInUi(BdfDictionary meta, boolean visible); + void setVisibleInUi(BdfDictionary meta, boolean visible); - void setAvailableToAnswer(BdfDictionary meta, boolean available); + void setAvailableToAnswer(BdfDictionary meta, boolean available); - Message encodeRequestMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId); + Message encodeRequestMessage(GroupId contactGroupId, long timestamp, + @Nullable MessageId previousMessageId); - Message encodeAcceptMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId, SessionId sessionId, - byte[] ephemeralPublicKey, long acceptTimestamp, - Map<TransportId, TransportProperties> transportProperties); + Message encodeMailboxAcceptMessage(GroupId contactGroupId, long timestamp, + @Nullable MessageId previousMessageId, SessionId sessionId, + byte[] ephemeralPublicKey, long acceptTimestamp, + Map<TransportId, TransportProperties> transportProperties); - Message encodeDeclineMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId, SessionId sessionId); + Message encodeDeclineMessage(GroupId contactGroupId, long timestamp, + @Nullable MessageId previousMessageId, SessionId sessionId); - Message encodeAuthMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId, SessionId sessionId, - byte[] mac, byte[] signature); - - Message encodeActivateMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId, SessionId sessionId, - byte[] mac); - - Message encodeAbortMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId, SessionId sessionId); + Message encodeAbortMessage(GroupId contactGroupId, long timestamp, + @Nullable MessageId previousMessageId, SessionId sessionId); } diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageEncoderImpl.java index e71a481c8..dfb5dfd3c 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageEncoderImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageEncoderImpl.java @@ -24,146 +24,122 @@ import static org.briarproject.briar.mailbox.IntroductionConstants.MSG_KEY_MESSA import static org.briarproject.briar.mailbox.IntroductionConstants.MSG_KEY_SESSION_ID; import static org.briarproject.briar.mailbox.IntroductionConstants.MSG_KEY_TIMESTAMP; import static org.briarproject.briar.mailbox.IntroductionConstants.MSG_KEY_VISIBLE_IN_UI; +import static org.briarproject.briar.mailbox.MessageType.ABORT; import static org.briarproject.briar.mailbox.MessageType.DECLINE; +import static org.briarproject.briar.mailbox.MessageType.MAILBOX_ACCEPT; import static org.briarproject.briar.mailbox.MessageType.MAILBOX_REQUEST; @NotNullByDefault class MessageEncoderImpl implements - MessageEncoder { - - private final ClientHelper clientHelper; - private final MessageFactory messageFactory; - - @Inject - MessageEncoderImpl(ClientHelper clientHelper, - MessageFactory messageFactory) { - this.clientHelper = clientHelper; - this.messageFactory = messageFactory; - } - - @Override - public BdfDictionary encodeRequestMetadata(long timestamp) { - BdfDictionary meta = - encodeMetadata(MAILBOX_REQUEST, null, timestamp, false); - meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, false); - return meta; - } - - @Override - public BdfDictionary encodeMetadata(MessageType type, - @Nullable SessionId sessionId, long timestamp, boolean local) { - BdfDictionary meta = new BdfDictionary(); - meta.put(MSG_KEY_MESSAGE_TYPE, type.getValue()); - if (sessionId != null) - meta.put(MSG_KEY_SESSION_ID, sessionId); - else if (type != MAILBOX_REQUEST) - throw new IllegalArgumentException(); - meta.put(MSG_KEY_TIMESTAMP, timestamp); - meta.put(MSG_KEY_LOCAL, local); - return meta; - } - - @Override - public void addSessionId(BdfDictionary meta, SessionId sessionId) { - meta.put(MSG_KEY_SESSION_ID, sessionId); - } - - @Override - public void setVisibleInUi(BdfDictionary meta, boolean visible) { - meta.put(MSG_KEY_VISIBLE_IN_UI, visible); - } - - @Override - public void setAvailableToAnswer(BdfDictionary meta, boolean available) { - meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available); - } - - @Override - public Message encodeRequestMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId) { - BdfList body = BdfList.of( - MAILBOX_REQUEST.getValue(), - previousMessageId - ); - return createMessage(contactGroupId, timestamp, body); - } - - @Override - public Message encodeAcceptMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId, SessionId sessionId, - byte[] ephemeralPublicKey, long acceptTimestamp, - Map<TransportId, TransportProperties> transportProperties) { - BdfList body = BdfList.of( - ACCEPT.getValue(), - sessionId, - previousMessageId, - ephemeralPublicKey, - acceptTimestamp, - clientHelper.toDictionary(transportProperties) - ); - return createMessage(contactGroupId, timestamp, body); - } - - @Override - public Message encodeDeclineMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId, SessionId sessionId) { - return encodeMessage(DECLINE, contactGroupId, sessionId, timestamp, - previousMessageId); - } - - @Override - public Message encodeAuthMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId, SessionId sessionId, - byte[] mac, byte[] signature) { - BdfList body = BdfList.of( - AUTH.getValue(), - sessionId, - previousMessageId, - mac, - signature - ); - return createMessage(contactGroupId, timestamp, body); - } - - @Override - public Message encodeActivateMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId, SessionId sessionId, - byte[] mac) { - BdfList body = BdfList.of( - ACTIVATE.getValue(), - sessionId, - previousMessageId, - mac - ); - return createMessage(contactGroupId, timestamp, body); - } - - @Override - public Message encodeAbortMessage(GroupId contactGroupId, long timestamp, - @Nullable MessageId previousMessageId, SessionId sessionId) { - return encodeMessage(ABORT, contactGroupId, sessionId, timestamp, - previousMessageId); - } - - private Message encodeMessage(MessageType type, GroupId contactGroupId, - SessionId sessionId, long timestamp, - @Nullable MessageId previousMessageId) { - BdfList body = BdfList.of( - type.getValue(), - sessionId, - previousMessageId - ); - return createMessage(contactGroupId, timestamp, body); - } - - private Message createMessage(GroupId contactGroupId, long timestamp, - BdfList body) { - try { - return messageFactory.createMessage(contactGroupId, timestamp, - clientHelper.toByteArray(body)); - } catch (FormatException e) { - throw new AssertionError(e); - } - } + MessageEncoder { + + private final ClientHelper clientHelper; + private final MessageFactory messageFactory; + + @Inject + MessageEncoderImpl(ClientHelper clientHelper, + MessageFactory messageFactory) { + this.clientHelper = clientHelper; + this.messageFactory = messageFactory; + } + + @Override + public BdfDictionary encodeRequestMetadata(long timestamp) { + BdfDictionary meta = + encodeMetadata(MAILBOX_REQUEST, null, timestamp, false); + meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, false); + return meta; + } + + @Override + public BdfDictionary encodeMetadata(MessageType type, + @Nullable SessionId sessionId, long timestamp, boolean local) { + BdfDictionary meta = new BdfDictionary(); + meta.put(MSG_KEY_MESSAGE_TYPE, type.getValue()); + if (sessionId != null) + meta.put(MSG_KEY_SESSION_ID, sessionId); + else if (type != MAILBOX_REQUEST) + throw new IllegalArgumentException(); + meta.put(MSG_KEY_TIMESTAMP, timestamp); + meta.put(MSG_KEY_LOCAL, local); + return meta; + } + + @Override + public void addSessionId(BdfDictionary meta, SessionId sessionId) { + meta.put(MSG_KEY_SESSION_ID, sessionId); + } + + @Override + public void setVisibleInUi(BdfDictionary meta, boolean visible) { + meta.put(MSG_KEY_VISIBLE_IN_UI, visible); + } + + @Override + public void setAvailableToAnswer(BdfDictionary meta, boolean available) { + meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available); + } + + @Override + public Message encodeRequestMessage(GroupId contactGroupId, long timestamp, + @Nullable MessageId previousMessageId) { + BdfList body = BdfList.of( + MAILBOX_REQUEST.getValue(), + previousMessageId + ); + return createMessage(contactGroupId, timestamp, body); + } + + @Override + public Message encodeMailboxAcceptMessage(GroupId contactGroupId, + long timestamp, + @Nullable MessageId previousMessageId, SessionId sessionId, + byte[] ephemeralPublicKey, long acceptTimestamp, + Map<TransportId, TransportProperties> transportProperties) { + BdfList body = BdfList.of( + MAILBOX_ACCEPT.getValue(), + sessionId, + previousMessageId, + ephemeralPublicKey, + acceptTimestamp, + clientHelper.toDictionary(transportProperties) + ); + return createMessage(contactGroupId, timestamp, body); + } + + @Override + public Message encodeDeclineMessage(GroupId contactGroupId, long timestamp, + @Nullable MessageId previousMessageId, SessionId sessionId) { + return encodeMessage(DECLINE, contactGroupId, sessionId, timestamp, + previousMessageId); + } + + @Override + public Message encodeAbortMessage(GroupId contactGroupId, long timestamp, + @Nullable MessageId previousMessageId, SessionId sessionId) { + return encodeMessage(ABORT, contactGroupId, sessionId, timestamp, + previousMessageId); + } + + private Message encodeMessage(MessageType type, GroupId contactGroupId, + SessionId sessionId, long timestamp, + @Nullable MessageId previousMessageId) { + BdfList body = BdfList.of( + type.getValue(), + sessionId, + previousMessageId + ); + return createMessage(contactGroupId, timestamp, body); + } + + private Message createMessage(GroupId contactGroupId, long timestamp, + BdfList body) { + try { + return messageFactory.createMessage(contactGroupId, timestamp, + clientHelper.toByteArray(body)); + } catch (FormatException e) { + throw new AssertionError(e); + } + } } diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageParser.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageParser.java index 5b6f729e8..640178c99 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageParser.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageParser.java @@ -6,12 +6,6 @@ import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.Message; import org.briarproject.briar.api.client.SessionId; -import org.briarproject.briar.introduction.AcceptMessage; -import org.briarproject.briar.introduction.ActivateMessage; -import org.briarproject.briar.introduction.AuthMessage; -import org.briarproject.briar.introduction.DeclineMessage; -import org.briarproject.briar.introduction.MessageMetadata; -import org.briarproject.briar.introduction.RequestMessage; @NotNullByDefault interface MessageParser { @@ -20,24 +14,21 @@ interface MessageParser { BdfDictionary getRequestsAvailableToAnswerQuery(SessionId sessionId); - MessageMetadata parseMetadata(BdfDictionary meta) throws FormatException; + //MessageMetadata parseMetadata(BdfDictionary meta) throws FormatException; RequestMessage parseRequestMessage(Message m, BdfList body) throws FormatException; - AcceptMessage parseAcceptMessage(Message m, BdfList body) + MailboxAcceptMessage parseMailboxAcceptMessage(Message m, BdfList body) throws FormatException; DeclineMessage parseDeclineMessage(Message m, BdfList body) throws FormatException; - AuthMessage parseAuthMessage(Message m, BdfList body) + MailboxAuthMessage parseMailboxAuthMessage(Message m, BdfList body) throws FormatException; - ActivateMessage parseActivateMessage(Message m, BdfList body) - throws FormatException; - - org.briarproject.briar.introduction.AbortMessage parseAbortMessage( + AbortMessage parseAbortMessage( Message m, BdfList body) throws FormatException; diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageParserImpl.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageParserImpl.java index 89284e26a..ec0e13baa 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageParserImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageParserImpl.java @@ -5,33 +5,22 @@ import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfEntry; import org.briarproject.bramble.api.data.BdfList; -import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.briar.api.client.SessionId; -import org.briarproject.briar.introduction.AcceptMessage; -import org.briarproject.briar.introduction.ActivateMessage; -import org.briarproject.briar.introduction.AuthMessage; -import org.briarproject.briar.introduction.DeclineMessage; -import org.briarproject.briar.introduction.MessageMetadata; -import org.briarproject.briar.introduction.MessageType; -import org.briarproject.briar.introduction.RequestMessage; import java.util.Map; import javax.inject.Inject; -import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ; -import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER; -import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_LOCAL; -import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE; -import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_SESSION_ID; -import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_TIMESTAMP; -import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_VISIBLE_IN_UI; -import static org.briarproject.briar.introduction.MessageType.REQUEST; +import static org.briarproject.briar.mailbox.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER; +import static org.briarproject.briar.mailbox.IntroductionConstants.MSG_KEY_MESSAGE_TYPE; +import static org.briarproject.briar.mailbox.IntroductionConstants.MSG_KEY_SESSION_ID; +import static org.briarproject.briar.mailbox.IntroductionConstants.MSG_KEY_VISIBLE_IN_UI; +import static org.briarproject.briar.mailbox.MessageType.MAILBOX_REQUEST; @NotNullByDefault class MessageParserImpl implements @@ -50,14 +39,16 @@ class MessageParserImpl implements } @Override - public BdfDictionary getRequestsAvailableToAnswerQuery(SessionId sessionId) { + public BdfDictionary getRequestsAvailableToAnswerQuery( + SessionId sessionId) { return BdfDictionary.of( new BdfEntry(MSG_KEY_AVAILABLE_TO_ANSWER, true), - new BdfEntry(MSG_KEY_MESSAGE_TYPE, REQUEST.getValue()), + new BdfEntry(MSG_KEY_MESSAGE_TYPE, MAILBOX_REQUEST.getValue()), new BdfEntry(MSG_KEY_SESSION_ID, sessionId) ); } + /* @Override public MessageMetadata parseMetadata(BdfDictionary d) throws FormatException { @@ -74,6 +65,7 @@ class MessageParserImpl implements return new MessageMetadata(type, sessionId, timestamp, local, read, visible, available); } + */ @Override public RequestMessage parseRequestMessage(Message m, BdfList body) @@ -81,14 +73,13 @@ class MessageParserImpl implements byte[] previousMsgBytes = body.getOptionalRaw(1); MessageId previousMessageId = (previousMsgBytes == null ? null : new MessageId(previousMsgBytes)); - Author author = clientHelper.parseAndValidateAuthor(body.getList(2)); - String message = body.getOptionalString(3); return new RequestMessage(m.getId(), m.getGroupId(), - m.getTimestamp(), previousMessageId, author, message); + m.getTimestamp(), previousMessageId, body.getLong(2)); } @Override - public AcceptMessage parseAcceptMessage(Message m, BdfList body) + public MailboxAcceptMessage parseMailboxAcceptMessage(Message m, + BdfList body) throws FormatException { SessionId sessionId = new SessionId(body.getRaw(1)); byte[] previousMsgBytes = body.getOptionalRaw(2); @@ -98,9 +89,10 @@ class MessageParserImpl implements long acceptTimestamp = body.getLong(4); Map<TransportId, TransportProperties> transportProperties = clientHelper .parseAndValidateTransportPropertiesMap(body.getDictionary(5)); - return new AcceptMessage(m.getId(), m.getGroupId(), m.getTimestamp(), + return new MailboxAcceptMessage(m.getId(), m.getGroupId(), + m.getTimestamp(), previousMessageId, sessionId, ephemeralPublicKey, - acceptTimestamp, transportProperties); + acceptTimestamp, transportProperties, body.getLong(5)); } @Override @@ -111,41 +103,34 @@ class MessageParserImpl implements MessageId previousMessageId = (previousMsgBytes == null ? null : new MessageId(previousMsgBytes)); return new DeclineMessage(m.getId(), m.getGroupId(), m.getTimestamp(), - previousMessageId, sessionId); + previousMessageId, sessionId, body.getLong(3)); } @Override - public AuthMessage parseAuthMessage(Message m, BdfList body) + public MailboxAuthMessage parseMailboxAuthMessage(Message m, BdfList body) throws FormatException { + /* SessionId sessionId = new SessionId(body.getRaw(1)); byte[] previousMsgBytes = body.getRaw(2); MessageId previousMessageId = new MessageId(previousMsgBytes); byte[] mac = body.getRaw(3); byte[] signature = body.getRaw(4); - return new AuthMessage(m.getId(), m.getGroupId(), m.getTimestamp(), - previousMessageId, sessionId, mac, signature); - } - - @Override - public ActivateMessage parseActivateMessage(Message m, BdfList body) - throws FormatException { - SessionId sessionId = new SessionId(body.getRaw(1)); - byte[] previousMsgBytes = body.getRaw(2); - MessageId previousMessageId = new MessageId(previousMsgBytes); - byte[] mac = body.getRaw(3); - return new ActivateMessage(m.getId(), m.getGroupId(), m.getTimestamp(), + return new MailboxAuthMessage(m.getId(), m.getGroupId(), m.getTimestamp(), previousMessageId, sessionId, mac); + */ + return null; } @Override - public org.briarproject.briar.introduction.AbortMessage parseAbortMessage(Message m, BdfList body) + public AbortMessage parseAbortMessage( + Message m, BdfList body) throws FormatException { SessionId sessionId = new SessionId(body.getRaw(1)); byte[] previousMsgBytes = body.getOptionalRaw(2); MessageId previousMessageId = (previousMsgBytes == null ? null : new MessageId(previousMsgBytes)); - return new org.briarproject.briar.introduction.AbortMessage(m.getId(), m.getGroupId(), m.getTimestamp(), - previousMessageId, sessionId); + return new AbortMessage(m.getId(), m.getGroupId(), m.getTimestamp(), + previousMessageId, sessionId, body.getLong(3)); } } diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerProtocolEngine.java index a330103c7..3aa608b08 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerProtocolEngine.java @@ -69,7 +69,8 @@ class OwnerProtocolEngine extends AbstractProtocolEngine<OwnerSession> { @Override public OwnerSession onRequestMessage(Transaction txn, OwnerSession session, RequestMessage m) throws DbException, FormatException { - return abort(txn, session); +// return abort(txn, session); + throw new UnsupportedOperationException(); } @Override @@ -90,6 +91,13 @@ class OwnerProtocolEngine extends AbstractProtocolEngine<OwnerSession> { } } + @Override + public OwnerSession onContactAcceptMessage(Transaction txn, + OwnerSession session, MailboxAcceptMessage m) + throws DbException, FormatException { + throw new UnsupportedOperationException(); + } + private void onMailboxAccept(Transaction txn, OwnerSession s, MailboxAcceptMessage m) { diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerSession.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerSession.java index c68382ac7..d63caea3d 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerSession.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerSession.java @@ -4,45 +4,45 @@ import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.briar.api.client.SessionId; -import org.briarproject.briar.api.introduction.Role; +import org.briarproject.briar.api.mailbox.Role; import javax.annotation.concurrent.Immutable; -import static org.briarproject.briar.api.introduction.Role.INTRODUCER; +import static org.briarproject.briar.api.mailbox.Role.MAILBOX; @Immutable @NotNullByDefault class OwnerSession extends Session<OwnerState> { - private final Introducee mailbox, introducee; - - OwnerSession(SessionId sessionId, OwnerState state, - long requestTimestamp, Introducee mailbox, - Introducee introducee, long sessionCounter) { - super(sessionId, state, requestTimestamp, sessionCounter); - this.mailbox = mailbox; - this.introducee = introducee; - } - - OwnerSession(SessionId sessionId, GroupId groupIdA, Author authorA, - GroupId groupIdB, Author authorB, long sessionCounter) { - this(sessionId, OwnerState.START, -1, - new Introducee(sessionId, groupIdA, authorA), - new Introducee(sessionId, groupIdB, authorB), sessionCounter); - } - - @Override - Role getRole() { - return INTRODUCER; - } - - Introducee getMailbox() { - return mailbox; - } - - Introducee getIntroducee() { - return introducee; - } + private final Introducee mailbox, introducee; + + OwnerSession(SessionId sessionId, OwnerState state, + long requestTimestamp, Introducee mailbox, + Introducee introducee, long sessionCounter) { + super(sessionId, state, requestTimestamp, sessionCounter); + this.mailbox = mailbox; + this.introducee = introducee; + } + + OwnerSession(SessionId sessionId, GroupId groupIdA, Author authorA, + GroupId groupIdB, Author authorB, long sessionCounter) { + this(sessionId, OwnerState.START, -1, + new Introducee(sessionId, groupIdA, authorA), + new Introducee(sessionId, groupIdB, authorB), sessionCounter); + } + + @Override + Role getRole() { + return MAILBOX; + } + + Introducee getMailbox() { + return mailbox; + } + + Introducee getIntroducee() { + return introducee; + } } diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/Session.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/Session.java index 83da3b464..5f1305600 100644 --- a/briar-core/src/main/java/org/briarproject/briar/mailbox/Session.java +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/Session.java @@ -2,7 +2,7 @@ package org.briarproject.briar.mailbox; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.briar.api.client.SessionId; -import org.briarproject.briar.api.introduction.Role; +import org.briarproject.briar.api.mailbox.Role; import javax.annotation.concurrent.Immutable; diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionEncoder.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionEncoder.java new file mode 100644 index 000000000..c781f014a --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionEncoder.java @@ -0,0 +1,18 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +@NotNullByDefault +interface SessionEncoder { + + BdfDictionary getIntroduceeSessionsByIntroducerQuery(Author introducer); + + BdfDictionary getIntroducerSessionsQuery(); + + BdfDictionary encodeIntroducerSession(OwnerSession s); + + BdfDictionary encodeIntroduceeSession(IntroduceeSession s); + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionEncoderImpl.java new file mode 100644 index 000000000..413048223 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionEncoderImpl.java @@ -0,0 +1,155 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.client.ClientHelper; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.data.BdfEntry; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.transport.KeySetId; + +import java.util.Map; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import javax.inject.Inject; + +import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE; +import static org.briarproject.briar.api.introduction.Role.INTRODUCEE; +import static org.briarproject.briar.api.introduction.Role.INTRODUCER; +import static org.briarproject.briar.mailbox.IntroduceeSession.Common; +import static org.briarproject.briar.mailbox.IntroduceeSession.Local; +import static org.briarproject.briar.mailbox.IntroduceeSession.Remote; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_ACCEPT_TIMESTAMP; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_ALICE; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_AUTHOR; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_EPHEMERAL_PRIVATE_KEY; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_EPHEMERAL_PUBLIC_KEY; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_GROUP_ID; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_INTRODUCEE_A; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_INTRODUCEE_B; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_INTRODUCER; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_LOCAL; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_LOCAL_TIMESTAMP; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_MAC_KEY; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_MASTER_KEY; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_REMOTE; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_REMOTE_AUTHOR; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_REQUEST_TIMESTAMP; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_ROLE; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_SESSION_ID; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_STATE; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_TRANSPORT_KEYS; + +@Immutable +@NotNullByDefault +class SessionEncoderImpl implements + SessionEncoder { + + private final ClientHelper clientHelper; + + @Inject + SessionEncoderImpl(ClientHelper clientHelper) { + this.clientHelper = clientHelper; + } + + @Override + public BdfDictionary getIntroduceeSessionsByIntroducerQuery( + Author introducer) { + return BdfDictionary.of( + new BdfEntry(SESSION_KEY_ROLE, INTRODUCEE.getValue()), + new BdfEntry(SESSION_KEY_INTRODUCER, + clientHelper.toList(introducer)) + ); + } + + @Override + public BdfDictionary getIntroducerSessionsQuery() { + return BdfDictionary.of( + new BdfEntry(SESSION_KEY_ROLE, INTRODUCER.getValue()) + ); + } + + @Override + public BdfDictionary encodeIntroducerSession(OwnerSession s) { + BdfDictionary d = encodeSession(s); + d.put(SESSION_KEY_INTRODUCEE_A, encodeIntroducee(s.getMailbox())); + d.put(SESSION_KEY_INTRODUCEE_B, encodeIntroducee(s.getIntroducee())); + return d; + } + + private BdfDictionary encodeIntroducee(Introducee i) { + BdfDictionary d = new BdfDictionary(); + putNullable(d, SESSION_KEY_LAST_LOCAL_MESSAGE_ID, i.lastLocalMessageId); + putNullable(d, SESSION_KEY_LAST_REMOTE_MESSAGE_ID, + i.lastRemoteMessageId); + d.put(SESSION_KEY_LOCAL_TIMESTAMP, i.localTimestamp); + d.put(SESSION_KEY_GROUP_ID, i.groupId); + d.put(SESSION_KEY_AUTHOR, clientHelper.toList(i.author)); + return d; + } + + @Override + public BdfDictionary encodeIntroduceeSession(IntroduceeSession s) { + BdfDictionary d = encodeSession(s); + d.put(SESSION_KEY_INTRODUCER, clientHelper.toList(s.getIntroducer())); + d.put(SESSION_KEY_LOCAL, encodeLocal(s.getLocal())); + d.put(SESSION_KEY_REMOTE, encodeRemote(s.getRemote())); + putNullable(d, SESSION_KEY_MASTER_KEY, s.getMasterKey()); + putNullable(d, SESSION_KEY_TRANSPORT_KEYS, + encodeTransportKeys(s.getTransportKeys())); + return d; + } + + private BdfDictionary encodeCommon(Common s) { + BdfDictionary d = new BdfDictionary(); + d.put(SESSION_KEY_ALICE, s.alice); + putNullable(d, SESSION_KEY_EPHEMERAL_PUBLIC_KEY, s.ephemeralPublicKey); + d.put(SESSION_KEY_ACCEPT_TIMESTAMP, s.acceptTimestamp); + putNullable(d, SESSION_KEY_MAC_KEY, s.macKey); + return d; + } + + private BdfDictionary encodeLocal(Local s) { + BdfDictionary d = encodeCommon(s); + d.put(SESSION_KEY_LOCAL_TIMESTAMP, s.lastMessageTimestamp); + putNullable(d, SESSION_KEY_LAST_LOCAL_MESSAGE_ID, s.lastMessageId); + putNullable(d, SESSION_KEY_EPHEMERAL_PRIVATE_KEY, + s.ephemeralPrivateKey); + return d; + } + + private BdfDictionary encodeRemote(Remote s) { + BdfDictionary d = encodeCommon(s); + d.put(SESSION_KEY_REMOTE_AUTHOR, clientHelper.toList(s.author)); + putNullable(d, SESSION_KEY_LAST_REMOTE_MESSAGE_ID, s.lastMessageId); + return d; + } + + private BdfDictionary encodeSession(Session s) { + BdfDictionary d = new BdfDictionary(); + d.put(SESSION_KEY_SESSION_ID, s.getSessionId()); + d.put(SESSION_KEY_ROLE, s.getRole().getValue()); + d.put(SESSION_KEY_STATE, s.getState().getValue()); + d.put(SESSION_KEY_REQUEST_TIMESTAMP, s.getRequestTimestamp()); + return d; + } + + @Nullable + private BdfDictionary encodeTransportKeys( + @Nullable Map<TransportId, KeySetId> keys) { + if (keys == null) return null; + BdfDictionary d = new BdfDictionary(); + for (Map.Entry<TransportId, KeySetId> e : keys.entrySet()) { + d.put(e.getKey().getString(), e.getValue().getInt()); + } + return d; + } + + private void putNullable(BdfDictionary d, String key, @Nullable Object o) { + d.put(key, o == null ? NULL_VALUE : o); + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionParser.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionParser.java new file mode 100644 index 000000000..a66a24f70 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionParser.java @@ -0,0 +1,23 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.briar.api.client.SessionId; +import org.briarproject.briar.api.mailbox.Role; + +@NotNullByDefault +interface SessionParser { + + BdfDictionary getSessionQuery(SessionId s); + + Role getRole(BdfDictionary d) throws FormatException; + + OwnerSession parseOwnerSession(BdfDictionary d) + throws FormatException; + + MailboxSession parseMailboxSession(GroupId introducerGroupId, + BdfDictionary d) throws FormatException; + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionParserImpl.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionParserImpl.java new file mode 100644 index 000000000..ebc687e95 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/SessionParserImpl.java @@ -0,0 +1,210 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.client.ClientHelper; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.data.BdfEntry; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.properties.TransportProperties; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.bramble.api.transport.KeySetId; +import org.briarproject.briar.api.client.SessionId; +import org.briarproject.briar.api.mailbox.Role; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import javax.inject.Inject; + +import static org.briarproject.briar.api.mailbox.Role.INTRODUCEE; +import static org.briarproject.briar.api.mailbox.Role.OWNER; +import static org.briarproject.briar.api.mailbox.Role.fromValue; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_ACCEPT_TIMESTAMP; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_ALICE; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_AUTHOR; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_COUNTER; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_EPHEMERAL_PRIVATE_KEY; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_EPHEMERAL_PUBLIC_KEY; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_GROUP_ID; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_INTRODUCEE_A; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_INTRODUCEE_B; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_INTRODUCER; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_LOCAL; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_LOCAL_TIMESTAMP; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_MAC_KEY; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_MASTER_KEY; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_REMOTE; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_REMOTE_AUTHOR; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_REQUEST_TIMESTAMP; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_ROLE; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_SESSION_ID; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_STATE; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_TRANSPORT_KEYS; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_TRANSPORT_PROPERTIES; +import static org.briarproject.briar.mailbox.MailboxSession.Local; +import static org.briarproject.briar.mailbox.MailboxSession.Remote; + +@Immutable +@NotNullByDefault +class SessionParserImpl implements SessionParser { + + private final ClientHelper clientHelper; + + @Inject + SessionParserImpl(ClientHelper clientHelper) { + this.clientHelper = clientHelper; + } + + @Override + public BdfDictionary getSessionQuery(SessionId s) { + return BdfDictionary.of(new BdfEntry(SESSION_KEY_SESSION_ID, s)); + } + + @Override + public Role getRole(BdfDictionary d) throws FormatException { + return fromValue(d.getLong(SESSION_KEY_ROLE).intValue()); + } + + @Override + public OwnerSession parseOwnerSession(BdfDictionary d) + throws FormatException { + if (getRole(d) != OWNER) throw new IllegalArgumentException(); + SessionId sessionId = getSessionId(d); + long sessionCounter = getSessionCounter(d); + OwnerState state = OwnerState.fromValue(getState(d)); + long requestTimestamp = d.getLong(SESSION_KEY_REQUEST_TIMESTAMP); + Introducee introduceeA = parseIntroducee(sessionId, + d.getDictionary(SESSION_KEY_INTRODUCEE_A)); + Introducee introduceeB = parseIntroducee(sessionId, + d.getDictionary(SESSION_KEY_INTRODUCEE_B)); + return new OwnerSession(sessionId, state, requestTimestamp, + introduceeA, introduceeB, sessionCounter); + } + + private Introducee parseIntroducee(SessionId sessionId, + BdfDictionary d) + throws FormatException { + MessageId lastLocalMessageId = + getMessageId(d, SESSION_KEY_LAST_LOCAL_MESSAGE_ID); + MessageId lastRemoteMessageId = + getMessageId(d, SESSION_KEY_LAST_REMOTE_MESSAGE_ID); + long localTimestamp = d.getLong(SESSION_KEY_LOCAL_TIMESTAMP); + GroupId groupId = getGroupId(d, SESSION_KEY_GROUP_ID); + Author author = getAuthor(d, SESSION_KEY_AUTHOR); + return new Introducee(sessionId, groupId, author, + localTimestamp, + lastLocalMessageId, lastRemoteMessageId); + } + + @Override + public MailboxSession parseMailboxSession( + GroupId introducerGroupId, + BdfDictionary d) throws FormatException { + if (getRole(d) != INTRODUCEE) throw new IllegalArgumentException(); + SessionId sessionId = getSessionId(d); + long sessionCounter = getSessionCounter(d); + IntroduceeState state = IntroduceeState.fromValue(getState(d)); + long requestTimestamp = d.getLong(SESSION_KEY_REQUEST_TIMESTAMP); + Author introducer = getAuthor(d, SESSION_KEY_INTRODUCER); + Local local = parseLocal(d.getDictionary(SESSION_KEY_LOCAL)); + Remote remote = parseRemote(d.getDictionary(SESSION_KEY_REMOTE)); + byte[] masterKey = d.getOptionalRaw(SESSION_KEY_MASTER_KEY); + Map<TransportId, KeySetId> transportKeys = parseTransportKeys( + d.getOptionalDictionary(SESSION_KEY_TRANSPORT_KEYS)); + return new MailboxSession(sessionId, state, requestTimestamp, + introducerGroupId, introducer, local, remote, + masterKey, transportKeys, sessionCounter); + } + + private Local parseLocal(BdfDictionary d) throws FormatException { + boolean alice = d.getBoolean(SESSION_KEY_ALICE); + MessageId lastLocalMessageId = + getMessageId(d, SESSION_KEY_LAST_LOCAL_MESSAGE_ID); + long localTimestamp = d.getLong(SESSION_KEY_LOCAL_TIMESTAMP); + byte[] ephemeralPublicKey = + d.getOptionalRaw(SESSION_KEY_EPHEMERAL_PUBLIC_KEY); + BdfDictionary tpDict = + d.getOptionalDictionary(SESSION_KEY_TRANSPORT_PROPERTIES); + byte[] ephemeralPrivateKey = + d.getOptionalRaw(SESSION_KEY_EPHEMERAL_PRIVATE_KEY); + Map<TransportId, TransportProperties> transportProperties = + tpDict == null ? null : clientHelper + .parseAndValidateTransportPropertiesMap(tpDict); + long acceptTimestamp = d.getLong(SESSION_KEY_ACCEPT_TIMESTAMP); + byte[] macKey = d.getOptionalRaw(SESSION_KEY_MAC_KEY); + return new Local(alice, lastLocalMessageId, localTimestamp, + ephemeralPublicKey, ephemeralPrivateKey, acceptTimestamp, + macKey); + } + + private Remote parseRemote(BdfDictionary d) throws FormatException { + boolean alice = d.getBoolean(SESSION_KEY_ALICE); + Author remoteAuthor = getAuthor(d, SESSION_KEY_REMOTE_AUTHOR); + MessageId lastRemoteMessageId = + getMessageId(d, SESSION_KEY_LAST_REMOTE_MESSAGE_ID); + byte[] ephemeralPublicKey = + d.getOptionalRaw(SESSION_KEY_EPHEMERAL_PUBLIC_KEY); + BdfDictionary tpDict = + d.getOptionalDictionary(SESSION_KEY_TRANSPORT_PROPERTIES); + Map<TransportId, TransportProperties> transportProperties = + tpDict == null ? null : clientHelper + .parseAndValidateTransportPropertiesMap(tpDict); + long acceptTimestamp = d.getLong(SESSION_KEY_ACCEPT_TIMESTAMP); + byte[] macKey = d.getOptionalRaw(SESSION_KEY_MAC_KEY); + return new Remote(alice, remoteAuthor, + lastRemoteMessageId, + ephemeralPublicKey, transportProperties, acceptTimestamp, + macKey); + } + + private int getState(BdfDictionary d) throws FormatException { + return d.getLong(SESSION_KEY_STATE).intValue(); + } + + private long getSessionCounter(BdfDictionary d) throws FormatException { + return d.getLong(SESSION_KEY_COUNTER); + } + + private SessionId getSessionId(BdfDictionary d) throws FormatException { + byte[] b = d.getRaw(SESSION_KEY_SESSION_ID); + return new SessionId(b); + } + + @Nullable + private MessageId getMessageId(BdfDictionary d, String key) + throws FormatException { + byte[] b = d.getOptionalRaw(key); + return b == null ? null : new MessageId(b); + } + + private GroupId getGroupId(BdfDictionary d, String key) + throws FormatException { + return new GroupId(d.getRaw(key)); + } + + private Author getAuthor(BdfDictionary d, String key) + throws FormatException { + return clientHelper.parseAndValidateAuthor(d.getList(key)); + } + + @Nullable + private Map<TransportId, KeySetId> parseTransportKeys( + @Nullable BdfDictionary d) throws FormatException { + if (d == null) return null; + Map<TransportId, KeySetId> map = new HashMap<>(d.size()); + for (String key : d.keySet()) { + map.put(new TransportId(key), + new KeySetId(d.getLong(key).intValue()) + ); + } + return map; + } + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java index 108f48849..4c520efe3 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java @@ -21,6 +21,7 @@ import org.briarproject.bramble.versioning.VersioningModule; import org.briarproject.briar.blog.BlogModule; import org.briarproject.briar.client.BriarClientModule; import org.briarproject.briar.forum.ForumModule; +import org.briarproject.briar.mailbox.MailboxIntroductionModule; import org.briarproject.briar.messaging.MessagingModule; import org.briarproject.briar.privategroup.PrivateGroupModule; import org.briarproject.briar.privategroup.invitation.GroupInvitationModule; @@ -49,6 +50,7 @@ import dagger.Component; GroupInvitationModule.class, IdentityModule.class, IntroductionModule.class, + MailboxIntroductionModule.class, LifecycleModule.class, MessagingModule.class, PrivateGroupModule.class, diff --git a/briar-core/src/test/java/org/briarproject/briar/mailbox/MailboxIntroductionIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/mailbox/MailboxIntroductionIntegrationTest.java new file mode 100644 index 000000000..c8e83d3cd --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/mailbox/MailboxIntroductionIntegrationTest.java @@ -0,0 +1,352 @@ +package org.briarproject.briar.mailbox; + +import net.jodah.concurrentunit.Waiter; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.client.ClientHelper; +import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.PrivateMailbox; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.data.BdfEntry; +import org.briarproject.bramble.api.data.BdfList; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.event.EventListener; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.bramble.api.properties.TransportPropertyManager; +import org.briarproject.bramble.api.sync.Group; +import org.briarproject.bramble.api.sync.Message; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.bramble.test.TestDatabaseModule; +import org.briarproject.briar.api.client.SessionId; +import org.briarproject.briar.api.introduction.IntroductionMessage; +import org.briarproject.briar.api.introduction.IntroductionRequest; +import org.briarproject.briar.api.introduction.IntroductionResponse; +import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent; +import org.briarproject.briar.api.introduction.event.IntroductionRequestReceivedEvent; +import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent; +import org.briarproject.briar.api.introduction.event.IntroductionSucceededEvent; +import org.briarproject.briar.api.mailbox.MailboxIntroductionManager; +import org.briarproject.briar.test.BriarIntegrationTest; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +import static org.briarproject.bramble.test.TestPluginConfigModule.TRANSPORT_ID; +import static org.briarproject.bramble.test.TestUtils.getTransportProperties; +import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID; +import static org.briarproject.briar.api.introduction.IntroductionManager.MAJOR_VERSION; +import static org.briarproject.briar.mailbox.IntroductionConstants.MSG_KEY_MESSAGE_TYPE; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_AUTHOR; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_INTRODUCEE_A; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_INTRODUCEE_B; +import static org.briarproject.briar.mailbox.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; +import static org.briarproject.briar.mailbox.MessageType.DECLINE; +import static org.briarproject.briar.mailbox.MessageType.MAILBOX_ACCEPT; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class MailboxIntroductionIntegrationTest + extends + BriarIntegrationTest<MailboxIntroductionIntegrationTestComponent> { + + // objects accessed from background threads need to be volatile + private volatile MailboxIntroductionManager introductionManager0; + private volatile MailboxIntroductionManager introductionManager1; + private volatile MailboxIntroductionManager introductionManager2; + private volatile MailboxIntroductionManager introductionManagerMailbox; + private volatile Waiter eventWaiter; + + private OwnerListener listener0; + private IntroduceeListener listener1; + private IntroduceeListener listener2; + + /* + interface StateVisitor { + AcceptMessage visit(AcceptMessage response); + }*/ + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + + introductionManager0 = c0.getMailboxIntroductionManager(); + introductionManager1 = c1.getMailboxIntroductionManager(); + introductionManager2 = c2.getMailboxIntroductionManager(); + introductionManagerMailbox = cMailbox.getMailboxIntroductionManager(); + + // initialize waiter fresh for each test + eventWaiter = new Waiter(); + + addTransportProperties(); + } + + @Override + protected void createComponents() { + MailboxIntroductionIntegrationTestComponent component = + DaggerMailboxIntroductionIntegrationTestComponent.builder() + .build(); + component.inject(this); + + c0 = DaggerMailboxIntroductionIntegrationTestComponent.builder() + .testDatabaseModule(new TestDatabaseModule(t0Dir)).build(); + injectEagerSingletons(c0); + + c1 = DaggerMailboxIntroductionIntegrationTestComponent.builder() + .testDatabaseModule(new TestDatabaseModule(t1Dir)).build(); + injectEagerSingletons(c1); + + c2 = DaggerMailboxIntroductionIntegrationTestComponent.builder() + .testDatabaseModule(new TestDatabaseModule(t2Dir)).build(); + injectEagerSingletons(c2); + + cMailbox = DaggerMailboxIntroductionIntegrationTestComponent.builder() + .testDatabaseModule(new TestDatabaseModule(t3Dir)).build(); + injectEagerSingletons(cMailbox); + } + + @Test + public void testIntroductionSession() throws Exception { + addListeners(true, true); + + // make introduction + long time = clock.currentTimeMillis(); + Contact introducee = contact1From0; + PrivateMailbox mailbox = privateMailboxFrom0; + introductionManager0 + .makeIntroduction(mailbox, introducee, time); + + // sync first REQUEST message + sync0ToMailbox(1, true); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener1.requestReceived); + assertEquals(mailbox.getAuthor().getName(), + listener1.getRequest().getName()); + } + + + private void addTransportProperties() + throws DbException, IOException, TimeoutException { + TransportPropertyManager tpm0 = c0.getTransportPropertyManager(); + TransportPropertyManager tpm1 = c1.getTransportPropertyManager(); + TransportPropertyManager tpm2 = c2.getTransportPropertyManager(); + + tpm0.mergeLocalProperties(TRANSPORT_ID, getTransportProperties(2)); + sync0To1(1, true); + sync0To2(1, true); + + tpm1.mergeLocalProperties(TRANSPORT_ID, getTransportProperties(2)); + sync1To0(1, true); + + tpm2.mergeLocalProperties(TRANSPORT_ID, getTransportProperties(2)); + sync2To0(1, true); + } + + private void assertMessagesAreAcked( + Collection<IntroductionMessage> messages) { + for (IntroductionMessage msg : messages) { + if (msg.isLocal()) assertTrue(msg.isSeen()); + } + } + + private void addListeners(boolean accept1, boolean accept2) { + // listen to events + listener0 = new OwnerListener(); + c0.getEventBus().addListener(listener0); + listener1 = new IntroduceeListener(1, accept1); + c1.getEventBus().addListener(listener1); + listener2 = new IntroduceeListener(2, accept2); + c2.getEventBus().addListener(listener2); + } + + @MethodsNotNullByDefault + @ParametersNotNullByDefault + private abstract class IntroductionListener implements EventListener { + + protected volatile boolean aborted = false; + protected volatile Event latestEvent; + + @SuppressWarnings("WeakerAccess") + IntroductionResponse getResponse() { + assertTrue( + latestEvent instanceof IntroductionResponseReceivedEvent); + return ((IntroductionResponseReceivedEvent) latestEvent) + .getIntroductionResponse(); + } + } + + @MethodsNotNullByDefault + @ParametersNotNullByDefault + private class IntroduceeListener extends IntroductionListener { + + private volatile boolean requestReceived = false; + private volatile boolean succeeded = false; + private volatile boolean answerRequests = true; + private volatile SessionId sessionId; + + private final int introducee; + private final boolean accept; + + private IntroduceeListener(int introducee, boolean accept) { + this.introducee = introducee; + this.accept = accept; + } + + @Override + public void eventOccurred(Event e) { + if (e instanceof IntroductionRequestReceivedEvent) { + latestEvent = e; + IntroductionRequestReceivedEvent introEvent = + ((IntroductionRequestReceivedEvent) e); + requestReceived = true; + IntroductionRequest ir = introEvent.getIntroductionRequest(); + ContactId contactId = introEvent.getContactId(); + sessionId = ir.getSessionId(); + long time = clock.currentTimeMillis(); + try { + if (introducee == 1 && answerRequests) { + /* introductionManager1 + .respondToIntroduction(contactId, sessionId, + time, accept);*/ + } else if (introducee == 2 && answerRequests) { +/* introductionManager2 + .respondToIntroduction(contactId, sessionId, + time, accept);*/ + } + // } catch (DbException exception) { + //eventWaiter.rethrow(exception); + } finally { + eventWaiter.resume(); + } + } else if (e instanceof IntroductionResponseReceivedEvent) { + // only broadcast for DECLINE messages in introducee role + latestEvent = e; + eventWaiter.resume(); + } else if (e instanceof IntroductionSucceededEvent) { + latestEvent = e; + succeeded = true; + Contact contact = ((IntroductionSucceededEvent) e).getContact(); + eventWaiter + .assertFalse(contact.getId().equals(contactId0From1)); + eventWaiter.resume(); + } else if (e instanceof IntroductionAbortedEvent) { + latestEvent = e; + aborted = true; + eventWaiter.resume(); + } + } + + private IntroductionRequest getRequest() { + assertTrue( + latestEvent instanceof IntroductionRequestReceivedEvent); + return ((IntroductionRequestReceivedEvent) latestEvent) + .getIntroductionRequest(); + } + } + + @NotNullByDefault + private class OwnerListener extends IntroductionListener { + + private volatile boolean response1Received = false; + private volatile boolean response2Received = false; + + @Override + public void eventOccurred(Event e) { + if (e instanceof IntroductionResponseReceivedEvent) { + latestEvent = e; + ContactId c = + ((IntroductionResponseReceivedEvent) e) + .getContactId(); + if (c.equals(contactId1From0)) { + response1Received = true; + } else if (c.equals(contactId2From0)) { + response2Received = true; + } + eventWaiter.resume(); + } else if (e instanceof IntroductionAbortedEvent) { + latestEvent = e; + aborted = true; + eventWaiter.resume(); + } + } + + } + + private void replacePreviousLocalMessageId(Author author, + BdfDictionary d, MessageId id) throws FormatException { + BdfDictionary i1 = d.getDictionary(SESSION_KEY_INTRODUCEE_A); + BdfDictionary i2 = d.getDictionary(SESSION_KEY_INTRODUCEE_B); + Author a1 = clientHelper + .parseAndValidateAuthor(i1.getList(SESSION_KEY_AUTHOR)); + Author a2 = clientHelper + .parseAndValidateAuthor(i2.getList(SESSION_KEY_AUTHOR)); + + if (a1.equals(author)) { + i1.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, id); + d.put(SESSION_KEY_INTRODUCEE_A, i1); + } else if (a2.equals(author)) { + i2.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, id); + d.put(SESSION_KEY_INTRODUCEE_B, i2); + } else { + throw new AssertionError(); + } + } + + private AbstractMailboxIntroductionMessage getMessageFor(ClientHelper ch, + Contact contact, MessageType type) + throws FormatException, DbException { + Group g = introductionManager0.getContactGroup(contact); + BdfDictionary query = BdfDictionary.of( + new BdfEntry(MSG_KEY_MESSAGE_TYPE, type.getValue()) + ); + Map<MessageId, BdfDictionary> map = + ch.getMessageMetadataAsDictionary(g.getId(), query); + assertEquals(1, map.size()); + MessageId id = map.entrySet().iterator().next().getKey(); + Message m = ch.getMessage(id); + BdfList body = ch.getMessageAsList(id); + if (type == MAILBOX_ACCEPT) { + //noinspection ConstantConditions + return c0.getMessageParser().parseMailboxAcceptMessage(m, body); + } else if (type == DECLINE) { + //noinspection ConstantConditions + return c0.getMessageParser().parseDeclineMessage(m, body); + } else throw new AssertionError("Not implemented"); + } + + private OwnerSession getOwnerSession() + throws DbException, FormatException { + Map<MessageId, BdfDictionary> dicts = c0.getClientHelper() + .getMessageMetadataAsDictionary(getLocalGroup().getId()); + assertEquals(1, dicts.size()); + BdfDictionary d = dicts.values().iterator().next(); + return c0.getSessionParser().parseOwnerSession(d); + } + + private MailboxSession getMailboxSession( + MailboxIntroductionIntegrationTestComponent c) + throws DbException, FormatException { + Map<MessageId, BdfDictionary> dicts = c.getClientHelper() + .getMessageMetadataAsDictionary(getLocalGroup().getId()); + assertEquals(1, dicts.size()); + BdfDictionary d = dicts.values().iterator().next(); + Group introducerGroup = + introductionManager2.getContactGroup(contact0From2); + return c.getSessionParser() + .parseMailboxSession(introducerGroup.getId(), d); + } + + private Group getLocalGroup() { + return contactGroupFactory.createLocalGroup(CLIENT_ID, MAJOR_VERSION); + } + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/mailbox/MailboxIntroductionIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/mailbox/MailboxIntroductionIntegrationTestComponent.java new file mode 100644 index 000000000..80abea604 --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/mailbox/MailboxIntroductionIntegrationTestComponent.java @@ -0,0 +1,78 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.client.ClientModule; +import org.briarproject.bramble.contact.ContactModule; +import org.briarproject.bramble.crypto.CryptoExecutorModule; +import org.briarproject.bramble.crypto.CryptoModule; +import org.briarproject.bramble.data.DataModule; +import org.briarproject.bramble.db.DatabaseModule; +import org.briarproject.bramble.event.EventModule; +import org.briarproject.bramble.identity.IdentityModule; +import org.briarproject.bramble.lifecycle.LifecycleModule; +import org.briarproject.bramble.properties.PropertiesModule; +import org.briarproject.bramble.record.RecordModule; +import org.briarproject.bramble.sync.SyncModule; +import org.briarproject.bramble.system.SystemModule; +import org.briarproject.bramble.test.TestDatabaseModule; +import org.briarproject.bramble.test.TestPluginConfigModule; +import org.briarproject.bramble.test.TestSecureRandomModule; +import org.briarproject.bramble.transport.TransportModule; +import org.briarproject.bramble.versioning.VersioningModule; +import org.briarproject.briar.blog.BlogModule; +import org.briarproject.briar.client.BriarClientModule; +import org.briarproject.briar.forum.ForumModule; +import org.briarproject.briar.introduction.IntroductionModule; +import org.briarproject.briar.messaging.MessagingModule; +import org.briarproject.briar.privategroup.PrivateGroupModule; +import org.briarproject.briar.privategroup.invitation.GroupInvitationModule; +import org.briarproject.briar.sharing.SharingModule; +import org.briarproject.briar.test.BriarIntegrationTestComponent; + +import javax.inject.Singleton; + +import dagger.Component; + +@Singleton +@Component(modules = { + TestDatabaseModule.class, + TestPluginConfigModule.class, + TestSecureRandomModule.class, + BlogModule.class, + BriarClientModule.class, + ClientModule.class, + ContactModule.class, + CryptoModule.class, + CryptoExecutorModule.class, + DataModule.class, + DatabaseModule.class, + EventModule.class, + ForumModule.class, + GroupInvitationModule.class, + IdentityModule.class, + IntroductionModule.class, + MailboxIntroductionModule.class, + LifecycleModule.class, + MessagingModule.class, + PrivateGroupModule.class, + PropertiesModule.class, + RecordModule.class, + SharingModule.class, + SyncModule.class, + SystemModule.class, + TransportModule.class, + VersioningModule.class +}) +interface MailboxIntroductionIntegrationTestComponent + extends BriarIntegrationTestComponent { + + void inject(MailboxIntroductionIntegrationTest init); + + MessageEncoder getMessageEncoder(); + + MessageParser getMessageParser(); + + SessionParser getSessionParser(); + + MailboxIntroductionCrypto getMailboxIntroductionCrypto(); + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTest.java index f10d94183..7c43ea31a 100644 --- a/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTest.java @@ -7,6 +7,7 @@ import org.briarproject.bramble.api.client.ContactGroupFactory; import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactManager; +import org.briarproject.bramble.api.contact.PrivateMailbox; import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; @@ -44,6 +45,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory import org.briarproject.briar.blog.BlogModule; import org.briarproject.briar.forum.ForumModule; import org.briarproject.briar.introduction.IntroductionModule; +import org.briarproject.briar.mailbox.MailboxIntroductionModule; import org.briarproject.briar.messaging.MessagingModule; import org.briarproject.briar.privategroup.PrivateGroupModule; import org.briarproject.briar.privategroup.invitation.GroupInvitationModule; @@ -80,19 +82,21 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone @Nullable protected ContactId contactId1From2, contactId2From1; protected ContactId contactId0From1, contactId0From2, contactId1From0, - contactId2From0; + contactId2From0, privateMailboxIdFrom0, contactId0FromMailbox; protected Contact contact0From1, contact0From2, contact1From0, - contact2From0; - protected LocalAuthor author0, author1, author2; - protected ContactManager contactManager0, contactManager1, contactManager2; + contact2From0, contact0FromMailbox; + protected PrivateMailbox privateMailboxFrom0; + protected LocalAuthor author0, author1, author2, authorMailbox; + protected ContactManager contactManager0, contactManager1, contactManager2, + contactManagerMailbox; protected IdentityManager identityManager0, identityManager1, - identityManager2; - protected DatabaseComponent db0, db1, db2; + identityManager2, identityManagerMailbox; + protected DatabaseComponent db0, db1, db2, dbMailbox; protected MessageTracker messageTracker0, messageTracker1, messageTracker2; private LifecycleManager lifecycleManager0, lifecycleManager1, - lifecycleManager2; - private SyncSessionFactory sync0, sync1, sync2; + lifecycleManager2, lifecycleManagerMailbox; + private SyncSessionFactory sync0, sync1, sync2, syncMailbox; @Inject protected Clock clock; @@ -123,17 +127,19 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone private volatile Waiter validationWaiter; private volatile Waiter deliveryWaiter; - protected final static int TIMEOUT = 15000; - protected C c0, c1, c2; + protected final static int TIMEOUT = 600000; + protected C c0, c1, c2, cMailbox; private final File testDir = TestUtils.getTestDirectory(); private final String AUTHOR0 = "Author 0"; private final String AUTHOR1 = "Author 1"; private final String AUTHOR2 = "Author 2"; + private final String AUTHOR_MAILBOX = "Author MB"; protected File t0Dir = new File(testDir, AUTHOR0); protected File t1Dir = new File(testDir, AUTHOR1); protected File t2Dir = new File(testDir, AUTHOR2); + protected File t3Dir = new File(testDir, AUTHOR_MAILBOX); @Before public void setUp() throws Exception { @@ -143,18 +149,22 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone identityManager0 = c0.getIdentityManager(); identityManager1 = c1.getIdentityManager(); identityManager2 = c2.getIdentityManager(); + identityManagerMailbox = cMailbox.getIdentityManager(); contactManager0 = c0.getContactManager(); contactManager1 = c1.getContactManager(); contactManager2 = c2.getContactManager(); + contactManagerMailbox = cMailbox.getContactManager(); messageTracker0 = c0.getMessageTracker(); messageTracker1 = c1.getMessageTracker(); messageTracker2 = c2.getMessageTracker(); db0 = c0.getDatabaseComponent(); db1 = c1.getDatabaseComponent(); db2 = c2.getDatabaseComponent(); + dbMailbox = cMailbox.getDatabaseComponent(); sync0 = c0.getSyncSessionFactory(); sync1 = c1.getSyncSessionFactory(); sync2 = c2.getSyncSessionFactory(); + syncMailbox = cMailbox.getSyncSessionFactory(); // initialize waiters fresh for each test validationWaiter = new Waiter(); @@ -177,6 +187,7 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone component.inject(new GroupInvitationModule.EagerSingletons()); component.inject(new IdentityModule.EagerSingletons()); component.inject(new IntroductionModule.EagerSingletons()); + component.inject(new MailboxIntroductionModule.EagerSingletons()); component.inject(new LifecycleModule.EagerSingletons()); component.inject(new MessagingModule.EagerSingletons()); component.inject(new PrivateGroupModule.EagerSingletons()); @@ -193,12 +204,15 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone lifecycleManager0 = c0.getLifecycleManager(); lifecycleManager1 = c1.getLifecycleManager(); lifecycleManager2 = c2.getLifecycleManager(); + lifecycleManagerMailbox = cMailbox.getLifecycleManager(); lifecycleManager0.startServices(getSecretKey()); lifecycleManager1.startServices(getSecretKey()); lifecycleManager2.startServices(getSecretKey()); + lifecycleManagerMailbox.startServices(getSecretKey()); lifecycleManager0.waitForStartup(); lifecycleManager1.waitForStartup(); lifecycleManager2.waitForStartup(); + lifecycleManagerMailbox.waitForStartup(); } private void listenToEvents() { @@ -208,6 +222,8 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone c1.getEventBus().addListener(listener1); Listener listener2 = new Listener(); c2.getEventBus().addListener(listener2); + Listener listenerMailbox = new Listener(); + cMailbox.getEventBus().addListener(listenerMailbox); } private class Listener implements EventListener { @@ -237,6 +253,9 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone identityManager1.registerLocalAuthor(author1); author2 = identityManager2.createLocalAuthor(AUTHOR2); identityManager2.registerLocalAuthor(author2); + authorMailbox = + identityManagerMailbox.createLocalAuthor(AUTHOR_MAILBOX); + identityManagerMailbox.registerLocalAuthor(authorMailbox); } protected void addDefaultContacts() throws Exception { @@ -256,14 +275,29 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone .addContact(author0, author2.getId(), getSecretKey(), clock.currentTimeMillis(), true, true, true); contact0From2 = contactManager2.getContact(contactId0From2); + privateMailboxIdFrom0 = contactManager0 + .addPrivateMailbox(authorMailbox, author0.getId(), + getSecretKey(), clock.currentTimeMillis(), true); + privateMailboxFrom0 = + (PrivateMailbox) contactManager0 + .getContact(privateMailboxIdFrom0); + contactId0FromMailbox = contactManagerMailbox + .addMailboxOwner(author0, authorMailbox.getId(), + getSecretKey(), + clock.currentTimeMillis(), false); + contact0FromMailbox = + contactManagerMailbox.getContact(contactId0FromMailbox); // Sync initial client versioning updates sync0To1(1, true); sync0To2(1, true); + sync0ToMailbox(1, true); sync1To0(1, true); sync2To0(1, true); + syncMailboxTo0(1, true); sync0To1(1, true); sync0To2(1, true); + sync0ToMailbox(1, true); } protected void addContacts1And2() throws Exception { @@ -291,43 +325,65 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone lifecycleManager0.stopServices(); lifecycleManager1.stopServices(); lifecycleManager2.stopServices(); + lifecycleManagerMailbox.stopServices(); lifecycleManager0.waitForShutdown(); lifecycleManager1.waitForShutdown(); lifecycleManager2.waitForShutdown(); + lifecycleManagerMailbox.waitForShutdown(); } protected void sync0To1(int num, boolean valid) throws IOException, TimeoutException { - syncMessage(sync0, contactId0From1, sync1, contactId1From0, num, valid); + syncMessage(sync0, contactId0From1, sync1, contactId1From0, num, + valid); } protected void sync0To2(int num, boolean valid) throws IOException, TimeoutException { - syncMessage(sync0, contactId0From2, sync2, contactId2From0, num, valid); + syncMessage(sync0, contactId0From2, sync2, contactId2From0, num, + valid); + } + + protected void sync0ToMailbox(int num, boolean valid) + throws IOException, TimeoutException { + syncMessage(sync0, contactId0FromMailbox, syncMailbox, + privateMailboxIdFrom0, num, + valid); } protected void sync1To0(int num, boolean valid) throws IOException, TimeoutException { - syncMessage(sync1, contactId1From0, sync0, contactId0From1, num, valid); + syncMessage(sync1, contactId1From0, sync0, contactId0From1, num, + valid); } protected void sync2To0(int num, boolean valid) throws IOException, TimeoutException { - syncMessage(sync2, contactId2From0, sync0, contactId0From2, num, valid); + syncMessage(sync2, contactId2From0, sync0, contactId0From2, num, + valid); + } + + protected void syncMailboxTo0(int num, boolean valid) + throws IOException, TimeoutException { + syncMessage(syncMailbox, privateMailboxIdFrom0, sync0, + contactId0FromMailbox, + num, valid); } protected void sync2To1(int num, boolean valid) throws IOException, TimeoutException { assertNotNull(contactId2From1); assertNotNull(contactId1From2); - syncMessage(sync2, contactId2From1, sync1, contactId1From2, num, valid); + syncMessage(sync2, contactId2From1, sync1, contactId1From2, num, + valid); } protected void sync1To2(int num, boolean valid) throws IOException, TimeoutException { assertNotNull(contactId2From1); assertNotNull(contactId1From2); - syncMessage(sync1, contactId1From2, sync2, contactId2From1, num, valid); + syncMessage(sync1, contactId1From2, sync2, contactId2From1, num, + valid); } private void syncMessage(SyncSessionFactory fromSync, ContactId fromId, @@ -338,21 +394,25 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone String from = "0"; if (fromSync == sync1) from = "1"; else if (fromSync == sync2) from = "2"; + else if (fromSync == syncMailbox) from = "Mailbox"; String to = "0"; if (toSync == sync1) to = "1"; else if (toSync == sync2) to = "2"; + else if (toSync == syncMailbox) to = "Mailbox"; LOG.info("TEST: Sending message from " + from + " to " + to); ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamWriter streamWriter = new TestStreamWriter(out); // Create an outgoing sync session - SyncSession sessionFrom = fromSync.createSimplexOutgoingSession(toId, - MAX_LATENCY, streamWriter); + SyncSession sessionFrom = + fromSync.createSimplexOutgoingSession(toId, + MAX_LATENCY, streamWriter); // Write whatever needs to be written sessionFrom.run(); out.close(); - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + ByteArrayInputStream in = + new ByteArrayInputStream(out.toByteArray()); // Create an incoming sync session SyncSession sessionTo = toSync.createIncomingSession(fromId, in); // Read whatever needs to be read @@ -371,6 +431,7 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone contactManager0.removeContact(contactId2From0); contactManager1.removeContact(contactId0From1); contactManager2.removeContact(contactId0From2); + contactManagerMailbox.removeContact(contactId0FromMailbox); assertNotNull(contactId2From1); contactManager1.removeContact(contactId2From1); assertNotNull(contactId1From2); diff --git a/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java index a3b74d4c2..493a4e76c 100644 --- a/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java +++ b/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java @@ -34,12 +34,14 @@ import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.forum.ForumManager; import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.introduction.IntroductionManager; +import org.briarproject.briar.api.mailbox.MailboxIntroductionManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager; import org.briarproject.briar.blog.BlogModule; import org.briarproject.briar.client.BriarClientModule; import org.briarproject.briar.forum.ForumModule; import org.briarproject.briar.introduction.IntroductionModule; +import org.briarproject.briar.mailbox.MailboxIntroductionModule; import org.briarproject.briar.messaging.MessagingModule; import org.briarproject.briar.privategroup.PrivateGroupModule; import org.briarproject.briar.privategroup.invitation.GroupInvitationModule; @@ -67,6 +69,7 @@ import dagger.Component; GroupInvitationModule.class, IdentityModule.class, IntroductionModule.class, + MailboxIntroductionModule.class, LifecycleModule.class, MessagingModule.class, PrivateGroupModule.class, @@ -96,6 +99,8 @@ public interface BriarIntegrationTestComponent { void inject(IntroductionModule.EagerSingletons init); + void inject(MailboxIntroductionModule.EagerSingletons init); + void inject(LifecycleModule.EagerSingletons init); void inject(MessagingModule.EagerSingletons init); @@ -140,6 +145,8 @@ public interface BriarIntegrationTestComponent { IntroductionManager getIntroductionManager(); + MailboxIntroductionManager getMailboxIntroductionManager(); + MessageTracker getMessageTracker(); PrivateGroupManager getPrivateGroupManager(); -- GitLab