From 672a52b2e5014a4fc76adcb8035d9effbb316536 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Tue, 17 Apr 2018 18:13:35 -0300 Subject: [PATCH] Implement MessageEncoder and MessageParser --- .../briar/introduction2/AcceptMessage.java | 7 + .../introduction2/IntroductionConstants.java | 14 ++ .../introduction2/IntroductionValidator.java | 11 +- .../briar/introduction2/MessageEncoder.java | 52 +++- .../introduction2/MessageEncoderImpl.java | 205 ++++++++++++++++ .../briar/introduction2/MessageMetadata.java | 65 +++++ .../briar/introduction2/MessageParser.java | 37 +++ .../introduction2/MessageParserImpl.java | 144 +++++++++++ .../IntroductionValidatorTest.java | 22 +- .../MessageEncoderParserIntegrationTest.java | 231 ++++++++++++++++++ .../introduction2/MessageEncoderTest.java | 63 +++++ .../test/BriarIntegrationTestComponent.java | 3 + .../briar/test/BriarTestUtils.java | 10 + 13 files changed, 844 insertions(+), 20 deletions(-) create mode 100644 briar-core/src/main/java/org/briarproject/briar/introduction2/IntroductionConstants.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/introduction2/MessageEncoderImpl.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/introduction2/MessageMetadata.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/introduction2/MessageParser.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/introduction2/MessageParserImpl.java create mode 100644 briar-core/src/test/java/org/briarproject/briar/introduction2/MessageEncoderParserIntegrationTest.java create mode 100644 briar-core/src/test/java/org/briarproject/briar/introduction2/MessageEncoderTest.java diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction2/AcceptMessage.java b/briar-core/src/main/java/org/briarproject/briar/introduction2/AcceptMessage.java index b9bf92b6c4..77d09c74ac 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction2/AcceptMessage.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction2/AcceptMessage.java @@ -18,16 +18,19 @@ class AcceptMessage extends IntroductionMessage { private final SessionId sessionId; private final byte[] ephemeralPublicKey; + private final long acceptTimestamp; private final Map<TransportId, TransportProperties> transportProperties; protected AcceptMessage(MessageId messageId, GroupId groupId, long timestamp, @Nullable MessageId previousMessageId, SessionId sessionId, byte[] ephemeralPublicKey, + long acceptTimestamp, Map<TransportId, TransportProperties> transportProperties) { super(messageId, groupId, timestamp, previousMessageId); this.sessionId = sessionId; this.ephemeralPublicKey = ephemeralPublicKey; + this.acceptTimestamp = acceptTimestamp; this.transportProperties = transportProperties; } @@ -39,6 +42,10 @@ class AcceptMessage extends IntroductionMessage { 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/introduction2/IntroductionConstants.java b/briar-core/src/main/java/org/briarproject/briar/introduction2/IntroductionConstants.java new file mode 100644 index 0000000000..4098b2421e --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/introduction2/IntroductionConstants.java @@ -0,0 +1,14 @@ +package org.briarproject.briar.introduction2; + +interface IntroductionConstants { + + // Message metadata keys + String MSG_KEY_MESSAGE_TYPE = "messageType"; + String MSG_KEY_SESSION_ID = "sessionId"; + String MSG_KEY_TIMESTAMP = "timestamp"; + String MSG_KEY_LOCAL = "local"; + String MSG_KEY_VISIBLE_IN_UI = "visibleInUi"; + String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer"; + String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted"; + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction2/IntroductionValidator.java b/briar-core/src/main/java/org/briarproject/briar/introduction2/IntroductionValidator.java index ce8359701f..1bdf07ff64 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction2/IntroductionValidator.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction2/IntroductionValidator.java @@ -28,7 +28,6 @@ import static org.briarproject.bramble.util.ValidationUtils.checkSize; import static org.briarproject.briar.api.introduction2.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH; import static org.briarproject.briar.introduction2.MessageType.ACCEPT; import static org.briarproject.briar.introduction2.MessageType.AUTH; -import static org.briarproject.briar.introduction2.MessageType.REQUEST; @Immutable @@ -79,8 +78,8 @@ class IntroductionValidator extends BdfMessageValidator { checkLength(msg, 1, MAX_REQUEST_MESSAGE_LENGTH); BdfDictionary meta = messageEncoder - .encodeRequestMetadata(REQUEST, m.getTimestamp(), false, - false, false, false, false); + .encodeRequestMetadata(m.getTimestamp(), false, false, + false, false); if (previousMessageId == null) { return new BdfMessageContext(meta); } else { @@ -92,7 +91,7 @@ class IntroductionValidator extends BdfMessageValidator { private BdfMessageContext validateAcceptMessage(Message m, BdfList body) throws FormatException { - checkSize(body, 5); + checkSize(body, 6); byte[] sessionIdBytes = body.getRaw(1); checkLength(sessionIdBytes, UniqueId.LENGTH); @@ -103,7 +102,9 @@ class IntroductionValidator extends BdfMessageValidator { byte[] ephemeralPublicKey = body.getRaw(3); checkLength(ephemeralPublicKey, 0, MAX_PUBLIC_KEY_LENGTH); - BdfDictionary transportProperties = body.getDictionary(4); + body.getLong(4); + + BdfDictionary transportProperties = body.getDictionary(5); if (transportProperties.size() < 1) throw new FormatException(); for (String tId : transportProperties.keySet()) { checkLength(tId, 1, MAX_TRANSPORT_ID_LENGTH); diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageEncoder.java b/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageEncoder.java index 25371b68cb..390ea9b0fb 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageEncoder.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageEncoder.java @@ -1,15 +1,57 @@ package org.briarproject.briar.introduction2; 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 java.util.Map; + +import javax.annotation.Nullable; + +@NotNullByDefault interface MessageEncoder { - BdfDictionary encodeRequestMetadata(MessageType type, - long timestamp, boolean local, boolean read, boolean visible, - boolean available, boolean accepted); + BdfDictionary encodeRequestMetadata(long timestamp, boolean local, + boolean read, boolean available, boolean accepted); + + 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); + + void setInvitationAccepted(BdfDictionary meta, boolean accepted); + + 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); - BdfDictionary encodeMetadata(MessageType type, SessionId sessionId, - long timestamp, boolean local, boolean read, boolean visible); + Message encodeAbortMessage(GroupId contactGroupId, long timestamp, + @Nullable MessageId previousMessageId, SessionId sessionId); } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageEncoderImpl.java new file mode 100644 index 0000000000..7f44671e0c --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageEncoderImpl.java @@ -0,0 +1,205 @@ +package org.briarproject.briar.introduction2; + +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 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.introduction2.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_INVITATION_ACCEPTED; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_LOCAL; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_MESSAGE_TYPE; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_SESSION_ID; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_TIMESTAMP; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_VISIBLE_IN_UI; +import static org.briarproject.briar.introduction2.MessageType.ABORT; +import static org.briarproject.briar.introduction2.MessageType.ACCEPT; +import static org.briarproject.briar.introduction2.MessageType.ACTIVATE; +import static org.briarproject.briar.introduction2.MessageType.AUTH; +import static org.briarproject.briar.introduction2.MessageType.DECLINE; +import static org.briarproject.briar.introduction2.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, + boolean local, boolean read, boolean available, + boolean accepted) { + BdfDictionary meta = + encodeMetadata(REQUEST, null, timestamp, local, read, false); + meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available); + meta.put(MSG_KEY_INVITATION_ACCEPTED, accepted); + 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 void setInvitationAccepted(BdfDictionary meta, boolean accepted) { + meta.put(MSG_KEY_INVITATION_ACCEPTED, accepted); + } + + @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 + ); + try { + return messageFactory.createMessage(contactGroupId, timestamp, + clientHelper.toByteArray(body)); + } catch (FormatException e) { + throw new AssertionError(e); + } + } + + @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, + encodeTransportProperties(transportProperties) + ); + try { + return messageFactory.createMessage(contactGroupId, timestamp, + clientHelper.toByteArray(body)); + } catch (FormatException e) { + throw new AssertionError(e); + } + } + + @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 + ); + try { + return messageFactory.createMessage(contactGroupId, timestamp, + clientHelper.toByteArray(body)); + } catch (FormatException e) { + throw new AssertionError(e); + } + } + + @Override + public Message encodeActivateMessage(GroupId contactGroupId, long timestamp, + @Nullable MessageId previousMessageId, SessionId sessionId) { + return encodeMessage(ACTIVATE, 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 + ); + try { + return messageFactory.createMessage(contactGroupId, timestamp, + clientHelper.toByteArray(body)); + } catch (FormatException e) { + throw new AssertionError(e); + } + } + + private BdfDictionary encodeTransportProperties( + Map<TransportId, TransportProperties> map) { + BdfDictionary d = new BdfDictionary(); + for (Map.Entry<TransportId, TransportProperties> e : map.entrySet()) { + d.put(e.getKey().getString(), e.getValue()); + } + return d; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageMetadata.java b/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageMetadata.java new file mode 100644 index 0000000000..67ba5b0319 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageMetadata.java @@ -0,0 +1,65 @@ +package org.briarproject.briar.introduction2; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.briar.api.client.SessionId; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +class MessageMetadata { + + private final MessageType type; + @Nullable + private final SessionId sessionId; + private final long timestamp; + private final boolean local, read, visible, available, accepted; + + MessageMetadata(MessageType type, @Nullable SessionId sessionId, + long timestamp, boolean local, boolean read, boolean visible, + boolean available, boolean accepted) { + this.type = type; + this.sessionId = sessionId; + this.timestamp = timestamp; + this.local = local; + this.read = read; + this.visible = visible; + this.available = available; + this.accepted = accepted; + } + + MessageType getMessageType() { + return type; + } + + @Nullable + public SessionId getSessionId() { + return sessionId; + } + + long getTimestamp() { + return timestamp; + } + + boolean isLocal() { + return local; + } + + boolean isRead() { + return read; + } + + boolean isVisibleInConversation() { + return visible; + } + + boolean isAvailableToAnswer() { + return available; + } + + public boolean wasAccepted() { + return accepted; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageParser.java b/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageParser.java new file mode 100644 index 0000000000..45526a0919 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageParser.java @@ -0,0 +1,37 @@ +package org.briarproject.briar.introduction2; + +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; + +@NotNullByDefault +interface MessageParser { + + BdfDictionary getMessagesVisibleInUiQuery(); + + BdfDictionary getInvitesAvailableToAnswerQuery(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; + + AbortMessage parseAbortMessage(Message m, BdfList body) + throws FormatException; + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageParserImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageParserImpl.java new file mode 100644 index 0000000000..1c55be11d4 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/introduction2/MessageParserImpl.java @@ -0,0 +1,144 @@ +package org.briarproject.briar.introduction2; + +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 java.util.Map; + +import javax.inject.Inject; + +import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_INVITATION_ACCEPTED; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_LOCAL; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_MESSAGE_TYPE; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_SESSION_ID; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_TIMESTAMP; +import static org.briarproject.briar.introduction2.IntroductionConstants.MSG_KEY_VISIBLE_IN_UI; +import static org.briarproject.briar.introduction2.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 getInvitesAvailableToAnswerQuery(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); + boolean accepted = d.getBoolean(MSG_KEY_INVITATION_ACCEPTED, false); + return new MessageMetadata(type, sessionId, timestamp, local, read, + visible, available, accepted); + } + + @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); + return new ActivateMessage(m.getId(), m.getGroupId(), m.getTimestamp(), + previousMessageId, sessionId); + } + + @Override + 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 AbortMessage(m.getId(), m.getGroupId(), m.getTimestamp(), + previousMessageId, sessionId); + } + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction2/IntroductionValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction2/IntroductionValidatorTest.java index fc01094979..0fa094a5ea 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction2/IntroductionValidatorTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction2/IntroductionValidatorTest.java @@ -41,6 +41,7 @@ public class IntroductionValidatorTest extends ValidatorTestCase { private final String text = getRandomString(MAX_INTRODUCTION_MESSAGE_LENGTH); private final BdfDictionary meta = new BdfDictionary(); + private final long acceptTimestamp = 42; private final BdfDictionary transportProperties = BdfDictionary.of( new BdfEntry("transportId", new BdfDictionary()) ); @@ -124,7 +125,7 @@ public class IntroductionValidatorTest extends ValidatorTestCase { public void testAcceptsAccept() throws Exception { BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(), previousMsgId.getBytes(), getRandomBytes(MAX_PUBLIC_KEY_LENGTH), - transportProperties); + acceptTimestamp, transportProperties); context.checking(new Expectations() {{ oneOf(clientHelper).parseAndValidateTransportProperties( transportProperties.getDictionary("transportId")); @@ -140,7 +141,7 @@ public class IntroductionValidatorTest extends ValidatorTestCase { public void testRejectsTooShortBodyForAccept() throws Exception { BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(), previousMsgId.getBytes(), - getRandomBytes(MAX_PUBLIC_KEY_LENGTH)); + getRandomBytes(MAX_PUBLIC_KEY_LENGTH), acceptTimestamp); validator.validateMessage(message, group, body); } @@ -148,7 +149,7 @@ public class IntroductionValidatorTest extends ValidatorTestCase { public void testRejectsTooLongBodyForAccept() throws Exception { BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(), previousMsgId.getBytes(), getRandomBytes(MAX_PUBLIC_KEY_LENGTH), - transportProperties, null); + acceptTimestamp, transportProperties, null); validator.validateMessage(message, group, body); } @@ -156,7 +157,7 @@ public class IntroductionValidatorTest extends ValidatorTestCase { public void testRejectsInvalidSessionIdForAccept() throws Exception { BdfList body = BdfList.of(ACCEPT.getValue(), null, previousMsgId.getBytes(), - getRandomBytes(MAX_PUBLIC_KEY_LENGTH), + getRandomBytes(MAX_PUBLIC_KEY_LENGTH), acceptTimestamp, transportProperties); validator.validateMessage(message, group, body); } @@ -164,7 +165,7 @@ public class IntroductionValidatorTest extends ValidatorTestCase { @Test(expected = FormatException.class) public void testRejectsInvalidPreviousMsgIdForAccept() throws Exception { BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(), - null, getRandomBytes(MAX_PUBLIC_KEY_LENGTH), + null, getRandomBytes(MAX_PUBLIC_KEY_LENGTH), acceptTimestamp, transportProperties); validator.validateMessage(message, group, body); } @@ -173,7 +174,8 @@ public class IntroductionValidatorTest extends ValidatorTestCase { public void testRejectsTooLongPublicKeyForAccept() throws Exception { BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(), previousMsgId.getBytes(), - getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), transportProperties); + getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), acceptTimestamp, + transportProperties); validator.validateMessage(message, group, body); } @@ -182,7 +184,8 @@ public class IntroductionValidatorTest extends ValidatorTestCase { throws Exception { BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(), previousMsgId.getBytes(), - getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), new BdfDictionary()); + getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), acceptTimestamp, + new BdfDictionary()); validator.validateMessage(message, group, body); } @@ -397,9 +400,8 @@ public class IntroductionValidatorTest extends ValidatorTestCase { private void expectEncodeRequestMetadata() { context.checking(new Expectations() {{ oneOf(messageEncoder) - .encodeRequestMetadata(REQUEST, message.getTimestamp(), - false, false, - false, false, false); + .encodeRequestMetadata(message.getTimestamp(), false, false, + false, false); will(returnValue(meta)); }}); } diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction2/MessageEncoderParserIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction2/MessageEncoderParserIntegrationTest.java new file mode 100644 index 0000000000..e623f814ac --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/introduction2/MessageEncoderParserIntegrationTest.java @@ -0,0 +1,231 @@ +package org.briarproject.briar.introduction2; + +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.identity.Author; +import org.briarproject.bramble.api.identity.AuthorFactory; +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.bramble.test.BrambleTestCase; +import org.briarproject.briar.api.client.SessionId; +import org.briarproject.briar.test.BriarIntegrationTestComponent; +import org.briarproject.briar.test.DaggerBriarIntegrationTestComponent; +import org.junit.Test; + +import java.util.Map; + +import javax.inject.Inject; + +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.test.TestUtils.getRandomBytes; +import static org.briarproject.bramble.test.TestUtils.getRandomId; +import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap; +import static org.briarproject.bramble.util.StringUtils.getRandomString; +import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH; +import static org.briarproject.briar.introduction2.MessageType.ABORT; +import static org.briarproject.briar.introduction2.MessageType.REQUEST; +import static org.briarproject.briar.test.BriarTestUtils.getRealAuthor; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class MessageEncoderParserIntegrationTest extends BrambleTestCase { + + @Inject + ClientHelper clientHelper; + @Inject + MessageFactory messageFactory; + @Inject + AuthorFactory authorFactory; + + private final MessageEncoder messageEncoder; + private final MessageParser messageParser; + + private final GroupId groupId = new GroupId(getRandomId()); + private final long timestamp = 42L; + private final SessionId sessionId = new SessionId(getRandomId()); + private final MessageId previousMsgId = new MessageId(getRandomId()); + private final Author author; + private final String text = + getRandomString(MAX_INTRODUCTION_MESSAGE_LENGTH); + private final byte[] ephemeralPublicKey = + getRandomBytes(MAX_PUBLIC_KEY_LENGTH); + private final byte[] mac = getRandomBytes(MAC_BYTES); + private final byte[] signature = getRandomBytes(MAX_SIGNATURE_BYTES); + + public MessageEncoderParserIntegrationTest() { + BriarIntegrationTestComponent component = + DaggerBriarIntegrationTestComponent.builder().build(); + component.inject(this); + + messageEncoder = new MessageEncoderImpl(clientHelper, messageFactory); + messageParser = new MessageParserImpl(clientHelper); + author = getRealAuthor(authorFactory); + } + + @Test + public void testRequestMessageMetadata() throws FormatException { + BdfDictionary d = messageEncoder + .encodeRequestMetadata(timestamp, true, false, false, + true); + MessageMetadata meta = messageParser.parseMetadata(d); + + assertEquals(REQUEST, meta.getMessageType()); + assertNull(meta.getSessionId()); + assertEquals(timestamp, meta.getTimestamp()); + assertTrue(meta.isLocal()); + assertFalse(meta.isRead()); + assertFalse(meta.isVisibleInConversation()); + assertFalse(meta.isAvailableToAnswer()); + assertTrue(meta.wasAccepted()); + } + + @Test + public void testMessageMetadata() throws FormatException { + BdfDictionary d = messageEncoder + .encodeMetadata(ABORT, sessionId, timestamp, false, true, + false); + MessageMetadata meta = messageParser.parseMetadata(d); + + assertEquals(ABORT, meta.getMessageType()); + assertEquals(sessionId, meta.getSessionId()); + assertEquals(timestamp, meta.getTimestamp()); + assertFalse(meta.isLocal()); + assertTrue(meta.isRead()); + assertFalse(meta.isVisibleInConversation()); + assertFalse(meta.isAvailableToAnswer()); + assertFalse(meta.wasAccepted()); + } + + @Test + public void testRequestMessage() throws FormatException { + Message m = messageEncoder + .encodeRequestMessage(groupId, timestamp, previousMsgId, author, + text); + RequestMessage rm = + messageParser.parseRequestMessage(m, clientHelper.toList(m)); + + assertEquals(m.getId(), rm.getMessageId()); + assertEquals(m.getGroupId(), rm.getGroupId()); + assertEquals(m.getTimestamp(), rm.getTimestamp()); + assertEquals(previousMsgId, rm.getPreviousMessageId()); + assertEquals(author, rm.getAuthor()); + assertEquals(text, rm.getMessage()); + } + + @Test + public void testRequestMessageWithPreviousMsgNull() throws FormatException { + Message m = messageEncoder + .encodeRequestMessage(groupId, timestamp, null, author, text); + RequestMessage rm = + messageParser.parseRequestMessage(m, clientHelper.toList(m)); + + assertNull(rm.getPreviousMessageId()); + } + + @Test + public void testRequestMessageWithMsgNull() throws FormatException { + Message m = messageEncoder + .encodeRequestMessage(groupId, timestamp, previousMsgId, author, + null); + RequestMessage rm = + messageParser.parseRequestMessage(m, clientHelper.toList(m)); + + assertNull(rm.getMessage()); + } + + @Test + public void testAcceptMessage() throws Exception { + Map<TransportId, TransportProperties> transportProperties = + getTransportPropertiesMap(2); + + long acceptTimestamp = 1337L; + Message m = messageEncoder + .encodeAcceptMessage(groupId, timestamp, previousMsgId, + sessionId, ephemeralPublicKey, acceptTimestamp, + transportProperties); + AcceptMessage rm = + messageParser.parseAcceptMessage(m, clientHelper.toList(m)); + + assertEquals(m.getId(), rm.getMessageId()); + assertEquals(m.getGroupId(), rm.getGroupId()); + assertEquals(m.getTimestamp(), rm.getTimestamp()); + assertEquals(previousMsgId, rm.getPreviousMessageId()); + assertEquals(sessionId, rm.getSessionId()); + assertArrayEquals(ephemeralPublicKey, rm.getEphemeralPublicKey()); + assertEquals(acceptTimestamp, rm.getAcceptTimestamp()); + assertEquals(transportProperties, rm.getTransportProperties()); + } + + @Test + public void testDeclineMessage() throws Exception { + Message m = messageEncoder + .encodeDeclineMessage(groupId, timestamp, previousMsgId, + sessionId); + DeclineMessage rm = + messageParser.parseDeclineMessage(m, clientHelper.toList(m)); + + assertEquals(m.getId(), rm.getMessageId()); + assertEquals(m.getGroupId(), rm.getGroupId()); + assertEquals(m.getTimestamp(), rm.getTimestamp()); + assertEquals(previousMsgId, rm.getPreviousMessageId()); + assertEquals(sessionId, rm.getSessionId()); + } + + @Test + public void testAuthMessage() throws Exception { + Message m = messageEncoder + .encodeAuthMessage(groupId, timestamp, previousMsgId, + sessionId, mac, signature); + AuthMessage rm = + messageParser.parseAuthMessage(m, clientHelper.toList(m)); + + assertEquals(m.getId(), rm.getMessageId()); + assertEquals(m.getGroupId(), rm.getGroupId()); + assertEquals(m.getTimestamp(), rm.getTimestamp()); + assertEquals(previousMsgId, rm.getPreviousMessageId()); + assertEquals(sessionId, rm.getSessionId()); + assertArrayEquals(mac, rm.getMac()); + assertArrayEquals(signature, rm.getSignature()); + } + + @Test + public void testActivateMessage() throws Exception { + Message m = messageEncoder + .encodeActivateMessage(groupId, timestamp, previousMsgId, + sessionId); + ActivateMessage rm = + messageParser.parseActivateMessage(m, clientHelper.toList(m)); + + assertEquals(m.getId(), rm.getMessageId()); + assertEquals(m.getGroupId(), rm.getGroupId()); + assertEquals(m.getTimestamp(), rm.getTimestamp()); + assertEquals(previousMsgId, rm.getPreviousMessageId()); + assertEquals(sessionId, rm.getSessionId()); + } + + @Test + public void testAbortMessage() throws Exception { + Message m = messageEncoder + .encodeAbortMessage(groupId, timestamp, previousMsgId, + sessionId); + AbortMessage rm = + messageParser.parseAbortMessage(m, clientHelper.toList(m)); + + assertEquals(m.getId(), rm.getMessageId()); + assertEquals(m.getGroupId(), rm.getGroupId()); + assertEquals(m.getTimestamp(), rm.getTimestamp()); + assertEquals(previousMsgId, rm.getPreviousMessageId()); + assertEquals(sessionId, rm.getSessionId()); + } + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction2/MessageEncoderTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction2/MessageEncoderTest.java new file mode 100644 index 0000000000..c7d60662d4 --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/introduction2/MessageEncoderTest.java @@ -0,0 +1,63 @@ +package org.briarproject.briar.introduction2; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.client.ClientHelper; +import org.briarproject.bramble.api.data.BdfList; +import org.briarproject.bramble.api.identity.Author; +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.bramble.test.BrambleMockTestCase; +import org.jmock.Expectations; +import org.junit.Test; + +import static org.briarproject.bramble.test.TestUtils.getAuthor; +import static org.briarproject.bramble.test.TestUtils.getRandomBytes; +import static org.briarproject.bramble.test.TestUtils.getRandomId; +import static org.briarproject.bramble.util.StringUtils.getRandomString; +import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH; +import static org.briarproject.briar.introduction2.MessageType.REQUEST; + +public class MessageEncoderTest extends BrambleMockTestCase { + + private final ClientHelper clientHelper = context.mock(ClientHelper.class); + private final MessageFactory messageFactory = + context.mock(MessageFactory.class); + private final MessageEncoder messageEncoder = + new MessageEncoderImpl(clientHelper, messageFactory); + + private final GroupId groupId = new GroupId(getRandomId()); + private final long timestamp = 42L; + private final Message message = + new Message(new MessageId(getRandomId()), groupId, timestamp, + getRandomBytes(48)); + private final byte[] body = getRandomBytes(42); + private final Author author = getAuthor(); + private final BdfList authorList = new BdfList(); + private final String text = + getRandomString(MAX_INTRODUCTION_MESSAGE_LENGTH); + + @Test + public void testEncodeRequestMessage() throws FormatException { + context.checking(new Expectations() {{ + oneOf(clientHelper).toList(author); + will(returnValue(authorList)); + }}); + expectCreateMessage( + BdfList.of(REQUEST.getValue(), null, authorList, text)); + + messageEncoder + .encodeRequestMessage(groupId, timestamp, null, author, text); + } + + private void expectCreateMessage(BdfList bodyList) throws FormatException { + context.checking(new Expectations() {{ + oneOf(clientHelper).toByteArray(bodyList); + will(returnValue(body)); + oneOf(messageFactory).createMessage(groupId, timestamp, body); + will(returnValue(message)); + }}); + } + +} 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 1b2344d3ad..1afcdea7bc 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 @@ -37,6 +37,7 @@ 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.introduction2.MessageEncoderParserIntegrationTest; import org.briarproject.briar.messaging.MessagingModule; import org.briarproject.briar.privategroup.PrivateGroupModule; import org.briarproject.briar.privategroup.invitation.GroupInvitationModule; @@ -76,6 +77,8 @@ public interface BriarIntegrationTestComponent { void inject(BriarIntegrationTest<BriarIntegrationTestComponent> init); + void inject(MessageEncoderParserIntegrationTest init); + void inject(BlogModule.EagerSingletons init); void inject(ContactModule.EagerSingletons init); diff --git a/briar-core/src/test/java/org/briarproject/briar/test/BriarTestUtils.java b/briar-core/src/test/java/org/briarproject/briar/test/BriarTestUtils.java index d29fc0b541..5de39e9f56 100644 --- a/briar-core/src/test/java/org/briarproject/briar/test/BriarTestUtils.java +++ b/briar-core/src/test/java/org/briarproject/briar/test/BriarTestUtils.java @@ -1,10 +1,15 @@ package org.briarproject.briar.test; import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.identity.AuthorFactory; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker.GroupCount; +import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; +import static org.briarproject.bramble.test.TestUtils.getRandomBytes; +import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.junit.Assert.assertEquals; public class BriarTestUtils { @@ -25,4 +30,9 @@ public class BriarTestUtils { assertEquals(unreadCount, c1.getUnreadCount()); } + public static Author getRealAuthor(AuthorFactory authorFactory) { + return authorFactory.createAuthor(getRandomString(5), + getRandomBytes(MAX_PUBLIC_KEY_LENGTH)); + } + } -- GitLab