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 { @Nullable String message, long timestamp) throws DbException {
// Send REQUEST messages // Send REQUEST messages
long localTimestamp = long localTimestamp =
Math.max(timestamp, getLocalTimestamp(s, s.getIntroducee1())); Math.max(timestamp, getLocalTimestamp(s, s.getIntroduceeA()));
Message sent1 = sendRequestMessage(txn, s.getIntroducee1(), Message sentA = sendRequestMessage(txn, s.getIntroduceeA(),
localTimestamp, s.getIntroducee2().author, message localTimestamp, s.getIntroduceeB().author, message
); );
Message sent2 = sendRequestMessage(txn, s.getIntroducee2(), Message sentB = sendRequestMessage(txn, s.getIntroduceeB(),
localTimestamp, s.getIntroducee1().author, message localTimestamp, s.getIntroduceeA().author, message
); );
// Track the messages // Track the messages
messageTracker.trackOutgoingMessage(txn, sent1); messageTracker.trackOutgoingMessage(txn, sentA);
messageTracker.trackOutgoingMessage(txn, sent2); messageTracker.trackOutgoingMessage(txn, sentB);
// Move to the AWAIT_RESPONSES state // Move to the AWAIT_RESPONSES state
Introducee introducee1 = new Introducee(s.getIntroducee1(), sent1); Introducee introduceeA = new Introducee(s.getIntroduceeA(), sentA);
Introducee introducee2 = new Introducee(s.getIntroducee2(), sent2); Introducee introduceeB = new Introducee(s.getIntroduceeB(), sentB);
return new IntroducerSession(s.getSessionId(), AWAIT_RESPONSES, return new IntroducerSession(s.getSessionId(), AWAIT_RESPONSES,
localTimestamp, introducee1, introducee2); localTimestamp, introduceeA, introduceeB);
} }
private IntroducerSession onRemoteAccept(Transaction txn, private IntroducerSession onRemoteAccept(Transaction txn,
@@ -225,6 +225,14 @@ class IntroducerProtocolEngine
// The dependency, if any, must be the last remote message // The dependency, if any, must be the last remote message
if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId())) if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId()))
return abort(txn, s); 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 // Mark the response visible in the UI
markMessageVisibleInUi(txn, m.getMessageId()); markMessageVisibleInUi(txn, m.getMessageId());
@@ -240,27 +248,24 @@ class IntroducerProtocolEngine
m.getAcceptTimestamp(), m.getTransportProperties(), m.getAcceptTimestamp(), m.getTransportProperties(),
false); false);
// Move to the next state // Create the next state
IntroducerState state = AWAIT_AUTHS; IntroducerState state = AWAIT_AUTHS;
Introducee introducee1, introducee2; Introducee introduceeA, introduceeB;
Contact c; if (senderIsAlice) {
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())) {
if (s.getState() == AWAIT_RESPONSES) state = AWAIT_RESPONSE_B; if (s.getState() == AWAIT_RESPONSES) state = AWAIT_RESPONSE_B;
introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId());
introducee2 = new Introducee(s.getIntroducee2(), sent); introduceeB = new Introducee(s.getIntroduceeB(), sent);
c = contactManager } else {
.getContact(txn, s.getIntroducee1().author.getId(), if (s.getState() == AWAIT_RESPONSES) state = AWAIT_RESPONSE_A;
identityManager.getLocalAuthor(txn).getId()); introduceeA = new Introducee(s.getIntroduceeA(), sent);
} else throw new AssertionError(); introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId());
}
// Broadcast IntroductionResponseReceivedEvent // Broadcast IntroductionResponseReceivedEvent
AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
Contact c = contactManager.getContact(txn,
senderIsAlice ? introduceeA.author.getId() :
introduceeB.author.getId(), localAuthorId);
IntroductionResponse request = IntroductionResponse request =
new IntroductionResponse(s.getSessionId(), m.getMessageId(), new IntroductionResponse(s.getSessionId(), m.getMessageId(),
m.getGroupId(), INTRODUCER, m.getTimestamp(), false, m.getGroupId(), INTRODUCER, m.getTimestamp(), false,
@@ -269,8 +274,14 @@ class IntroducerProtocolEngine
new IntroductionResponseReceivedEvent(c.getId(), request); new IntroductionResponseReceivedEvent(c.getId(), request);
txn.attach(e); txn.attach(e);
// Move to the next state
return new IntroducerSession(s.getSessionId(), 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, private IntroducerSession onRemoteDecline(Transaction txn,
@@ -281,6 +292,14 @@ class IntroducerProtocolEngine
// The dependency, if any, must be the last remote message // The dependency, if any, must be the last remote message
if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId())) if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId()))
return abort(txn, s); 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 // Mark the response visible in the UI
markMessageVisibleInUi(txn, m.getMessageId()); markMessageVisibleInUi(txn, m.getMessageId());
@@ -293,25 +312,21 @@ class IntroducerProtocolEngine
long timestamp = getLocalTimestamp(s, i); long timestamp = getLocalTimestamp(s, i);
Message sent = sendDeclineMessage(txn, i, timestamp, false); Message sent = sendDeclineMessage(txn, i, timestamp, false);
// Move to the START state // Update introducee state
Introducee introducee1, introducee2; Introducee introduceeA, introduceeB;
AuthorId localAuthorId =identityManager.getLocalAuthor(txn).getId(); if (senderIsAlice) {
Contact c; introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId());
if (i.equals(s.getIntroducee1())) { introduceeB = new Introducee(s.getIntroduceeB(), sent);
introducee1 = new Introducee(s.getIntroducee1(), sent); } else {
introducee2 = new Introducee(s.getIntroducee2(), m.getMessageId()); introduceeA = new Introducee(s.getIntroduceeA(), sent);
c = contactManager introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId());
.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();
// Broadcast IntroductionResponseReceivedEvent // Broadcast IntroductionResponseReceivedEvent
AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
Contact c = contactManager.getContact(txn,
senderIsAlice ? introduceeA.author.getId() :
introduceeB.author.getId(), localAuthorId);
IntroductionResponse request = IntroductionResponse request =
new IntroductionResponse(s.getSessionId(), m.getMessageId(), new IntroductionResponse(s.getSessionId(), m.getMessageId(),
m.getGroupId(), INTRODUCER, m.getTimestamp(), false, m.getGroupId(), INTRODUCER, m.getTimestamp(), false,
@@ -321,7 +336,7 @@ class IntroducerProtocolEngine
txn.attach(e); txn.attach(e);
return new IntroducerSession(s.getSessionId(), START, return new IntroducerSession(s.getSessionId(), START,
s.getRequestTimestamp(), introducee1, introducee2); s.getRequestTimestamp(), introduceeA, introduceeB);
} }
private IntroducerSession onRemoteResponseInStart(Transaction txn, private IntroducerSession onRemoteResponseInStart(Transaction txn,
@@ -341,20 +356,20 @@ class IntroducerProtocolEngine
.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false); .trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
Introducee i = getIntroducee(s, m.getGroupId()); Introducee i = getIntroducee(s, m.getGroupId());
Introducee introducee1, introducee2; Introducee introduceeA, introduceeB;
AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId(); AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
Contact c; Contact c;
if (i.equals(s.getIntroducee1())) { if (i.equals(s.getIntroduceeA())) {
introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId());
introducee2 = s.getIntroducee2(); introduceeB = s.getIntroduceeB();
c = contactManager c = contactManager
.getContact(txn, s.getIntroducee1().author.getId(), .getContact(txn, s.getIntroduceeA().author.getId(),
localAuthorId); localAuthorId);
} else if (i.equals(s.getIntroducee2())) { } else if (i.equals(s.getIntroduceeB())) {
introducee1 = s.getIntroducee1(); introduceeA = s.getIntroduceeA();
introducee2 = new Introducee(s.getIntroducee2(), m.getMessageId()); introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId());
c = contactManager c = contactManager
.getContact(txn, s.getIntroducee2().author.getId(), .getContact(txn, s.getIntroduceeB().author.getId(),
localAuthorId); localAuthorId);
} else throw new AssertionError(); } else throw new AssertionError();
@@ -369,7 +384,7 @@ class IntroducerProtocolEngine
txn.attach(e); txn.attach(e);
return new IntroducerSession(s.getSessionId(), START, return new IntroducerSession(s.getSessionId(), START,
s.getRequestTimestamp(), introducee1, introducee2); s.getRequestTimestamp(), introduceeA, introduceeB);
} }
private IntroducerSession onRemoteAuth(Transaction txn, private IntroducerSession onRemoteAuth(Transaction txn,
@@ -377,6 +392,14 @@ class IntroducerProtocolEngine
// The dependency, if any, must be the last remote message // The dependency, if any, must be the last remote message
if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId())) if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId()))
return abort(txn, s); 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 // Forward AUTH message
Introducee i = getOtherIntroducee(s, m.getGroupId()); Introducee i = getOtherIntroducee(s, m.getGroupId());
@@ -386,18 +409,18 @@ class IntroducerProtocolEngine
// Move to the next state // Move to the next state
IntroducerState state = AWAIT_ACTIVATES; IntroducerState state = AWAIT_ACTIVATES;
Introducee introducee1, introducee2; Introducee introduceeA, introduceeB;
if (i.equals(s.getIntroducee1())) { if (senderIsAlice) {
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())) {
if (s.getState() == AWAIT_AUTHS) state = AWAIT_AUTH_B; if (s.getState() == AWAIT_AUTHS) state = AWAIT_AUTH_B;
introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId());
introducee2 = new Introducee(s.getIntroducee2(), sent); introduceeB = new Introducee(s.getIntroduceeB(), sent);
} else throw new AssertionError(); } 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, return new IntroducerSession(s.getSessionId(), state,
s.getRequestTimestamp(), introducee1, introducee2); s.getRequestTimestamp(), introduceeA, introduceeB);
} }
private IntroducerSession onRemoteActivate(Transaction txn, private IntroducerSession onRemoteActivate(Transaction txn,
@@ -405,26 +428,34 @@ class IntroducerProtocolEngine
// The dependency, if any, must be the last remote message // The dependency, if any, must be the last remote message
if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId())) if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId()))
return abort(txn, s); 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()); Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i); long timestamp = getLocalTimestamp(s, i);
Message sent = sendActivateMessage(txn, i, timestamp); Message sent = sendActivateMessage(txn, i, timestamp);
// Move to the next state // Move to the next state
IntroducerState state = START; IntroducerState state = START;
Introducee introducee1, introducee2; Introducee introduceeA, introduceeB;
if (i.equals(s.getIntroducee1())) { if (senderIsAlice) {
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())) {
if (s.getState() == AWAIT_ACTIVATES) state = AWAIT_ACTIVATE_B; if (s.getState() == AWAIT_ACTIVATES) state = AWAIT_ACTIVATE_B;
introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId());
introducee2 = new Introducee(s.getIntroducee2(), sent); introduceeB = new Introducee(s.getIntroduceeB(), sent);
} else throw new AssertionError(); } 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, return new IntroducerSession(s.getSessionId(), state,
s.getRequestTimestamp(), introducee1, introducee2); s.getRequestTimestamp(), introduceeA, introduceeB);
} }
private IntroducerSession onRemoteAbort(Transaction txn, private IntroducerSession onRemoteAbort(Transaction txn,
@@ -438,16 +469,16 @@ class IntroducerProtocolEngine
txn.attach(new IntroductionAbortedEvent(s.getSessionId())); txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
// Reset the session back to initial state // Reset the session back to initial state
Introducee introducee1, introducee2; Introducee introduceeA, introduceeB;
if (i.equals(s.getIntroducee1())) { if (i.equals(s.getIntroduceeA())) {
introducee1 = new Introducee(s.getIntroducee1(), sent); introduceeA = new Introducee(s.getIntroduceeA(), sent);
introducee2 = new Introducee(s.getIntroducee2(), m.getMessageId()); introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId());
} else if (i.equals(s.getIntroducee2())) { } else if (i.equals(s.getIntroduceeB())) {
introducee1 = new Introducee(s.getIntroducee1(), m.getMessageId()); introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId());
introducee2 = new Introducee(s.getIntroducee2(), sent); introduceeB = new Introducee(s.getIntroduceeB(), sent);
} else throw new AssertionError(); } else throw new AssertionError();
return new IntroducerSession(s.getSessionId(), START, return new IntroducerSession(s.getSessionId(), START,
s.getRequestTimestamp(), introducee1, introducee2); s.getRequestTimestamp(), introduceeA, introduceeB);
} }
private IntroducerSession abort(Transaction txn, private IntroducerSession abort(Transaction txn,
@@ -456,28 +487,28 @@ class IntroducerProtocolEngine
txn.attach(new IntroductionAbortedEvent(s.getSessionId())); txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
// Send an ABORT message to both introducees // Send an ABORT message to both introducees
long timestamp1 = getLocalTimestamp(s, s.getIntroducee1()); long timestampA = getLocalTimestamp(s, s.getIntroduceeA());
Message sent1 = sendAbortMessage(txn, s.getIntroducee1(), timestamp1); Message sentA = sendAbortMessage(txn, s.getIntroduceeA(), timestampA);
long timestamp2 = getLocalTimestamp(s, s.getIntroducee2()); long timestampB = getLocalTimestamp(s, s.getIntroduceeB());
Message sent2 = sendAbortMessage(txn, s.getIntroducee2(), timestamp2); Message sentB = sendAbortMessage(txn, s.getIntroduceeB(), timestampB);
// Reset the session back to initial state // Reset the session back to initial state
Introducee introducee1 = new Introducee(s.getIntroducee1(), sent1); Introducee introduceeA = new Introducee(s.getIntroduceeA(), sentA);
Introducee introducee2 = new Introducee(s.getIntroducee2(), sent2); Introducee introduceeB = new Introducee(s.getIntroduceeB(), sentB);
return new IntroducerSession(s.getSessionId(), START, return new IntroducerSession(s.getSessionId(), START,
s.getRequestTimestamp(), introducee1, introducee2); s.getRequestTimestamp(), introduceeA, introduceeB);
} }
private Introducee getIntroducee(IntroducerSession s, GroupId g) { private Introducee getIntroducee(IntroducerSession s, GroupId g) {
if (s.getIntroducee1().groupId.equals(g)) return s.getIntroducee1(); if (s.getIntroduceeA().groupId.equals(g)) return s.getIntroduceeA();
else if (s.getIntroducee2().groupId.equals(g)) else if (s.getIntroduceeB().groupId.equals(g))
return s.getIntroducee2(); return s.getIntroduceeB();
else throw new AssertionError(); else throw new AssertionError();
} }
private Introducee getOtherIntroducee(IntroducerSession s, GroupId g) { private Introducee getOtherIntroducee(IntroducerSession s, GroupId g) {
if (s.getIntroducee1().groupId.equals(g)) return s.getIntroducee2(); if (s.getIntroduceeA().groupId.equals(g)) return s.getIntroduceeB();
else if (s.getIntroducee2().groupId.equals(g)) else if (s.getIntroduceeB().groupId.equals(g))
return s.getIntroducee1(); return s.getIntroduceeA();
else throw new AssertionError(); else throw new AssertionError();
} }

View File

@@ -17,21 +17,21 @@ import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
@NotNullByDefault @NotNullByDefault
class IntroducerSession extends Session<IntroducerState> { class IntroducerSession extends Session<IntroducerState> {
private final Introducee introducee1, introducee2; private final Introducee introduceeA, introduceeB;
IntroducerSession(SessionId sessionId, IntroducerState state, IntroducerSession(SessionId sessionId, IntroducerState state,
long requestTimestamp, Introducee introducee1, long requestTimestamp, Introducee introduceeA,
Introducee introducee2) { Introducee introduceeB) {
super(sessionId, state, requestTimestamp); super(sessionId, state, requestTimestamp);
this.introducee1 = introducee1; this.introduceeA = introduceeA;
this.introducee2 = introducee2; this.introduceeB = introduceeB;
} }
IntroducerSession(SessionId sessionId, GroupId groupId1, Author author1, IntroducerSession(SessionId sessionId, GroupId groupIdA, Author authorA,
GroupId groupId2, Author author2) { GroupId groupIdB, Author authorB) {
this(sessionId, IntroducerState.START, -1, this(sessionId, IntroducerState.START, -1,
new Introducee(sessionId, groupId1, author1), new Introducee(sessionId, groupIdA, authorA),
new Introducee(sessionId, groupId2, author2)); new Introducee(sessionId, groupIdB, authorB));
} }
@Override @Override
@@ -39,12 +39,12 @@ class IntroducerSession extends Session<IntroducerState> {
return INTRODUCER; return INTRODUCER;
} }
Introducee getIntroducee1() { Introducee getIntroduceeA() {
return introducee1; return introduceeA;
} }
Introducee getIntroducee2() { Introducee getIntroduceeB() {
return introducee2; return introduceeB;
} }
@Immutable @Immutable

View File

@@ -23,8 +23,8 @@ interface IntroductionConstants {
String SESSION_KEY_LAST_REMOTE_MESSAGE_ID = "lastRemoteMessageId"; String SESSION_KEY_LAST_REMOTE_MESSAGE_ID = "lastRemoteMessageId";
// Session Keys Introducer // Session Keys Introducer
String SESSION_KEY_INTRODUCEE_1 = "introducee1"; String SESSION_KEY_INTRODUCEE_A = "introduceeA";
String SESSION_KEY_INTRODUCEE_2 = "introducee2"; String SESSION_KEY_INTRODUCEE_B = "introduceeB";
String SESSION_KEY_GROUP_ID = "groupId"; String SESSION_KEY_GROUP_ID = "groupId";
String SESSION_KEY_AUTHOR = "author"; String SESSION_KEY_AUTHOR = "author";

View File

@@ -308,8 +308,15 @@ class IntroductionManagerImpl extends ConversationClientImpl
// This is the first request - create a new session // This is the first request - create a new session
GroupId groupId1 = getContactGroup(c1).getId(); GroupId groupId1 = getContactGroup(c1).getId();
GroupId groupId2 = getContactGroup(c2).getId(); GroupId groupId2 = getContactGroup(c2).getId();
session = new IntroducerSession(sessionId, groupId1, boolean alice = crypto.isAlice(c1.getAuthor().getId(),
c1.getAuthor(), groupId2, c2.getAuthor()); 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); storageId = createStorageId(txn);
} else { } else {
// An earlier request exists, so we already have a session // An earlier request exists, so we already have a session
@@ -425,10 +432,10 @@ class IntroductionManagerImpl extends ConversationClientImpl
IntroducerSession session = IntroducerSession session =
sessionParser.parseIntroducerSession(bdfSession); sessionParser.parseIntroducerSession(bdfSession);
sessionId = session.getSessionId(); sessionId = session.getSessionId();
if (contactGroupId.equals(session.getIntroducee1().groupId)) { if (contactGroupId.equals(session.getIntroduceeA().groupId)) {
author = session.getIntroducee2().author; author = session.getIntroduceeB().author;
} else { } else {
author = session.getIntroducee1().author; author = session.getIntroduceeA().author;
} }
} else if (role == INTRODUCEE) { } else if (role == INTRODUCEE) {
IntroduceeSession session = sessionParser IntroduceeSession session = sessionParser
@@ -465,10 +472,10 @@ class IntroductionManagerImpl extends ConversationClientImpl
sessionParser.parseIntroducerSession(bdfSession); sessionParser.parseIntroducerSession(bdfSession);
sessionId = session.getSessionId(); sessionId = session.getSessionId();
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn); LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
if (localAuthor.equals(session.getIntroducee1().author)) { if (localAuthor.equals(session.getIntroduceeA().author)) {
author = session.getIntroducee2().author; author = session.getIntroduceeB().author;
} else { } else {
author = session.getIntroducee1().author; author = session.getIntroduceeA().author;
} }
} else if (role == INTRODUCEE) { } else if (role == INTRODUCEE) {
IntroduceeSession session = sessionParser IntroduceeSession session = sessionParser
@@ -516,12 +523,12 @@ class IntroductionManagerImpl extends ConversationClientImpl
} catch (FormatException e) { } catch (FormatException e) {
throw new AssertionError(); throw new AssertionError();
} }
if (s.getIntroducee1().author.equals(c.getAuthor())) { if (s.getIntroduceeA().author.equals(c.getAuthor())) {
abortOrRemoveSessionWithIntroducee(txn, s, session.getKey(), abortOrRemoveSessionWithIntroducee(txn, s, session.getKey(),
s.getIntroducee2(), localAuthor); s.getIntroduceeB(), localAuthor);
} else if (s.getIntroducee2().author.equals(c.getAuthor())) { } else if (s.getIntroduceeB().author.equals(c.getAuthor())) {
abortOrRemoveSessionWithIntroducee(txn, s, session.getKey(), 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_PRIVATE_KEY;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_EPHEMERAL_PUBLIC_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_GROUP_ID;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_1; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_A;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_2; 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_INTRODUCER;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; 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; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
@@ -72,8 +72,8 @@ class SessionEncoderImpl implements SessionEncoder {
@Override @Override
public BdfDictionary encodeIntroducerSession(IntroducerSession s) { public BdfDictionary encodeIntroducerSession(IntroducerSession s) {
BdfDictionary d = encodeSession(s); BdfDictionary d = encodeSession(s);
d.put(SESSION_KEY_INTRODUCEE_1, encodeIntroducee(s.getIntroducee1())); d.put(SESSION_KEY_INTRODUCEE_A, encodeIntroducee(s.getIntroduceeA()));
d.put(SESSION_KEY_INTRODUCEE_2, encodeIntroducee(s.getIntroducee2())); d.put(SESSION_KEY_INTRODUCEE_B, encodeIntroducee(s.getIntroduceeB()));
return d; 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_PRIVATE_KEY;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_EPHEMERAL_PUBLIC_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_GROUP_ID;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_1; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_A;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_2; 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_INTRODUCER;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; 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; 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); SessionId sessionId = getSessionId(d);
IntroducerState state = IntroducerState.fromValue(getState(d)); IntroducerState state = IntroducerState.fromValue(getState(d));
long requestTimestamp = d.getLong(SESSION_KEY_REQUEST_TIMESTAMP); long requestTimestamp = d.getLong(SESSION_KEY_REQUEST_TIMESTAMP);
Introducee introducee1 = parseIntroducee(sessionId, Introducee introduceeA = parseIntroducee(sessionId,
d.getDictionary(SESSION_KEY_INTRODUCEE_1)); d.getDictionary(SESSION_KEY_INTRODUCEE_A));
Introducee introducee2 = parseIntroducee(sessionId, Introducee introduceeB = parseIntroducee(sessionId,
d.getDictionary(SESSION_KEY_INTRODUCEE_2)); d.getDictionary(SESSION_KEY_INTRODUCEE_B));
return new IntroducerSession(sessionId, state, requestTimestamp, return new IntroducerSession(sessionId, state, requestTimestamp,
introducee1, introducee2); introduceeA, introduceeB);
} }
private Introducee parseIntroducee(SessionId sessionId, BdfDictionary d) 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.api.introduction.IntroductionManager.CLIENT_VERSION;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE; 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_AUTHOR;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_1; import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_A;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_2; 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_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_SESSION_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.ACCEPT;
import static org.briarproject.briar.introduction.MessageType.AUTH;
import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount; import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@@ -610,6 +611,123 @@ public class IntroductionIntegrationTest
assertFalse(listener2.aborted); 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 @Test
public void testIntroducerRemovedCleanup() throws Exception { public void testIntroducerRemovedCleanup() throws Exception {
addListeners(true, true); addListeners(true, true);
@@ -955,8 +1073,8 @@ public class IntroductionIntegrationTest
private void replacePreviousLocalMessageId(Author author, private void replacePreviousLocalMessageId(Author author,
BdfDictionary d, MessageId id) throws FormatException { BdfDictionary d, MessageId id) throws FormatException {
BdfDictionary i1 = d.getDictionary(SESSION_KEY_INTRODUCEE_1); BdfDictionary i1 = d.getDictionary(SESSION_KEY_INTRODUCEE_A);
BdfDictionary i2 = d.getDictionary(SESSION_KEY_INTRODUCEE_2); BdfDictionary i2 = d.getDictionary(SESSION_KEY_INTRODUCEE_B);
Author a1 = clientHelper Author a1 = clientHelper
.parseAndValidateAuthor(i1.getList(SESSION_KEY_AUTHOR)); .parseAndValidateAuthor(i1.getList(SESSION_KEY_AUTHOR));
Author a2 = clientHelper Author a2 = clientHelper
@@ -964,10 +1082,10 @@ public class IntroductionIntegrationTest
if (a1.equals(author)) { if (a1.equals(author)) {
i1.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, id); 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)) { } else if (a2.equals(author)) {
i2.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, id); i2.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, id);
d.put(SESSION_KEY_INTRODUCEE_2, i2); d.put(SESSION_KEY_INTRODUCEE_B, i2);
} else { } else {
throw new AssertionError(); throw new AssertionError();
} }
@@ -986,8 +1104,13 @@ public class IntroductionIntegrationTest
MessageId id = map.entrySet().iterator().next().getKey(); MessageId id = map.entrySet().iterator().next().getKey();
Message m = ch.getMessage(id); Message m = ch.getMessage(id);
BdfList body = ch.getMessageAsList(id); BdfList body = ch.getMessageAsList(id);
//noinspection ConstantConditions if (type == ACCEPT) {
return c0.getMessageParser().parseAcceptMessage(m, body); //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( private IntroductionRequest getIntroductionRequest(
@@ -1002,6 +1125,15 @@ public class IntroductionIntegrationTest
throw new AssertionError("No IntroductionRequest found"); 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, private IntroduceeSession getIntroduceeSession(ClientHelper ch,
GroupId introducerGroup) throws DbException, FormatException { GroupId introducerGroup) throws DbException, FormatException {
Map<MessageId, BdfDictionary> dicts = Map<MessageId, BdfDictionary> dicts =

View File

@@ -102,8 +102,8 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
assertEquals(s1.getSessionId(), s2.getSessionId()); assertEquals(s1.getSessionId(), s2.getSessionId());
assertEquals(AWAIT_AUTHS, s1.getState()); assertEquals(AWAIT_AUTHS, s1.getState());
assertEquals(s1.getState(), s2.getState()); assertEquals(s1.getState(), s2.getState());
assertIntroduceeEquals(s1.getIntroducee1(), s2.getIntroducee1()); assertIntroduceeEquals(s1.getIntroduceeA(), s2.getIntroduceeA());
assertIntroduceeEquals(s1.getIntroducee2(), s2.getIntroducee2()); assertIntroduceeEquals(s1.getIntroduceeB(), s2.getIntroduceeB());
} }
@Test @Test
@@ -121,19 +121,19 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
BdfDictionary d = sessionEncoder.encodeIntroducerSession(s1); BdfDictionary d = sessionEncoder.encodeIntroducerSession(s1);
IntroducerSession s2 = sessionParser.parseIntroducerSession(d); IntroducerSession s2 = sessionParser.parseIntroducerSession(d);
assertNull(s1.getIntroducee1().lastLocalMessageId); assertNull(s1.getIntroduceeA().lastLocalMessageId);
assertEquals(s1.getIntroducee1().lastLocalMessageId, assertEquals(s1.getIntroduceeA().lastLocalMessageId,
s2.getIntroducee1().lastLocalMessageId); s2.getIntroduceeA().lastLocalMessageId);
assertNull(s1.getIntroducee1().lastRemoteMessageId); assertNull(s1.getIntroduceeA().lastRemoteMessageId);
assertEquals(s1.getIntroducee1().lastRemoteMessageId, assertEquals(s1.getIntroduceeA().lastRemoteMessageId,
s2.getIntroducee1().lastRemoteMessageId); s2.getIntroduceeA().lastRemoteMessageId);
assertNull(s1.getIntroducee2().lastLocalMessageId); assertNull(s1.getIntroduceeB().lastLocalMessageId);
assertEquals(s1.getIntroducee2().lastLocalMessageId, assertEquals(s1.getIntroduceeB().lastLocalMessageId,
s2.getIntroducee2().lastLocalMessageId); s2.getIntroduceeB().lastLocalMessageId);
assertNull(s1.getIntroducee2().lastRemoteMessageId); assertNull(s1.getIntroduceeB().lastRemoteMessageId);
assertEquals(s1.getIntroducee2().lastRemoteMessageId, assertEquals(s1.getIntroduceeB().lastRemoteMessageId,
s2.getIntroducee2().lastRemoteMessageId); s2.getIntroduceeB().lastRemoteMessageId);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)