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 new file mode 100644 index 0000000000000000000000000000000000000000..3edb9ab0b551baf4fcca59a7deadc5eee9d66e8b --- /dev/null +++ b/briar-api/src/main/java/org/briarproject/briar/api/mailbox/MailboxIntroductionManager.java @@ -0,0 +1,30 @@ +package org.briarproject.briar.api.mailbox; + +import org.briarproject.bramble.api.contact.Contact; +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; + +public interface MailboxIntroductionManager { + /** + * The unique ID of the introduction client. + */ + ClientId CLIENT_ID = + new ClientId("org.briarproject.briar.mailbox.introduction"); + + /** + * The current major version of the introduction client. + */ + int MAJOR_VERSION = 1; + + /** + * The current minor version of the introduction client. + */ + int MINOR_VERSION = 0; + + void contactAdded(Transaction txn, Contact contact) throws DbException; + + void privateMailboxAdded(Transaction txn, PrivateMailbox privateMailbox) throws DbException; + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/BriarCoreEagerSingletons.java b/briar-core/src/main/java/org/briarproject/briar/BriarCoreEagerSingletons.java index 7380afc3c564a2d53ba00356a7b2d3699d5f3d35..2c205e969604e4055464675d65d614480d31627e 100644 --- a/briar-core/src/main/java/org/briarproject/briar/BriarCoreEagerSingletons.java +++ b/briar-core/src/main/java/org/briarproject/briar/BriarCoreEagerSingletons.java @@ -4,6 +4,7 @@ import org.briarproject.briar.blog.BlogModule; import org.briarproject.briar.feed.FeedModule; 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; @@ -27,4 +28,6 @@ public interface BriarCoreEagerSingletons { void inject(SharingModule.EagerSingletons init); + void inject(MailboxIntroductionModule.EagerSingletons init); + } diff --git a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java index ec7e1944faded57f0e9a78190e3364e1231a38c1..eb6e3dc7a48a848e5c511ce9d72e200e01d7cc86 100644 --- a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java @@ -6,6 +6,7 @@ import org.briarproject.briar.feed.DnsModule; import org.briarproject.briar.feed.FeedModule; 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; @@ -25,6 +26,7 @@ import dagger.Module; MessagingModule.class, PrivateGroupModule.class, SharingModule.class, + MailboxIntroductionModule.class, TestModule.class }) public class BriarCoreModule { @@ -38,5 +40,6 @@ public class BriarCoreModule { c.inject(new PrivateGroupModule.EagerSingletons()); c.inject(new SharingModule.EagerSingletons()); c.inject(new IntroductionModule.EagerSingletons()); + c.inject(new MailboxIntroductionModule.EagerSingletons()); } } diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/AbortMessage.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/AbortMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..413705a173d5d2c81188126bfc06d011aee1d6ee --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/AbortMessage.java @@ -0,0 +1,28 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.briar.api.client.SessionId; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +class AbortMessage extends AbstractMailboxIntroductionMessage { + + private final SessionId sessionId; + + protected AbortMessage(MessageId messageId, GroupId groupId, long timestamp, + @Nullable MessageId previousMessageId, SessionId sessionId, + long messageCounter) { + super(messageId, groupId, timestamp, previousMessageId, messageCounter); + this.sessionId = sessionId; + } + + public SessionId getSessionId() { + return sessionId; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/AbstractMailboxIntroductionMessage.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/AbstractMailboxIntroductionMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..fd8419d726bf2f4c1ebb2085a250fa755939533d --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/AbstractMailboxIntroductionMessage.java @@ -0,0 +1,48 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.MessageId; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +abstract class AbstractMailboxIntroductionMessage { + + private final MessageId messageId; + private final GroupId groupId; + private final long timestamp; + @Nullable + private final MessageId previousMessageId; + private final long messageCounter; + + AbstractMailboxIntroductionMessage(MessageId messageId, GroupId groupId, + long timestamp, @Nullable MessageId previousMessageId, + long messageCounter) { + this.messageId = messageId; + this.groupId = groupId; + this.timestamp = timestamp; + this.previousMessageId = previousMessageId; + this.messageCounter = messageCounter; + } + + MessageId getMessageId() { + return messageId; + } + + GroupId getGroupId() { + return groupId; + } + + long getTimestamp() { + return timestamp; + } + + @Nullable + MessageId getPreviousMessageId() { + return previousMessageId; + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..dbd2464530e88e29da353df2c5a5edd2b0099e84 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/AbstractProtocolEngine.java @@ -0,0 +1,180 @@ +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.ContactManager; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.db.DatabaseComponent; +import org.briarproject.bramble.api.db.DbException; +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.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.bramble.api.system.Clock; +import org.briarproject.briar.api.client.MessageTracker; +import org.briarproject.briar.api.client.SessionId; + +import java.util.Map; + +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, Author author, @Nullable String message) + throws DbException { + Message m = messageEncoder + .encodeRequestMessage(s.getContactGroupId(), timestamp, + s.getLastLocalMessageId(), author, message); + 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 + ); + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/ContactAcceptMessage.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/ContactAcceptMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..82781be3dfd710fbfb140f353a5390530af0ea39 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/ContactAcceptMessage.java @@ -0,0 +1,53 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.briar.api.client.SessionId; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +class ContactAcceptMessage extends AbstractMailboxIntroductionMessage { + + private final SessionId sessionId; + private final byte[] ephemeralPublicKey; + private final long acceptTimestamp; + private final byte[] mac; + private final byte[] signature; + + protected ContactAcceptMessage(MessageId messageId, GroupId groupId, + long timestamp, @Nullable MessageId previousMessageId, + SessionId sessionId, byte[] ephemeralPublicKey, + long acceptTimestamp, byte[] mac, byte[] signature, + long messageCounter) { + super(messageId, groupId, timestamp, previousMessageId, messageCounter); + this.sessionId = sessionId; + this.ephemeralPublicKey = ephemeralPublicKey; + this.acceptTimestamp = acceptTimestamp; + this.mac = mac; + this.signature = signature; + } + + public SessionId getSessionId() { + return sessionId; + } + + public byte[] getEphemeralPublicKey() { + return ephemeralPublicKey; + } + + public long getAcceptTimestamp() { + return acceptTimestamp; + } + + public byte[] getMac() { + return mac; + } + + public byte[] getSignature() { + return signature; + } +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/ContactRequestMessage.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/ContactRequestMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..ecfa5385997b8fdc9bffc36e7bcda7a29f2c3705 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/ContactRequestMessage.java @@ -0,0 +1,42 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.briar.api.client.SessionId; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +class ContactRequestMessage extends AbstractMailboxIntroductionMessage { + + private final SessionId sessionId; + private final byte[] ephemeralPublicKey; + private final long acceptTimestamp; + + protected ContactRequestMessage(MessageId messageId, GroupId groupId, + long timestamp, @Nullable MessageId previousMessageId, + SessionId sessionId, byte[] ephemeralPublicKey, + long acceptTimestamp, + long messageCounter) { + super(messageId, groupId, timestamp, previousMessageId, messageCounter); + this.sessionId = sessionId; + this.ephemeralPublicKey = ephemeralPublicKey; + this.acceptTimestamp = acceptTimestamp; + } + + public SessionId getSessionId() { + return sessionId; + } + + public byte[] getEphemeralPublicKey() { + return ephemeralPublicKey; + } + + public long getAcceptTimestamp() { + return acceptTimestamp; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/DeclineMessage.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/DeclineMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..df841bdcdea00307478ac4abe53408b229c78542 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/DeclineMessage.java @@ -0,0 +1,28 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.briar.api.client.SessionId; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +class DeclineMessage extends AbstractMailboxIntroductionMessage { + + private final SessionId sessionId; + + protected DeclineMessage(MessageId messageId, GroupId groupId, + long timestamp, @Nullable MessageId previousMessageId, + SessionId sessionId, long messageCounter) { + super(messageId, groupId, timestamp, previousMessageId, messageCounter); + this.sessionId = sessionId; + } + + public SessionId getSessionId() { + return sessionId; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/Introducee.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/Introducee.java new file mode 100644 index 0000000000000000000000000000000000000000..d4459605fcf32bcebb076599349c352817afca97 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/Introducee.java @@ -0,0 +1,77 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.Message; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.briar.api.client.SessionId; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +final class Introducee implements PeerSession { + final SessionId sessionId; + final GroupId groupId; + final Author author; + final long localTimestamp; + @Nullable + final MessageId lastLocalMessageId, lastRemoteMessageId; + + Introducee(SessionId sessionId, GroupId groupId, Author author, + long localTimestamp, + @Nullable MessageId lastLocalMessageId, + @Nullable MessageId lastRemoteMessageId) { + this.sessionId = sessionId; + this.groupId = groupId; + this.localTimestamp = localTimestamp; + this.author = author; + this.lastLocalMessageId = lastLocalMessageId; + this.lastRemoteMessageId = lastRemoteMessageId; + } + + Introducee(Introducee i, Message sent) { + this(i.sessionId, i.groupId, i.author, sent.getTimestamp(), + sent.getId(), i.lastRemoteMessageId); + } + + Introducee(Introducee i, MessageId remoteMessageId) { + this(i.sessionId, i.groupId, i.author, i.localTimestamp, + i.lastLocalMessageId, remoteMessageId); + } + + Introducee(SessionId sessionId, GroupId groupId, + Author author) { + this(sessionId, groupId, author, -1, null, null); + } + + public SessionId getSessionId() { + return sessionId; + } + + @Override + public GroupId getContactGroupId() { + return groupId; + } + + @Override + public long getLocalTimestamp() { + return localTimestamp; + } + + @Nullable + @Override + public MessageId getLastLocalMessageId() { + return lastLocalMessageId; + } + + @Nullable + @Override + public MessageId getLastRemoteMessageId() { + return lastRemoteMessageId; + } + +} + 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 new file mode 100644 index 0000000000000000000000000000000000000000..93fcf713001291063d4c8a4bfb09acd573cc0c4b --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroduceeSession.java @@ -0,0 +1,246 @@ +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<org.briarproject.briar.mailbox.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); + } + + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroduceeState.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroduceeState.java new file mode 100644 index 0000000000000000000000000000000000000000..1f0aebd2c54c45008c84b8ad8b72d0f0dc47ab16 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/IntroduceeState.java @@ -0,0 +1,35 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +enum IntroduceeState implements State { + + START(0), + AWAIT_LOCAL_RESPONSE(1), + LOCAL_DECLINED(2), + LOCAL_ACCEPTED(3), + AWAIT_REMOTE_RESPONSE(4), + MAILBOX_ADDED(5); + + private final int value; + + IntroduceeState(int value) { + this.value = value; + } + + @Override + public int getValue() { + return value; + } + + static IntroduceeState fromValue(int value) throws FormatException { + for (IntroduceeState s : values()) if (s.value == value) return s; + throw new FormatException(); + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxAcceptMessage.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxAcceptMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..7284e02739c5d2c8fef0444d8897929fd52dfcbb --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxAcceptMessage.java @@ -0,0 +1,53 @@ +package org.briarproject.briar.mailbox; + +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.briar.api.client.SessionId; + +import java.util.Map; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +class MailboxAcceptMessage extends AbstractMailboxIntroductionMessage { + + private final SessionId sessionId; + private final byte[] ephemeralPublicKey; + private final long acceptTimestamp; + private final Map<TransportId, TransportProperties> transportProperties; + + protected MailboxAcceptMessage(MessageId messageId, GroupId groupId, + long timestamp, @Nullable MessageId previousMessageId, + SessionId sessionId, byte[] ephemeralPublicKey, + long acceptTimestamp, + Map<TransportId, TransportProperties> transportProperties, + long messageCounter) { + super(messageId, groupId, timestamp, previousMessageId, messageCounter); + this.sessionId = sessionId; + this.ephemeralPublicKey = ephemeralPublicKey; + this.acceptTimestamp = acceptTimestamp; + this.transportProperties = transportProperties; + } + + public SessionId getSessionId() { + return sessionId; + } + + public byte[] getEphemeralPublicKey() { + return ephemeralPublicKey; + } + + public long getAcceptTimestamp() { + return acceptTimestamp; + } + + public Map<TransportId, TransportProperties> getTransportProperties() { + return transportProperties; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxAuthMessage.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxAuthMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..917a841adeea8a4cfd560e6b08cbe5d380e70938 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxAuthMessage.java @@ -0,0 +1,65 @@ +package org.briarproject.briar.mailbox; + +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.briar.api.client.SessionId; + +import java.util.Map; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +class MailboxAuthMessage extends AbstractMailboxIntroductionMessage { + + private final SessionId sessionId; + private final byte[] ephemeralPublicKey; + private final long acceptTimestamp; + private final Map<TransportId, TransportProperties> transportProperties; + private final byte[] mac; + private final byte[] signature; + + protected MailboxAuthMessage(MessageId messageId, GroupId groupId, + long timestamp, @Nullable MessageId previousMessageId, + SessionId sessionId, byte[] ephemeralPublicKey, + long acceptTimestamp, + Map<TransportId, TransportProperties> transportProperties, + byte[] mac, byte[] signature, + long messageCounter) { + super(messageId, groupId, timestamp, previousMessageId, messageCounter); + this.sessionId = sessionId; + this.ephemeralPublicKey = ephemeralPublicKey; + this.acceptTimestamp = acceptTimestamp; + this.transportProperties = transportProperties; + this.mac = mac; + this.signature = signature; + } + + public SessionId getSessionId() { + return sessionId; + } + + public byte[] getEphemeralPublicKey() { + return ephemeralPublicKey; + } + + public long getAcceptTimestamp() { + return acceptTimestamp; + } + + public byte[] getMac() { + return mac; + } + + public byte[] getSignature() { + return signature; + } + + public Map<TransportId, TransportProperties> getTransportProperties() { + return transportProperties; + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..d68950aa4e0356e2df30d85fbbe9240c9c342e28 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionManagerImpl.java @@ -0,0 +1,91 @@ +package org.briarproject.briar.mailbox; + +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.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.Transaction; +import org.briarproject.bramble.api.sync.Client; +import org.briarproject.bramble.api.sync.Group; +import org.briarproject.bramble.api.sync.Message; +import org.briarproject.briar.api.mailbox.MailboxIntroductionManager; +import org.briarproject.briar.client.BdfIncomingMessageHook; + +import java.util.logging.Logger; + +import javax.inject.Inject; + +import static org.briarproject.bramble.api.contact.ContactManager.ContactHook; +import static org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook; + +class MailboxIntroductionManagerImpl extends BdfIncomingMessageHook + implements MailboxIntroductionManager, Client, ClientVersioningHook, + ContactHook { + + private static final Logger LOG = + Logger.getLogger(MailboxIntroductionManagerImpl.class.getName()); + + @Inject + MailboxIntroductionManagerImpl( + DatabaseComponent db, + ClientHelper clientHelper, + MetadataParser metadataParser) { + super(db, clientHelper, metadataParser); + } + + @Override + protected boolean incomingMessage(Transaction txn, Message m, BdfList body, + BdfDictionary meta) throws DbException, FormatException { + return false; + } + + @Override + public void addingContact(Transaction txn, Contact c) throws DbException { + switch (c.getType()) { + case PRIVATE_MAILBOX: + privateMailboxAdded(txn, (PrivateMailbox) c); + break; + case CONTACT: + contactAdded(txn, c); + break; + default: + return; + } + + } + + @Override + public void contactAdded(Transaction txn, Contact contact) + throws DbException { + LOG.info("Contact added"); + } + + @Override + public void privateMailboxAdded(Transaction txn, + PrivateMailbox privateMailbox) + throws DbException { + LOG.info("Private mailbox added"); + } + + @Override + public void removingContact(Transaction txn, Contact c) throws DbException { + LOG.info("contact removed"); + } + + @Override + public void createLocalState(Transaction txn) throws DbException { + + } + + @Override + public void onClientVisibilityChanging(Transaction txn, Contact c, + Group.Visibility v) throws DbException { + + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..967b3d6e828267793edabf4f40d97e7c90e1596c --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxIntroductionModule.java @@ -0,0 +1,42 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.contact.ContactManager; +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.api.sync.ValidationManager; +import org.briarproject.bramble.api.versioning.ClientVersioningManager; +import org.briarproject.briar.api.mailbox.MailboxIntroductionManager; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +import static org.briarproject.briar.api.mailbox.MailboxIntroductionManager.CLIENT_ID; +import static org.briarproject.briar.api.mailbox.MailboxIntroductionManager.MAJOR_VERSION; +import static org.briarproject.briar.api.mailbox.MailboxIntroductionManager.MINOR_VERSION; + +@Module +public class MailboxIntroductionModule { + + public static class EagerSingletons { + @Inject + MailboxIntroductionManager mailboxIntroductionManager; + } + + @Provides + @Singleton + MailboxIntroductionManager provideMailboxIntroductionManager( + LifecycleManager lifecycleManager, ContactManager contactManager, + ValidationManager validationManager, + ClientVersioningManager clientVersioningManager, + MailboxIntroductionManagerImpl mailboxIntroductionManager) { + lifecycleManager.registerClient(mailboxIntroductionManager); + contactManager.registerContactHook(mailboxIntroductionManager); + validationManager.registerIncomingMessageHook(CLIENT_ID, + MAJOR_VERSION, mailboxIntroductionManager); + clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION, + MINOR_VERSION, mailboxIntroductionManager); + return mailboxIntroductionManager; + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..511250dbe1c04b15fecfdb8ef9a562221dff4d15 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxProtocolEngine.java @@ -0,0 +1,4 @@ +package org.briarproject.briar.mailbox; + +class MailboxProtocolEngine extends AbstractProtocolEngine { +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxState.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxState.java new file mode 100644 index 0000000000000000000000000000000000000000..6a44a0c4d22eb84d368bba5be8ad224fcf2b2029 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MailboxState.java @@ -0,0 +1,37 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +enum MailboxState implements State { + + START(0), + AWAIT_LOCAL_RESPONSES(1), + LOCAL_DECLINED(2), + LOCAL_ACCEPTED(3), + AWAIT_REMOTE_RESPONSE(4), + REMOTE_DECLINED(5), + REMOTE_ACCEPTED(6), + CONTACT_ADDED(7); + + private final int value; + + MailboxState(int value) { + this.value = value; + } + + @Override + public int getValue() { + return value; + } + + static MailboxState fromValue(int value) throws FormatException { + for (MailboxState s : values()) if (s.value == value) return s; + throw new FormatException(); + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..2bf584c98b347b7b40fd7cc2427a7ff8ec392f03 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageEncoder.java @@ -0,0 +1,56 @@ +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; +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.briar.api.client.SessionId; +import org.briarproject.briar.introduction.MessageType; + +import java.util.Map; + +import javax.annotation.Nullable; + +@NotNullByDefault +interface MessageEncoder { + + BdfDictionary encodeRequestMetadata(long timestamp); + + BdfDictionary encodeMetadata(MessageType type, + @Nullable SessionId sessionId, long timestamp, boolean local, + boolean read, boolean visible); + + void addSessionId(BdfDictionary meta, SessionId sessionId); + + void setVisibleInUi(BdfDictionary meta, boolean visible); + + void setAvailableToAnswer(BdfDictionary meta, boolean available); + + Message encodeRequestMessage(GroupId contactGroupId, long timestamp, + @Nullable MessageId previousMessageId, Author author, + @Nullable String message); + + Message encodeAcceptMessage(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 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); + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..a75d0ba273d58a43b9af09a89a23c44f1e5b8701 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageEncoderImpl.java @@ -0,0 +1,185 @@ +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.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.GroupId; +import org.briarproject.bramble.api.sync.Message; +import org.briarproject.bramble.api.sync.MessageFactory; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.briar.api.client.SessionId; +import org.briarproject.briar.introduction.MessageType; + +import java.util.Map; + +import javax.annotation.Nullable; +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.ABORT; +import static org.briarproject.briar.introduction.MessageType.ACCEPT; +import static org.briarproject.briar.introduction.MessageType.ACTIVATE; +import static org.briarproject.briar.introduction.MessageType.AUTH; +import static org.briarproject.briar.introduction.MessageType.DECLINE; +import static org.briarproject.briar.introduction.MessageType.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(REQUEST, null, timestamp, false, false, false); + meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, false); + return meta; + } + + @Override + public BdfDictionary encodeMetadata(MessageType type, + @Nullable SessionId sessionId, long timestamp, boolean local, + boolean read, boolean visible) { + 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 != REQUEST) + throw new IllegalArgumentException(); + meta.put(MSG_KEY_TIMESTAMP, timestamp); + meta.put(MSG_KEY_LOCAL, local); + meta.put(MSG_KEY_READ, read); + meta.put(MSG_KEY_VISIBLE_IN_UI, visible); + 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, Author author, + @Nullable String message) { + if (message != null && message.equals("")) { + throw new IllegalArgumentException(); + } + BdfList body = BdfList.of( + REQUEST.getValue(), + previousMessageId, + clientHelper.toList(author), + message + ); + 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); + } + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..5b6f729e81e46e2e487801375a0bb867075226f9 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageParser.java @@ -0,0 +1,44 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.data.BdfDictionary; +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 { + + BdfDictionary getMessagesVisibleInUiQuery(); + + BdfDictionary getRequestsAvailableToAnswerQuery(SessionId sessionId); + + MessageMetadata parseMetadata(BdfDictionary meta) throws FormatException; + + RequestMessage parseRequestMessage(Message m, BdfList body) + throws FormatException; + + AcceptMessage parseAcceptMessage(Message m, BdfList body) + throws FormatException; + + DeclineMessage parseDeclineMessage(Message m, BdfList body) + throws FormatException; + + AuthMessage parseAuthMessage(Message m, BdfList body) + throws FormatException; + + ActivateMessage parseActivateMessage(Message m, BdfList body) + throws FormatException; + + org.briarproject.briar.introduction.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 new file mode 100644 index 0000000000000000000000000000000000000000..89284e26a1dcb2e6cf32cbbe069367e18c510a41 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageParserImpl.java @@ -0,0 +1,151 @@ +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.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; + +@NotNullByDefault +class MessageParserImpl implements + MessageParser { + + private final ClientHelper clientHelper; + + @Inject + MessageParserImpl(ClientHelper clientHelper) { + this.clientHelper = clientHelper; + } + + @Override + public BdfDictionary getMessagesVisibleInUiQuery() { + return BdfDictionary.of(new BdfEntry(MSG_KEY_VISIBLE_IN_UI, true)); + } + + @Override + 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_SESSION_ID, sessionId) + ); + } + + @Override + public MessageMetadata parseMetadata(BdfDictionary d) + throws FormatException { + MessageType type = MessageType + .fromValue(d.getLong(MSG_KEY_MESSAGE_TYPE).intValue()); + byte[] sessionIdBytes = d.getOptionalRaw(MSG_KEY_SESSION_ID); + SessionId sessionId = + sessionIdBytes == null ? null : new SessionId(sessionIdBytes); + long timestamp = d.getLong(MSG_KEY_TIMESTAMP); + boolean local = d.getBoolean(MSG_KEY_LOCAL); + boolean read = d.getBoolean(MSG_KEY_READ); + boolean visible = d.getBoolean(MSG_KEY_VISIBLE_IN_UI); + boolean available = d.getBoolean(MSG_KEY_AVAILABLE_TO_ANSWER, false); + return new MessageMetadata(type, sessionId, timestamp, local, read, + visible, available); + } + + @Override + public RequestMessage parseRequestMessage(Message m, BdfList body) + throws FormatException { + 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); + } + + @Override + public AcceptMessage parseAcceptMessage(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)); + byte[] ephemeralPublicKey = body.getRaw(3); + long acceptTimestamp = body.getLong(4); + Map<TransportId, TransportProperties> transportProperties = clientHelper + .parseAndValidateTransportPropertiesMap(body.getDictionary(5)); + return new AcceptMessage(m.getId(), m.getGroupId(), m.getTimestamp(), + previousMessageId, sessionId, ephemeralPublicKey, + acceptTimestamp, transportProperties); + } + + @Override + public DeclineMessage parseDeclineMessage(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 DeclineMessage(m.getId(), m.getGroupId(), m.getTimestamp(), + previousMessageId, sessionId); + } + + @Override + public AuthMessage parseAuthMessage(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(), + previousMessageId, sessionId, mac); + } + + @Override + public org.briarproject.briar.introduction.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); + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageType.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageType.java new file mode 100644 index 0000000000000000000000000000000000000000..644de22c4b8125d0c5f1f3675dc3a17b4b6a0c52 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/MessageType.java @@ -0,0 +1,30 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +enum MessageType { + + MAILBOX_REQUEST(0), MAILBOX_ACCEPT(1), CONTACT_REQUEST(2), + CONTACT_ACCEPT(3), CONTACT_INFO(4), MAILBOX_AUTH(5), DECLINE(5), ABORT(6); + + private final int value; + + MessageType(int value) { + this.value = value; + } + + int getValue() { + return value; + } + + static MessageType fromValue(int value) throws FormatException { + for (MessageType m : values()) if (m.value == value) return m; + throw new FormatException(); + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..384f5b032384e64258c3007cda5c84dd76e9baeb --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerProtocolEngine.java @@ -0,0 +1,4 @@ +package org.briarproject.briar.mailbox; + +class OwnerProtocolEngine extends AbstractProtocolEngine { +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..b6d4525d44076837ffabc2a408c66317e05bc7d5 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerSession.java @@ -0,0 +1,49 @@ +package org.briarproject.briar.mailbox; + +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 javax.annotation.concurrent.Immutable; + +import static org.briarproject.briar.api.introduction.Role.INTRODUCER; + +@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; + } + + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerState.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerState.java new file mode 100644 index 0000000000000000000000000000000000000000..a8ac3d6eaff26447d5718ee5054832514f81eeb0 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/OwnerState.java @@ -0,0 +1,36 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +enum OwnerState implements State { + + START(0), + AWAIT_RESPONSE_M(1), + M_DECLINE(2), + AWAIT_RESPONSE_B(3), + B_DECLINED(4), + AWAIT_AUTH_M(5), + ADDED(6); + + private final int value; + + OwnerState(int value) { + this.value = value; + } + + @Override + public int getValue() { + return value; + } + + static OwnerState fromValue(int value) throws FormatException { + for (OwnerState s : values()) if (s.value == value) return s; + throw new FormatException(); + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/PeerSession.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/PeerSession.java new file mode 100644 index 0000000000000000000000000000000000000000..69b944943b77f46ba6b8edd1b726aca41abebaf8 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/PeerSession.java @@ -0,0 +1,25 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.briar.api.client.SessionId; + +import javax.annotation.Nullable; + +@NotNullByDefault +interface PeerSession { + + SessionId getSessionId(); + + GroupId getContactGroupId(); + + long getLocalTimestamp(); + + @Nullable + MessageId getLastLocalMessageId(); + + @Nullable + MessageId getLastRemoteMessageId(); + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/ProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/ProtocolEngine.java new file mode 100644 index 0000000000000000000000000000000000000000..9845d4a68ea4e9f9fc85b7311548294032f8b876 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/ProtocolEngine.java @@ -0,0 +1,37 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.Transaction; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.Nullable; + +@NotNullByDefault +interface ProtocolEngine<S extends Session> { + + S onRequestAction(Transaction txn, S session, @Nullable String message, + long timestamp) throws DbException; + + S onAcceptAction(Transaction txn, S session, long timestamp) + throws DbException; + + S onDeclineAction(Transaction txn, S session, long timestamp) + throws DbException; + + S onRequestMessage(Transaction txn, S session, RequestMessage m) + throws DbException, FormatException; + + S onAcceptMessage(Transaction txn, S session, MailboxAcceptMessage m) + throws DbException, FormatException; + + S onDeclineMessage(Transaction txn, S session, DeclineMessage m) + throws DbException, FormatException; + + S onAuthMessage(Transaction txn, S session, MailboxAuthMessage m) + throws DbException, FormatException; + + S onAbortMessage(Transaction txn, S session, AbortMessage m) + throws DbException, FormatException; + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/RequestMessage.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/RequestMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..5c060c9e31d5ee2249a5345750f340487ea86f16 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/RequestMessage.java @@ -0,0 +1,19 @@ +package org.briarproject.briar.mailbox; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.MessageId; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +class RequestMessage extends AbstractMailboxIntroductionMessage { + + protected RequestMessage(MessageId messageId, GroupId groupId, + long timestamp, @Nullable MessageId previousMessageId, + long messageCounter) { + super(messageId, groupId, timestamp, previousMessageId, messageCounter); + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..83da3b46449a22e8925785a5ae0ae9f976c8b617 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/Session.java @@ -0,0 +1,44 @@ +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 javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +abstract class Session<S extends State> { + + private final SessionId sessionId; + private final S state; + private final long requestTimestamp; + private final long sessionCounter; + + Session(SessionId sessionId, S state, long requestTimestamp, + long sessionCounter) { + this.sessionId = sessionId; + this.state = state; + this.requestTimestamp = requestTimestamp; + this.sessionCounter = sessionCounter; + } + + abstract Role getRole(); + + public SessionId getSessionId() { + return sessionId; + } + + S getState() { + return state; + } + + long getRequestTimestamp() { + return requestTimestamp; + } + + long getSessionCounter() { + return sessionCounter; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/mailbox/State.java b/briar-core/src/main/java/org/briarproject/briar/mailbox/State.java new file mode 100644 index 0000000000000000000000000000000000000000..66a2e80d120ea5dd121746aed47a51ddbcc9791d --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/mailbox/State.java @@ -0,0 +1,7 @@ +package org.briarproject.briar.mailbox; + +interface State { + + int getValue(); + +}