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.
This commit is contained in:
Torsten Grote
2018-04-26 11:18:04 -03:00
parent 0a5d408686
commit 0e04044ebb
8 changed files with 328 additions and 158 deletions

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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";

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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 =

View File

@@ -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)