From 0e04044ebb2b15d99cc9d82297bc38037a3c15bb Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Thu, 26 Apr 2018 11:18:04 -0300 Subject: [PATCH] Ensure that incoming messages are expected in the current state Previously, the introducer would process and forward invalid messages by the introducees. This commit adds the necessary checks and tests. --- .../IntroducerProtocolEngine.java | 227 ++++++++++-------- .../briar/introduction/IntroducerSession.java | 26 +- .../introduction/IntroductionConstants.java | 4 +- .../introduction/IntroductionManagerImpl.java | 31 ++- .../introduction/SessionEncoderImpl.java | 8 +- .../briar/introduction/SessionParserImpl.java | 14 +- .../IntroductionIntegrationTest.java | 148 +++++++++++- .../SessionEncoderParserIntegrationTest.java | 28 +-- 8 files changed, 328 insertions(+), 158 deletions(-) diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java index 98ad2fe53c..2fbe0cab8b 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java @@ -200,21 +200,21 @@ class IntroducerProtocolEngine @Nullable String message, long timestamp) throws DbException { // Send REQUEST messages long localTimestamp = - Math.max(timestamp, getLocalTimestamp(s, s.getIntroducee1())); - Message sent1 = sendRequestMessage(txn, s.getIntroducee1(), - localTimestamp, s.getIntroducee2().author, message + Math.max(timestamp, getLocalTimestamp(s, s.getIntroduceeA())); + Message sentA = sendRequestMessage(txn, s.getIntroduceeA(), + localTimestamp, s.getIntroduceeB().author, message ); - Message sent2 = sendRequestMessage(txn, s.getIntroducee2(), - localTimestamp, s.getIntroducee1().author, message + Message sentB = sendRequestMessage(txn, s.getIntroduceeB(), + localTimestamp, s.getIntroduceeA().author, message ); // Track the messages - messageTracker.trackOutgoingMessage(txn, sent1); - messageTracker.trackOutgoingMessage(txn, sent2); + messageTracker.trackOutgoingMessage(txn, sentA); + messageTracker.trackOutgoingMessage(txn, sentB); // Move to the AWAIT_RESPONSES state - Introducee introducee1 = new Introducee(s.getIntroducee1(), sent1); - Introducee introducee2 = new Introducee(s.getIntroducee2(), sent2); + Introducee introduceeA = new Introducee(s.getIntroduceeA(), sentA); + Introducee introduceeB = new Introducee(s.getIntroduceeB(), sentB); return new IntroducerSession(s.getSessionId(), AWAIT_RESPONSES, - localTimestamp, introducee1, introducee2); + localTimestamp, introduceeA, introduceeB); } private IntroducerSession onRemoteAccept(Transaction txn, @@ -225,6 +225,14 @@ class IntroducerProtocolEngine // The dependency, if any, must be the last remote message if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId())) return abort(txn, s); + // The message must be expected in the current state + boolean senderIsAlice = senderIsAlice(s, m); + if (s.getState() != AWAIT_RESPONSES) { + if (senderIsAlice && s.getState() != AWAIT_RESPONSE_A) + return abort(txn, s); + else if (!senderIsAlice && s.getState() != AWAIT_RESPONSE_B) + return abort(txn, s); + } // Mark the response visible in the UI markMessageVisibleInUi(txn, m.getMessageId()); @@ -240,27 +248,24 @@ class IntroducerProtocolEngine m.getAcceptTimestamp(), m.getTransportProperties(), false); - // Move to the next state + // Create the next state IntroducerState state = AWAIT_AUTHS; - Introducee introducee1, introducee2; - Contact c; - if (i.equals(s.getIntroducee1())) { - if (s.getState() == AWAIT_RESPONSES) state = AWAIT_RESPONSE_A; - introducee1 = new Introducee(s.getIntroducee1(), sent); - introducee2 = new Introducee(s.getIntroducee2(), m.getMessageId()); - c = contactManager - .getContact(txn, s.getIntroducee2().author.getId(), - identityManager.getLocalAuthor(txn).getId()); - } else if (i.equals(s.getIntroducee2())) { + Introducee introduceeA, introduceeB; + if (senderIsAlice) { if (s.getState() == AWAIT_RESPONSES) state = AWAIT_RESPONSE_B; - introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); - introducee2 = new Introducee(s.getIntroducee2(), sent); - c = contactManager - .getContact(txn, s.getIntroducee1().author.getId(), - identityManager.getLocalAuthor(txn).getId()); - } else throw new AssertionError(); + introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId()); + introduceeB = new Introducee(s.getIntroduceeB(), sent); + } else { + if (s.getState() == AWAIT_RESPONSES) state = AWAIT_RESPONSE_A; + introduceeA = new Introducee(s.getIntroduceeA(), sent); + introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId()); + } // Broadcast IntroductionResponseReceivedEvent + AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId(); + Contact c = contactManager.getContact(txn, + senderIsAlice ? introduceeA.author.getId() : + introduceeB.author.getId(), localAuthorId); IntroductionResponse request = new IntroductionResponse(s.getSessionId(), m.getMessageId(), m.getGroupId(), INTRODUCER, m.getTimestamp(), false, @@ -269,8 +274,14 @@ class IntroducerProtocolEngine new IntroductionResponseReceivedEvent(c.getId(), request); txn.attach(e); + // Move to the next state return new IntroducerSession(s.getSessionId(), state, - s.getRequestTimestamp(), introducee1, introducee2); + s.getRequestTimestamp(), introduceeA, introduceeB); + } + + private boolean senderIsAlice(IntroducerSession s, + AbstractIntroductionMessage m) { + return m.getGroupId().equals(s.getIntroduceeA().groupId); } private IntroducerSession onRemoteDecline(Transaction txn, @@ -281,6 +292,14 @@ class IntroducerProtocolEngine // The dependency, if any, must be the last remote message if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId())) return abort(txn, s); + // The message must be expected in the current state + boolean senderIsAlice = senderIsAlice(s, m); + if (s.getState() != AWAIT_RESPONSES) { + if (senderIsAlice && s.getState() != AWAIT_RESPONSE_A) + return abort(txn, s); + else if (!senderIsAlice && s.getState() != AWAIT_RESPONSE_B) + return abort(txn, s); + } // Mark the response visible in the UI markMessageVisibleInUi(txn, m.getMessageId()); @@ -293,25 +312,21 @@ class IntroducerProtocolEngine long timestamp = getLocalTimestamp(s, i); Message sent = sendDeclineMessage(txn, i, timestamp, false); - // Move to the START state - Introducee introducee1, introducee2; - AuthorId localAuthorId =identityManager.getLocalAuthor(txn).getId(); - Contact c; - if (i.equals(s.getIntroducee1())) { - introducee1 = new Introducee(s.getIntroducee1(), sent); - introducee2 = new Introducee(s.getIntroducee2(), m.getMessageId()); - c = contactManager - .getContact(txn, s.getIntroducee2().author.getId(), - localAuthorId); - } else if (i.equals(s.getIntroducee2())) { - introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); - introducee2 = new Introducee(s.getIntroducee2(), sent); - c = contactManager - .getContact(txn, s.getIntroducee1().author.getId(), - localAuthorId); - } else throw new AssertionError(); + // Update introducee state + Introducee introduceeA, introduceeB; + if (senderIsAlice) { + introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId()); + introduceeB = new Introducee(s.getIntroduceeB(), sent); + } else { + introduceeA = new Introducee(s.getIntroduceeA(), sent); + introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId()); + } // Broadcast IntroductionResponseReceivedEvent + AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId(); + Contact c = contactManager.getContact(txn, + senderIsAlice ? introduceeA.author.getId() : + introduceeB.author.getId(), localAuthorId); IntroductionResponse request = new IntroductionResponse(s.getSessionId(), m.getMessageId(), m.getGroupId(), INTRODUCER, m.getTimestamp(), false, @@ -321,7 +336,7 @@ class IntroducerProtocolEngine txn.attach(e); return new IntroducerSession(s.getSessionId(), START, - s.getRequestTimestamp(), introducee1, introducee2); + s.getRequestTimestamp(), introduceeA, introduceeB); } private IntroducerSession onRemoteResponseInStart(Transaction txn, @@ -341,20 +356,20 @@ class IntroducerProtocolEngine .trackMessage(txn, m.getGroupId(), m.getTimestamp(), false); Introducee i = getIntroducee(s, m.getGroupId()); - Introducee introducee1, introducee2; + Introducee introduceeA, introduceeB; AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId(); Contact c; - if (i.equals(s.getIntroducee1())) { - introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); - introducee2 = s.getIntroducee2(); + if (i.equals(s.getIntroduceeA())) { + introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId()); + introduceeB = s.getIntroduceeB(); c = contactManager - .getContact(txn, s.getIntroducee1().author.getId(), + .getContact(txn, s.getIntroduceeA().author.getId(), localAuthorId); - } else if (i.equals(s.getIntroducee2())) { - introducee1 = s.getIntroducee1(); - introducee2 = new Introducee(s.getIntroducee2(), m.getMessageId()); + } else if (i.equals(s.getIntroduceeB())) { + introduceeA = s.getIntroduceeA(); + introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId()); c = contactManager - .getContact(txn, s.getIntroducee2().author.getId(), + .getContact(txn, s.getIntroduceeB().author.getId(), localAuthorId); } else throw new AssertionError(); @@ -369,7 +384,7 @@ class IntroducerProtocolEngine txn.attach(e); return new IntroducerSession(s.getSessionId(), START, - s.getRequestTimestamp(), introducee1, introducee2); + s.getRequestTimestamp(), introduceeA, introduceeB); } private IntroducerSession onRemoteAuth(Transaction txn, @@ -377,6 +392,14 @@ class IntroducerProtocolEngine // The dependency, if any, must be the last remote message if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId())) return abort(txn, s); + // The message must be expected in the current state + boolean senderIsAlice = senderIsAlice(s, m); + if (s.getState() != AWAIT_AUTHS) { + if (senderIsAlice && s.getState() != AWAIT_AUTH_A) + return abort(txn, s); + else if (!senderIsAlice && s.getState() != AWAIT_AUTH_B) + return abort(txn, s); + } // Forward AUTH message Introducee i = getOtherIntroducee(s, m.getGroupId()); @@ -386,18 +409,18 @@ class IntroducerProtocolEngine // Move to the next state IntroducerState state = AWAIT_ACTIVATES; - Introducee introducee1, introducee2; - if (i.equals(s.getIntroducee1())) { - if (s.getState() == AWAIT_AUTHS) state = AWAIT_AUTH_A; - introducee1 = new Introducee(s.getIntroducee1(), sent); - introducee2 = new Introducee(s.getIntroducee2(), m.getMessageId()); - } else if (i.equals(s.getIntroducee2())) { + Introducee introduceeA, introduceeB; + if (senderIsAlice) { if (s.getState() == AWAIT_AUTHS) state = AWAIT_AUTH_B; - introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); - introducee2 = new Introducee(s.getIntroducee2(), sent); - } else throw new AssertionError(); + introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId()); + introduceeB = new Introducee(s.getIntroduceeB(), sent); + } else { + if (s.getState() == AWAIT_AUTHS) state = AWAIT_AUTH_A; + introduceeA = new Introducee(s.getIntroduceeA(), sent); + introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId()); + } return new IntroducerSession(s.getSessionId(), state, - s.getRequestTimestamp(), introducee1, introducee2); + s.getRequestTimestamp(), introduceeA, introduceeB); } private IntroducerSession onRemoteActivate(Transaction txn, @@ -405,26 +428,34 @@ class IntroducerProtocolEngine // The dependency, if any, must be the last remote message if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId())) return abort(txn, s); + // The message must be expected in the current state + boolean senderIsAlice = senderIsAlice(s, m); + if (s.getState() != AWAIT_ACTIVATES) { + if (senderIsAlice && s.getState() != AWAIT_ACTIVATE_A) + return abort(txn, s); + else if (!senderIsAlice && s.getState() != AWAIT_ACTIVATE_B) + return abort(txn, s); + } - // Forward AUTH message + // Forward ACTIVATE message Introducee i = getOtherIntroducee(s, m.getGroupId()); long timestamp = getLocalTimestamp(s, i); Message sent = sendActivateMessage(txn, i, timestamp); // Move to the next state IntroducerState state = START; - Introducee introducee1, introducee2; - if (i.equals(s.getIntroducee1())) { - if (s.getState() == AWAIT_ACTIVATES) state = AWAIT_ACTIVATE_A; - introducee1 = new Introducee(s.getIntroducee1(), sent); - introducee2 = new Introducee(s.getIntroducee2(), m.getMessageId()); - } else if (i.equals(s.getIntroducee2())) { + Introducee introduceeA, introduceeB; + if (senderIsAlice) { if (s.getState() == AWAIT_ACTIVATES) state = AWAIT_ACTIVATE_B; - introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); - introducee2 = new Introducee(s.getIntroducee2(), sent); - } else throw new AssertionError(); + introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId()); + introduceeB = new Introducee(s.getIntroduceeB(), sent); + } else { + if (s.getState() == AWAIT_ACTIVATES) state = AWAIT_ACTIVATE_A; + introduceeA = new Introducee(s.getIntroduceeA(), sent); + introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId()); + } return new IntroducerSession(s.getSessionId(), state, - s.getRequestTimestamp(), introducee1, introducee2); + s.getRequestTimestamp(), introduceeA, introduceeB); } private IntroducerSession onRemoteAbort(Transaction txn, @@ -438,16 +469,16 @@ class IntroducerProtocolEngine txn.attach(new IntroductionAbortedEvent(s.getSessionId())); // Reset the session back to initial state - Introducee introducee1, introducee2; - if (i.equals(s.getIntroducee1())) { - introducee1 = new Introducee(s.getIntroducee1(), sent); - introducee2 = new Introducee(s.getIntroducee2(), m.getMessageId()); - } else if (i.equals(s.getIntroducee2())) { - introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); - introducee2 = new Introducee(s.getIntroducee2(), sent); + Introducee introduceeA, introduceeB; + if (i.equals(s.getIntroduceeA())) { + introduceeA = new Introducee(s.getIntroduceeA(), sent); + introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId()); + } else if (i.equals(s.getIntroduceeB())) { + introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId()); + introduceeB = new Introducee(s.getIntroduceeB(), sent); } else throw new AssertionError(); return new IntroducerSession(s.getSessionId(), START, - s.getRequestTimestamp(), introducee1, introducee2); + s.getRequestTimestamp(), introduceeA, introduceeB); } private IntroducerSession abort(Transaction txn, @@ -456,28 +487,28 @@ class IntroducerProtocolEngine txn.attach(new IntroductionAbortedEvent(s.getSessionId())); // Send an ABORT message to both introducees - long timestamp1 = getLocalTimestamp(s, s.getIntroducee1()); - Message sent1 = sendAbortMessage(txn, s.getIntroducee1(), timestamp1); - long timestamp2 = getLocalTimestamp(s, s.getIntroducee2()); - Message sent2 = sendAbortMessage(txn, s.getIntroducee2(), timestamp2); + long timestampA = getLocalTimestamp(s, s.getIntroduceeA()); + Message sentA = sendAbortMessage(txn, s.getIntroduceeA(), timestampA); + long timestampB = getLocalTimestamp(s, s.getIntroduceeB()); + Message sentB = sendAbortMessage(txn, s.getIntroduceeB(), timestampB); // Reset the session back to initial state - Introducee introducee1 = new Introducee(s.getIntroducee1(), sent1); - Introducee introducee2 = new Introducee(s.getIntroducee2(), sent2); + Introducee introduceeA = new Introducee(s.getIntroduceeA(), sentA); + Introducee introduceeB = new Introducee(s.getIntroduceeB(), sentB); return new IntroducerSession(s.getSessionId(), START, - s.getRequestTimestamp(), introducee1, introducee2); + s.getRequestTimestamp(), introduceeA, introduceeB); } private Introducee getIntroducee(IntroducerSession s, GroupId g) { - if (s.getIntroducee1().groupId.equals(g)) return s.getIntroducee1(); - else if (s.getIntroducee2().groupId.equals(g)) - return s.getIntroducee2(); + if (s.getIntroduceeA().groupId.equals(g)) return s.getIntroduceeA(); + else if (s.getIntroduceeB().groupId.equals(g)) + return s.getIntroduceeB(); else throw new AssertionError(); } private Introducee getOtherIntroducee(IntroducerSession s, GroupId g) { - if (s.getIntroducee1().groupId.equals(g)) return s.getIntroducee2(); - else if (s.getIntroducee2().groupId.equals(g)) - return s.getIntroducee1(); + if (s.getIntroduceeA().groupId.equals(g)) return s.getIntroduceeB(); + else if (s.getIntroduceeB().groupId.equals(g)) + return s.getIntroduceeA(); else throw new AssertionError(); } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerSession.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerSession.java index 9069479692..c26eb26d98 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerSession.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerSession.java @@ -17,21 +17,21 @@ import static org.briarproject.briar.api.introduction.Role.INTRODUCER; @NotNullByDefault class IntroducerSession extends Session<IntroducerState> { - private final Introducee introducee1, introducee2; + private final Introducee introduceeA, introduceeB; IntroducerSession(SessionId sessionId, IntroducerState state, - long requestTimestamp, Introducee introducee1, - Introducee introducee2) { + long requestTimestamp, Introducee introduceeA, + Introducee introduceeB) { super(sessionId, state, requestTimestamp); - this.introducee1 = introducee1; - this.introducee2 = introducee2; + this.introduceeA = introduceeA; + this.introduceeB = introduceeB; } - IntroducerSession(SessionId sessionId, GroupId groupId1, Author author1, - GroupId groupId2, Author author2) { + IntroducerSession(SessionId sessionId, GroupId groupIdA, Author authorA, + GroupId groupIdB, Author authorB) { this(sessionId, IntroducerState.START, -1, - new Introducee(sessionId, groupId1, author1), - new Introducee(sessionId, groupId2, author2)); + new Introducee(sessionId, groupIdA, authorA), + new Introducee(sessionId, groupIdB, authorB)); } @Override @@ -39,12 +39,12 @@ class IntroducerSession extends Session<IntroducerState> { return INTRODUCER; } - Introducee getIntroducee1() { - return introducee1; + Introducee getIntroduceeA() { + return introduceeA; } - Introducee getIntroducee2() { - return introducee2; + Introducee getIntroduceeB() { + return introduceeB; } @Immutable diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java index af93fa955e..afb1ff3e70 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java @@ -23,8 +23,8 @@ interface IntroductionConstants { String SESSION_KEY_LAST_REMOTE_MESSAGE_ID = "lastRemoteMessageId"; // Session Keys Introducer - String SESSION_KEY_INTRODUCEE_1 = "introducee1"; - String SESSION_KEY_INTRODUCEE_2 = "introducee2"; + String SESSION_KEY_INTRODUCEE_A = "introduceeA"; + String SESSION_KEY_INTRODUCEE_B = "introduceeB"; String SESSION_KEY_GROUP_ID = "groupId"; String SESSION_KEY_AUTHOR = "author"; diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java index bfb81a4b8d..959d1d92bd 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java @@ -308,8 +308,15 @@ class IntroductionManagerImpl extends ConversationClientImpl // This is the first request - create a new session GroupId groupId1 = getContactGroup(c1).getId(); GroupId groupId2 = getContactGroup(c2).getId(); - session = new IntroducerSession(sessionId, groupId1, - c1.getAuthor(), groupId2, c2.getAuthor()); + boolean alice = crypto.isAlice(c1.getAuthor().getId(), + c2.getAuthor().getId()); + // use fixed deterministic roles for the introducees + session = new IntroducerSession(sessionId, + alice ? groupId1 : groupId2, + alice ? c1.getAuthor() : c2.getAuthor(), + alice ? groupId2 : groupId1, + alice ? c2.getAuthor() : c1.getAuthor() + ); storageId = createStorageId(txn); } else { // An earlier request exists, so we already have a session @@ -425,10 +432,10 @@ class IntroductionManagerImpl extends ConversationClientImpl IntroducerSession session = sessionParser.parseIntroducerSession(bdfSession); sessionId = session.getSessionId(); - if (contactGroupId.equals(session.getIntroducee1().groupId)) { - author = session.getIntroducee2().author; + if (contactGroupId.equals(session.getIntroduceeA().groupId)) { + author = session.getIntroduceeB().author; } else { - author = session.getIntroducee1().author; + author = session.getIntroduceeA().author; } } else if (role == INTRODUCEE) { IntroduceeSession session = sessionParser @@ -465,10 +472,10 @@ class IntroductionManagerImpl extends ConversationClientImpl sessionParser.parseIntroducerSession(bdfSession); sessionId = session.getSessionId(); LocalAuthor localAuthor = identityManager.getLocalAuthor(txn); - if (localAuthor.equals(session.getIntroducee1().author)) { - author = session.getIntroducee2().author; + if (localAuthor.equals(session.getIntroduceeA().author)) { + author = session.getIntroduceeB().author; } else { - author = session.getIntroducee1().author; + author = session.getIntroduceeA().author; } } else if (role == INTRODUCEE) { IntroduceeSession session = sessionParser @@ -516,12 +523,12 @@ class IntroductionManagerImpl extends ConversationClientImpl } catch (FormatException e) { throw new AssertionError(); } - if (s.getIntroducee1().author.equals(c.getAuthor())) { + if (s.getIntroduceeA().author.equals(c.getAuthor())) { abortOrRemoveSessionWithIntroducee(txn, s, session.getKey(), - s.getIntroducee2(), localAuthor); - } else if (s.getIntroducee2().author.equals(c.getAuthor())) { + s.getIntroduceeB(), localAuthor); + } else if (s.getIntroduceeB().author.equals(c.getAuthor())) { abortOrRemoveSessionWithIntroducee(txn, s, session.getKey(), - s.getIntroducee1(), localAuthor); + s.getIntroduceeA(), localAuthor); } } } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java index 9cf17a2d0f..2ffee61d88 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java @@ -23,8 +23,8 @@ import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_ import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_EPHEMERAL_PRIVATE_KEY; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_EPHEMERAL_PUBLIC_KEY; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_GROUP_ID; -import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_1; -import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_2; +import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_A; +import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_B; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCER; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID; @@ -72,8 +72,8 @@ class SessionEncoderImpl implements SessionEncoder { @Override public BdfDictionary encodeIntroducerSession(IntroducerSession s) { BdfDictionary d = encodeSession(s); - d.put(SESSION_KEY_INTRODUCEE_1, encodeIntroducee(s.getIntroducee1())); - d.put(SESSION_KEY_INTRODUCEE_2, encodeIntroducee(s.getIntroducee2())); + d.put(SESSION_KEY_INTRODUCEE_A, encodeIntroducee(s.getIntroduceeA())); + d.put(SESSION_KEY_INTRODUCEE_B, encodeIntroducee(s.getIntroduceeB())); return d; } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java index f269b49730..7c5b219259 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java @@ -27,8 +27,8 @@ import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_ import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_EPHEMERAL_PRIVATE_KEY; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_EPHEMERAL_PUBLIC_KEY; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_GROUP_ID; -import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_1; -import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_2; +import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_A; +import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_B; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCER; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID; @@ -75,12 +75,12 @@ class SessionParserImpl implements SessionParser { SessionId sessionId = getSessionId(d); IntroducerState state = IntroducerState.fromValue(getState(d)); long requestTimestamp = d.getLong(SESSION_KEY_REQUEST_TIMESTAMP); - Introducee introducee1 = parseIntroducee(sessionId, - d.getDictionary(SESSION_KEY_INTRODUCEE_1)); - Introducee introducee2 = parseIntroducee(sessionId, - d.getDictionary(SESSION_KEY_INTRODUCEE_2)); + Introducee introduceeA = parseIntroducee(sessionId, + d.getDictionary(SESSION_KEY_INTRODUCEE_A)); + Introducee introduceeB = parseIntroducee(sessionId, + d.getDictionary(SESSION_KEY_INTRODUCEE_B)); return new IntroducerSession(sessionId, state, requestTimestamp, - introducee1, introducee2); + introduceeA, introduceeB); } private Introducee parseIntroducee(SessionId sessionId, BdfDictionary d) diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java index 816957dd4a..1adfb9207e 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java @@ -54,11 +54,12 @@ import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION; import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_AUTHOR; -import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_1; -import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_2; +import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_A; +import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_B; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_SESSION_ID; import static org.briarproject.briar.introduction.MessageType.ACCEPT; +import static org.briarproject.briar.introduction.MessageType.AUTH; import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -610,6 +611,123 @@ public class IntroductionIntegrationTest assertFalse(listener2.aborted); } + /** + * One introducee illegally sends two ACCEPT messages in a row. + * The introducer should notice this and ABORT the session. + */ + @Test + public void testDoubleAccept() throws Exception { + addListeners(true, true); + + // make the introduction + long time = clock.currentTimeMillis(); + introductionManager0 + .makeIntroduction(contact1From0, contact2From0, null, time); + + // sync REQUEST to introducee1 + sync0To1(1, true); + + // save ACCEPT from introducee1 + AcceptMessage m = (AcceptMessage) getMessageFor(c1.getClientHelper(), + contact0From1, ACCEPT); + + // sync ACCEPT back to introducer + sync1To0(1, true); + + // fake a second ACCEPT message from introducee1 + Message msg = c1.getMessageEncoder() + .encodeAcceptMessage(m.getGroupId(), clock.currentTimeMillis(), + m.getMessageId(), m.getSessionId(), + m.getEphemeralPublicKey(), m.getAcceptTimestamp(), + m.getTransportProperties()); + c1.getClientHelper().addLocalMessage(msg, new BdfDictionary(), true); + + // sync fake ACCEPT back to introducer + sync1To0(1, true); + + assertTrue(listener0.aborted); + } + + /** + * One introducee sends an ACCEPT and then another DECLINE message. + * The introducer should notice this and ABORT the session. + */ + @Test + public void testAcceptAndDecline() throws Exception { + addListeners(true, true); + + // make the introduction + long time = clock.currentTimeMillis(); + introductionManager0 + .makeIntroduction(contact1From0, contact2From0, null, time); + + // sync REQUEST to introducee1 + sync0To1(1, true); + + // save ACCEPT from introducee1 + AcceptMessage m = (AcceptMessage) getMessageFor(c1.getClientHelper(), + contact0From1, ACCEPT); + + // sync ACCEPT back to introducer + sync1To0(1, true); + + // fake a second DECLINE message also from introducee1 + Message msg = c1.getMessageEncoder() + .encodeDeclineMessage(m.getGroupId(), clock.currentTimeMillis(), + m.getMessageId(), m.getSessionId()); + c1.getClientHelper().addLocalMessage(msg, new BdfDictionary(), true); + + // sync fake DECLINE back to introducer + sync1To0(1, true); + + assertTrue(listener0.aborted); + } + + /** + * One introducee sends two AUTH messages. + * The introducer should notice this and ABORT the session. + */ + @Test + public void testDoubleAuth() throws Exception { + addListeners(true, true); + + // make the introduction + long time = clock.currentTimeMillis(); + introductionManager0 + .makeIntroduction(contact1From0, contact2From0, null, time); + + // sync REQUEST messages + sync0To1(1, true); + sync0To2(1, true); + + // sync ACCEPT messages + sync1To0(1, true); + sync2To0(1, true); + + // sync forwarded ACCEPT messages to introducees + sync0To1(1, true); + sync0To2(1, true); + + // save AUTH from introducee1 + AuthMessage m = (AuthMessage) getMessageFor(c1.getClientHelper(), + contact0From1, AUTH); + + // sync first AUTH message + sync1To0(1, true); + + // fake a second AUTH message also from introducee1 + Message msg = c1.getMessageEncoder() + .encodeAuthMessage(m.getGroupId(), clock.currentTimeMillis(), + m.getMessageId(), m.getSessionId(), m.getMac(), + m.getSignature()); + c1.getClientHelper().addLocalMessage(msg, new BdfDictionary(), true); + + // sync second AUTH message + sync1To0(1, true); + + assertTrue(listener0.aborted); + } + @Test public void testIntroducerRemovedCleanup() throws Exception { addListeners(true, true); @@ -955,8 +1073,8 @@ public class IntroductionIntegrationTest private void replacePreviousLocalMessageId(Author author, BdfDictionary d, MessageId id) throws FormatException { - BdfDictionary i1 = d.getDictionary(SESSION_KEY_INTRODUCEE_1); - BdfDictionary i2 = d.getDictionary(SESSION_KEY_INTRODUCEE_2); + BdfDictionary i1 = d.getDictionary(SESSION_KEY_INTRODUCEE_A); + BdfDictionary i2 = d.getDictionary(SESSION_KEY_INTRODUCEE_B); Author a1 = clientHelper .parseAndValidateAuthor(i1.getList(SESSION_KEY_AUTHOR)); Author a2 = clientHelper @@ -964,10 +1082,10 @@ public class IntroductionIntegrationTest if (a1.equals(author)) { i1.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, id); - d.put(SESSION_KEY_INTRODUCEE_1, i1); + d.put(SESSION_KEY_INTRODUCEE_A, i1); } else if (a2.equals(author)) { i2.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, id); - d.put(SESSION_KEY_INTRODUCEE_2, i2); + d.put(SESSION_KEY_INTRODUCEE_B, i2); } else { throw new AssertionError(); } @@ -986,8 +1104,13 @@ public class IntroductionIntegrationTest MessageId id = map.entrySet().iterator().next().getKey(); Message m = ch.getMessage(id); BdfList body = ch.getMessageAsList(id); - //noinspection ConstantConditions - return c0.getMessageParser().parseAcceptMessage(m, body); + if (type == ACCEPT) { + //noinspection ConstantConditions + return c0.getMessageParser().parseAcceptMessage(m, body); + } else if (type == AUTH) { + //noinspection ConstantConditions + return c0.getMessageParser().parseAuthMessage(m, body); + } else throw new AssertionError("Not implemented"); } private IntroductionRequest getIntroductionRequest( @@ -1002,6 +1125,15 @@ public class IntroductionIntegrationTest throw new AssertionError("No IntroductionRequest found"); } + private IntroducerSession getIntroducerSession() + throws DbException, FormatException { + Map<MessageId, BdfDictionary> dicts = c0.getClientHelper() + .getMessageMetadataAsDictionary(getLocalGroup().getId()); + assertEquals(1, dicts.size()); + BdfDictionary d = dicts.values().iterator().next(); + return c0.getSessionParser().parseIntroducerSession(d); + } + private IntroduceeSession getIntroduceeSession(ClientHelper ch, GroupId introducerGroup) throws DbException, FormatException { Map<MessageId, BdfDictionary> dicts = diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java index 00bec26a83..9d773bac0c 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java @@ -102,8 +102,8 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase { assertEquals(s1.getSessionId(), s2.getSessionId()); assertEquals(AWAIT_AUTHS, s1.getState()); assertEquals(s1.getState(), s2.getState()); - assertIntroduceeEquals(s1.getIntroducee1(), s2.getIntroducee1()); - assertIntroduceeEquals(s1.getIntroducee2(), s2.getIntroducee2()); + assertIntroduceeEquals(s1.getIntroduceeA(), s2.getIntroduceeA()); + assertIntroduceeEquals(s1.getIntroduceeB(), s2.getIntroduceeB()); } @Test @@ -121,19 +121,19 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase { BdfDictionary d = sessionEncoder.encodeIntroducerSession(s1); IntroducerSession s2 = sessionParser.parseIntroducerSession(d); - assertNull(s1.getIntroducee1().lastLocalMessageId); - assertEquals(s1.getIntroducee1().lastLocalMessageId, - s2.getIntroducee1().lastLocalMessageId); - assertNull(s1.getIntroducee1().lastRemoteMessageId); - assertEquals(s1.getIntroducee1().lastRemoteMessageId, - s2.getIntroducee1().lastRemoteMessageId); + assertNull(s1.getIntroduceeA().lastLocalMessageId); + assertEquals(s1.getIntroduceeA().lastLocalMessageId, + s2.getIntroduceeA().lastLocalMessageId); + assertNull(s1.getIntroduceeA().lastRemoteMessageId); + assertEquals(s1.getIntroduceeA().lastRemoteMessageId, + s2.getIntroduceeA().lastRemoteMessageId); - assertNull(s1.getIntroducee2().lastLocalMessageId); - assertEquals(s1.getIntroducee2().lastLocalMessageId, - s2.getIntroducee2().lastLocalMessageId); - assertNull(s1.getIntroducee2().lastRemoteMessageId); - assertEquals(s1.getIntroducee2().lastRemoteMessageId, - s2.getIntroducee2().lastRemoteMessageId); + assertNull(s1.getIntroduceeB().lastLocalMessageId); + assertEquals(s1.getIntroduceeB().lastLocalMessageId, + s2.getIntroduceeB().lastLocalMessageId); + assertNull(s1.getIntroduceeB().lastRemoteMessageId); + assertEquals(s1.getIntroduceeB().lastRemoteMessageId, + s2.getIntroduceeB().lastRemoteMessageId); } @Test(expected = FormatException.class) -- GitLab