mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 12:49:55 +01:00
Add one introduction test for modified response message
This commit is contained in:
@@ -1,7 +1,10 @@
|
|||||||
package org.briarproject;
|
package org.briarproject;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import net.jodah.concurrentunit.Waiter;
|
import net.jodah.concurrentunit.Waiter;
|
||||||
|
|
||||||
|
import org.briarproject.api.FormatException;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
import org.briarproject.api.clients.SessionId;
|
import org.briarproject.api.clients.SessionId;
|
||||||
import org.briarproject.api.contact.Contact;
|
import org.briarproject.api.contact.Contact;
|
||||||
@@ -35,6 +38,7 @@ import org.briarproject.api.properties.TransportProperties;
|
|||||||
import org.briarproject.api.properties.TransportPropertyManager;
|
import org.briarproject.api.properties.TransportPropertyManager;
|
||||||
import org.briarproject.api.sync.ClientId;
|
import org.briarproject.api.sync.ClientId;
|
||||||
import org.briarproject.api.sync.Group;
|
import org.briarproject.api.sync.Group;
|
||||||
|
import org.briarproject.api.sync.GroupId;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
import org.briarproject.api.sync.SyncSession;
|
import org.briarproject.api.sync.SyncSession;
|
||||||
import org.briarproject.api.sync.SyncSessionFactory;
|
import org.briarproject.api.sync.SyncSessionFactory;
|
||||||
@@ -59,6 +63,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -69,22 +74,15 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
||||||
import static org.briarproject.TestPluginsModule.TRANSPORT_ID;
|
import static org.briarproject.TestPluginsModule.TRANSPORT_ID;
|
||||||
|
import static org.briarproject.api.clients.MessageQueueManager.QUEUE_STATE_KEY;
|
||||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
|
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.MAC;
|
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.MAC_LENGTH;
|
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.SIGNATURE;
|
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TIME;
|
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
|
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
|
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||||
@@ -95,11 +93,13 @@ import static org.junit.Assert.assertTrue;
|
|||||||
|
|
||||||
public class IntroductionIntegrationTest extends BriarTestCase {
|
public class IntroductionIntegrationTest extends BriarTestCase {
|
||||||
|
|
||||||
private LifecycleManager lifecycleManager0, lifecycleManager1, lifecycleManager2;
|
private LifecycleManager lifecycleManager0, lifecycleManager1,
|
||||||
|
lifecycleManager2;
|
||||||
private SyncSessionFactory sync0, sync1, sync2;
|
private SyncSessionFactory sync0, sync1, sync2;
|
||||||
private ContactManager contactManager0, contactManager1, contactManager2;
|
private ContactManager contactManager0, contactManager1, contactManager2;
|
||||||
private ContactId contactId0, contactId1, contactId2;
|
private ContactId contactId0, contactId1, contactId2;
|
||||||
private IdentityManager identityManager0, identityManager1, identityManager2;
|
private IdentityManager identityManager0, identityManager1,
|
||||||
|
identityManager2;
|
||||||
private LocalAuthor author0, author1, author2;
|
private LocalAuthor author0, author1, author2;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -108,6 +108,8 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
CryptoComponent crypto;
|
CryptoComponent crypto;
|
||||||
@Inject
|
@Inject
|
||||||
AuthorFactory authorFactory;
|
AuthorFactory authorFactory;
|
||||||
|
@Inject
|
||||||
|
IntroductionGroupFactory introductionGroupFactory;
|
||||||
|
|
||||||
// objects accessed from background threads need to be volatile
|
// objects accessed from background threads need to be volatile
|
||||||
private volatile IntroductionManager introductionManager0;
|
private volatile IntroductionManager introductionManager0;
|
||||||
@@ -834,7 +836,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFakeResponse() throws Exception {
|
public void testModifiedResponse() throws Exception {
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
try {
|
try {
|
||||||
addDefaultIdentities();
|
addDefaultIdentities();
|
||||||
@@ -856,122 +858,106 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
introductionManager0
|
introductionManager0
|
||||||
.makeIntroduction(introducee1, introducee2, "Hi!", time);
|
.makeIntroduction(introducee1, introducee2, "Hi!", time);
|
||||||
|
|
||||||
// sync first request message
|
// sync request messages
|
||||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||||
eventWaiter.await(TIMEOUT, 1);
|
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||||
assertTrue(listener1.requestReceived);
|
eventWaiter.await(TIMEOUT, 2);
|
||||||
|
|
||||||
// sync first response
|
// sync first response
|
||||||
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
||||||
eventWaiter.await(TIMEOUT, 1);
|
eventWaiter.await(TIMEOUT, 1);
|
||||||
assertTrue(listener0.response1Received);
|
|
||||||
|
|
||||||
// get SessionId
|
// get response to be forwarded
|
||||||
List<IntroductionMessage> list = new ArrayList<>(
|
MessageId responseId = null;
|
||||||
introductionManager1.getIntroductionMessages(contactId0));
|
BdfDictionary response = null;
|
||||||
assertEquals(2, list.size());
|
Group g2 = introductionGroupFactory
|
||||||
assertTrue(list.get(0) instanceof IntroductionRequest);
|
.createIntroductionGroup(introducee2);
|
||||||
IntroductionRequest msg = (IntroductionRequest) list.get(0);
|
ClientHelper clientHelper0 = t0.getClientHelper();
|
||||||
SessionId sessionId = msg.getSessionId();
|
Map<MessageId, BdfDictionary> map =
|
||||||
|
clientHelper0.getMessageMetadataAsDictionary(g2.getId());
|
||||||
|
for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
|
||||||
|
if (entry.getValue().getLong(TYPE) == TYPE_RESPONSE) {
|
||||||
|
responseId = entry.getKey();
|
||||||
|
response = entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(responseId != null && response != null);
|
||||||
|
|
||||||
// get contact group
|
// adapt outgoing message queue to removed message
|
||||||
IntroductionGroupFactory groupFactory =
|
decreaseOutgoingMessageCounter(clientHelper0, g2.getId(), 1);
|
||||||
t0.getIntroductionGroupFactory();
|
|
||||||
Group group = groupFactory.createIntroductionGroup(introducee1);
|
|
||||||
|
|
||||||
// get data for contact2
|
// modify response by changing transport properties
|
||||||
long timestamp = clock.currentTimeMillis();
|
BdfDictionary tp = response.getDictionary(TRANSPORT);
|
||||||
KeyPair eKeyPair = crypto.generateAgreementKeyPair();
|
tp.put("fakeId", BdfDictionary.of(new BdfEntry("fake", "fake")));
|
||||||
byte[] ePublicKey = eKeyPair.getPublic().getEncoded();
|
response.put(TRANSPORT, tp);
|
||||||
TransportProperties tp = new TransportProperties(
|
|
||||||
Collections.singletonMap("key", "value"));
|
|
||||||
BdfDictionary tpDict = BdfDictionary.of(new BdfEntry("fake", tp));
|
|
||||||
|
|
||||||
// create a fake response
|
// replace original response with modified one
|
||||||
BdfDictionary d = BdfDictionary.of(
|
|
||||||
new BdfEntry(TYPE, TYPE_RESPONSE),
|
|
||||||
new BdfEntry(SESSION_ID, sessionId),
|
|
||||||
new BdfEntry(GROUP_ID, group.getId()),
|
|
||||||
new BdfEntry(ACCEPT, true),
|
|
||||||
new BdfEntry(TIME, timestamp),
|
|
||||||
new BdfEntry(E_PUBLIC_KEY, ePublicKey),
|
|
||||||
new BdfEntry(TRANSPORT, tpDict)
|
|
||||||
);
|
|
||||||
|
|
||||||
// add the message to the queue
|
|
||||||
DatabaseComponent db0 = t0.getDatabaseComponent();
|
|
||||||
MessageSender sender0 = t0.getMessageSender();
|
MessageSender sender0 = t0.getMessageSender();
|
||||||
|
DatabaseComponent db0 = t0.getDatabaseComponent();
|
||||||
Transaction txn = db0.startTransaction(false);
|
Transaction txn = db0.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
sender0.sendMessage(txn, d);
|
db0.deleteMessage(txn, responseId);
|
||||||
|
sender0.sendMessage(txn, response);
|
||||||
txn.setComplete();
|
txn.setComplete();
|
||||||
} finally {
|
} finally {
|
||||||
db0.endTransaction(txn);
|
db0.endTransaction(txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// send the fake response
|
// sync second response
|
||||||
|
deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
|
||||||
|
eventWaiter.await(TIMEOUT, 1);
|
||||||
|
|
||||||
|
// sync forwarded responses to introducees
|
||||||
|
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||||
|
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||||
|
|
||||||
|
// sync first ACK and its forward
|
||||||
|
deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
|
||||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||||
|
|
||||||
// fake session state for introducer, so she doesn't abort
|
// sync second ACK and forward it
|
||||||
ClientHelper clientHelper0 = t0.getClientHelper();
|
|
||||||
BdfDictionary state =
|
|
||||||
clientHelper0.getMessageMetadataAsDictionary(sessionId);
|
|
||||||
state.put(STATE, IntroducerProtocolState.AWAIT_ACKS.getValue());
|
|
||||||
clientHelper0.mergeMessageMetadata(sessionId, state);
|
|
||||||
|
|
||||||
// sync back the ACK
|
|
||||||
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
||||||
|
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||||
|
|
||||||
// create a fake ACK
|
// introducee2 should have detected the fake now
|
||||||
// TODO do we need to actually calculate a MAC and signature here?
|
// and deleted introducee1 again
|
||||||
byte[] mac = TestUtils.getRandomBytes(MAC_LENGTH);
|
Collection<Contact> contacts2;
|
||||||
byte[] sig = TestUtils.getRandomBytes(MAX_SIGNATURE_LENGTH);
|
DatabaseComponent db2 = t2.getDatabaseComponent();
|
||||||
d = BdfDictionary.of(
|
txn = db2.startTransaction(true);
|
||||||
new BdfEntry(TYPE, TYPE_ACK),
|
|
||||||
new BdfEntry(SESSION_ID, sessionId),
|
|
||||||
new BdfEntry(GROUP_ID, group.getId()),
|
|
||||||
new BdfEntry(MAC, mac),
|
|
||||||
new BdfEntry(SIGNATURE, sig)
|
|
||||||
);
|
|
||||||
|
|
||||||
// add the fake ACK to the message queue
|
|
||||||
txn = db0.startTransaction(false);
|
|
||||||
try {
|
try {
|
||||||
sender0.sendMessage(txn, d);
|
contacts2 = db2.getContacts(txn);
|
||||||
txn.setComplete();
|
txn.setComplete();
|
||||||
} finally {
|
} finally {
|
||||||
db0.endTransaction(txn);
|
db2.endTransaction(txn);
|
||||||
}
|
}
|
||||||
|
assertEquals(1, contacts2.size());
|
||||||
|
|
||||||
// make sure the contact was already added (as inactive)
|
// sync abort message to introducer
|
||||||
|
deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
|
||||||
|
|
||||||
|
// ensure introducer got the abort
|
||||||
|
SessionId sessionId = new SessionId(response.getRaw(SESSION_ID));
|
||||||
|
BdfDictionary state =
|
||||||
|
clientHelper0.getMessageMetadataAsDictionary(sessionId);
|
||||||
|
assertEquals(IntroducerProtocolState.ERROR.getValue(),
|
||||||
|
state.getLong(STATE).intValue());
|
||||||
|
|
||||||
|
// sync abort messages to introducees
|
||||||
|
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||||
|
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||||
|
|
||||||
|
// although aborted, introducee1 keeps the contact,
|
||||||
|
// so introducer can not make contacts disappear by sending abort
|
||||||
|
Collection<Contact> contacts1;
|
||||||
DatabaseComponent db1 = t1.getDatabaseComponent();
|
DatabaseComponent db1 = t1.getDatabaseComponent();
|
||||||
txn = db1.startTransaction(true);
|
txn = db1.startTransaction(true);
|
||||||
try {
|
try {
|
||||||
assertEquals(2, db1.getContacts(txn).size());
|
contacts1 = db1.getContacts(txn);
|
||||||
txn.setComplete();
|
txn.setComplete();
|
||||||
} finally {
|
} finally {
|
||||||
db1.endTransaction(txn);
|
db1.endTransaction(txn);
|
||||||
}
|
}
|
||||||
|
assertEquals(2, contacts1.size());
|
||||||
// send the fake ACK
|
|
||||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
|
||||||
|
|
||||||
// make sure session was aborted and contact deleted again
|
|
||||||
txn = db1.startTransaction(true);
|
|
||||||
try {
|
|
||||||
assertEquals(1, db1.getContacts(txn).size());
|
|
||||||
txn.setComplete();
|
|
||||||
} finally {
|
|
||||||
db1.endTransaction(txn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// there should now be an abort message to sync back
|
|
||||||
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
|
||||||
|
|
||||||
// ensure introducer got the abort
|
|
||||||
state = clientHelper0.getMessageMetadataAsDictionary(sessionId);
|
|
||||||
assertEquals(IntroducerProtocolState.ERROR.getValue(),
|
|
||||||
state.getLong(STATE).intValue());
|
|
||||||
} finally {
|
} finally {
|
||||||
stopLifecycles();
|
stopLifecycles();
|
||||||
}
|
}
|
||||||
@@ -1067,7 +1053,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||||
SyncSessionFactory toSync, ContactId toId, String debug)
|
SyncSessionFactory toSync, ContactId toId, @Nullable String debug)
|
||||||
throws IOException, TimeoutException {
|
throws IOException, TimeoutException {
|
||||||
|
|
||||||
if (debug != null) LOG.info("TEST: Sending message from " + debug);
|
if (debug != null) LOG.info("TEST: Sending message from " + debug);
|
||||||
@@ -1214,6 +1200,16 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void decreaseOutgoingMessageCounter(ClientHelper clientHelper,
|
||||||
|
GroupId g, int num) throws FormatException, DbException {
|
||||||
|
BdfDictionary gD = clientHelper.getGroupMetadataAsDictionary(g);
|
||||||
|
LOG.warning(gD.toString());
|
||||||
|
BdfDictionary queue = gD.getDictionary(QUEUE_STATE_KEY);
|
||||||
|
queue.put("nextOut", queue.getLong("nextOut") - num);
|
||||||
|
gD.put(QUEUE_STATE_KEY, queue);
|
||||||
|
clientHelper.mergeGroupMetadata(g, gD);
|
||||||
|
}
|
||||||
|
|
||||||
private void injectEagerSingletons(
|
private void injectEagerSingletons(
|
||||||
IntroductionIntegrationTestComponent component) {
|
IntroductionIntegrationTestComponent component) {
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.api.introduction;
|
|||||||
import static org.briarproject.api.introduction.IntroducerAction.ACK_1;
|
import static org.briarproject.api.introduction.IntroducerAction.ACK_1;
|
||||||
import static org.briarproject.api.introduction.IntroducerAction.ACK_2;
|
import static org.briarproject.api.introduction.IntroducerAction.ACK_2;
|
||||||
import static org.briarproject.api.introduction.IntroducerAction.LOCAL_REQUEST;
|
import static org.briarproject.api.introduction.IntroducerAction.LOCAL_REQUEST;
|
||||||
|
import static org.briarproject.api.introduction.IntroducerAction.REMOTE_ABORT;
|
||||||
import static org.briarproject.api.introduction.IntroducerAction.REMOTE_ACCEPT_1;
|
import static org.briarproject.api.introduction.IntroducerAction.REMOTE_ACCEPT_1;
|
||||||
import static org.briarproject.api.introduction.IntroducerAction.REMOTE_ACCEPT_2;
|
import static org.briarproject.api.introduction.IntroducerAction.REMOTE_ACCEPT_2;
|
||||||
import static org.briarproject.api.introduction.IntroducerAction.REMOTE_DECLINE_1;
|
import static org.briarproject.api.introduction.IntroducerAction.REMOTE_DECLINE_1;
|
||||||
@@ -65,7 +66,13 @@ public enum IntroducerProtocolState {
|
|||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FINISHED(8);
|
FINISHED(8) {
|
||||||
|
@Override
|
||||||
|
public IntroducerProtocolState next(IntroducerAction a) {
|
||||||
|
if (a == REMOTE_ABORT) return ERROR;
|
||||||
|
return FINISHED;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final int value;
|
private final int value;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user