diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java index 8bb5781eb5d6afe21859bd69f323e960b32abb95..44714170064ecda2ff3afa2b60d51577292cbf67 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java @@ -416,27 +416,27 @@ class IntroduceeProtocolEngine } catch (GeneralSecurityException e) { return abort(txn, s); } + long timestamp = + Math.min(s.getAcceptTimestamp(), s.getRemoteAcceptTimestamp()); + if (timestamp == -1) throw new AssertionError(); + Map<TransportId, KeySetId> keys = null; try { ContactId c = contactManager .addContact(txn, s.getRemoteAuthor(), localAuthor.getId(), false, false); - //noinspection ConstantConditions + if (s.getRemoteTransportProperties() == null || + s.getMasterKey() == null) throw new AssertionError(); transportPropertyManager.addRemoteProperties(txn, c, s.getRemoteTransportProperties()); + keys = keyManager + .addUnboundKeys(txn, new SecretKey(s.getMasterKey()), + timestamp, isAlice(txn, s)); } catch (ContactExistsException e) { - // TODO + // Ignore this and continue without adding transport properties + // or unbound transport keys. Continue with keys as null. } - long timestamp = - Math.min(s.getAcceptTimestamp(), s.getRemoteAcceptTimestamp()); - if (timestamp == -1) throw new AssertionError(); - - //noinspection ConstantConditions - Map<TransportId, KeySetId> keys = keyManager - .addUnboundKeys(txn, new SecretKey(s.getMasterKey()), timestamp, - isAlice(txn, s)); - Message sent = sendActivateMessage(txn, s, getLocalTimestamp(s)); // Move to AWAIT_ACTIVATE state and clear key material from session @@ -449,17 +449,22 @@ class IntroduceeProtocolEngine if (isInvalidDependency(s, m.getPreviousMessageId())) return abort(txn, s); - Contact c = contactManager.getContact(txn, s.getRemoteAuthor().getId(), - identityManager.getLocalAuthor(txn).getId()); - keyManager.bindKeys(txn, c.getId(), s.getTransportKeys()); - keyManager.activateKeys(txn, s.getTransportKeys()); - - // TODO remove when concept of inactive contacts is removed - contactManager.setContactActive(txn, c.getId(), true); - - // Broadcast IntroductionSucceededEvent - IntroductionSucceededEvent e = new IntroductionSucceededEvent(c); - txn.attach(e); + // Only bind keys if contact did not exist during AUTH + if (s.getTransportKeys() != null) { + Contact c = + contactManager.getContact(txn, s.getRemoteAuthor().getId(), + identityManager.getLocalAuthor(txn).getId()); + keyManager.bindKeys(txn, c.getId(), s.getTransportKeys()); + keyManager.activateKeys(txn, s.getTransportKeys()); + + // TODO remove when concept of inactive contacts is removed + contactManager.setContactActive(txn, c.getId(), true); + + // TODO move this to AUTH step when concept of inactive contacts is removed + // Broadcast IntroductionSucceededEvent + IntroductionSucceededEvent e = new IntroductionSucceededEvent(c); + txn.attach(e); + } // Move back to START state return IntroduceeSession diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java index 1b44452d972b1407591d6c716ff4788f805d771a..1aadc39ad564fe22c44f6a111fda939f3146ee6d 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java @@ -132,7 +132,7 @@ class IntroduceeSession extends Session<IntroduceeState> } static IntroduceeSession awaitActivate(IntroduceeSession s, AuthMessage m, - Message sent, Map<TransportId, KeySetId> transportKeys) { + Message sent, @Nullable Map<TransportId, KeySetId> transportKeys) { return new IntroduceeSession(s.getSessionId(), AWAIT_ACTIVATE, s.getRequestTimestamp(), s.contactGroupId, sent.getId(), sent.getTimestamp(), m.getMessageId(), s.introducer, null, null, diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java index 880210039adb938baefdba797482b1358373d40e..9c410e238627d6b69183a4b92aba6fece19c8373 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java @@ -20,6 +20,7 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.sync.Group; +import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.test.TestDatabaseModule; @@ -61,6 +62,8 @@ import static org.briarproject.briar.introduction.MessageType.ACCEPT; import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class IntroductionIntegrationTest @@ -166,11 +169,34 @@ public class IntroductionIntegrationTest sync1To0(1, true); sync0To2(1, true); + // assert that introducee2 added introducee1 + Contact contact1From2 = c2.getContactManager() + .getContact(author1.getId(), author2.getId()); + + // assert that introducee2 did add transport properties + // TODO check when notion of inactive contacts has been removed +// TransportProperties tp2 = c2.getTransportPropertyManager() +// .getRemoteProperties(contact1From2.getId(), TRANSPORT_ID); +// assertFalse(tp2.isEmpty()); + + // assert that introducee2 did add the transport keys + IntroduceeSession session2 = getIntroduceeSession(c2.getClientHelper(), + introductionManager2.getContactGroup(contact0From2).getId()); + assertNotNull(session2.getTransportKeys()); + assertFalse(session2.getTransportKeys().isEmpty()); + // sync second AUTH and its forward as well as the following ACTIVATE sync2To0(2, true); sync0To1(2, true); - // sync first ACTIVATE and its forward + // assert that introducee1 really purged the key material + IntroduceeSession session1 = getIntroduceeSession(c1.getClientHelper(), + introductionManager1.getContactGroup(contact0From1).getId()); + assertNull(session1.getMasterKey()); + assertNull(session1.getEphemeralPrivateKey()); + assertNull(session1.getTransportKeys()); + + // sync second ACTIVATE and its forward sync1To0(1, true); sync0To2(1, true); @@ -467,6 +493,71 @@ public class IntroductionIntegrationTest .makeIntroduction(contact1From0, contact2From0, null, time); } + @Test + public void testIntroductionToExistingContact() throws Exception { + // let contact1 and contact2 add each other already + addContacts1And2(); + assertNotNull(contactId2From1); + assertNotNull(contactId1From2); + + // both will still accept the introduction + 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); + + // assert that introducees get notified about the existing contact + IntroductionRequest ir1 = + getIntroductionRequest(introductionManager1, contactId0From1); + assertTrue(ir1.contactExists()); + IntroductionRequest ir2 = + getIntroductionRequest(introductionManager2, contactId0From2); + assertTrue(ir2.contactExists()); + + // sync ACCEPT messages back to introducer + sync1To0(1, true); + sync2To0(1, true); + + // sync forwarded ACCEPT messages to introducees + sync0To1(1, true); + sync0To2(1, true); + + // sync first AUTH and its forward + sync1To0(1, true); + sync0To2(1, true); + + // assert that introducee2 did not add any transport properties + TransportProperties tp2 = c2.getTransportPropertyManager() + .getRemoteProperties(contactId1From2, TRANSPORT_ID); + assertTrue(tp2.isEmpty()); + + // assert that introducee2 did not add any transport keys + IntroduceeSession session2 = getIntroduceeSession(c2.getClientHelper(), + introductionManager2.getContactGroup(contact0From2).getId()); + assertNull(session2.getTransportKeys()); + + // sync second AUTH and its forward as well as the following ACTIVATE + sync2To0(2, true); + sync0To1(2, true); + + // sync second ACTIVATE and its forward + sync1To0(1, true); + sync0To2(1, true); + + // assert that no session was aborted and no success event was broadcast + assertFalse(listener1.succeeded); + assertFalse(listener2.succeeded); + assertFalse(listener0.aborted); + assertFalse(listener1.aborted); + assertFalse(listener2.aborted); + } + @Test public void testIntroducerRemovedCleanup() throws Exception { addListeners(true, true); @@ -482,8 +573,7 @@ public class IntroductionIntegrationTest assertTrue(listener1.requestReceived); // get local group for introducee1 - Group group1 = - contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION); + Group group1 = getLocalGroup(); // check that we have one session state assertEquals(1, c1.getClientHelper() @@ -512,8 +602,7 @@ public class IntroductionIntegrationTest assertTrue(listener1.requestReceived); // get local group for introducer - Group group0 = - contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION); + Group group0 = getLocalGroup(); // check that we have one session state assertEquals(1, c0.getClientHelper() @@ -580,8 +669,7 @@ public class IntroductionIntegrationTest m.getTransportProperties()); c0.getClientHelper() .addLocalMessage(txn, msg, new BdfDictionary(), true); - Group group0 = contactGroupFactory - .createLocalGroup(CLIENT_ID, CLIENT_VERSION); + Group group0 = getLocalGroup(); BdfDictionary query = BdfDictionary.of( new BdfEntry(SESSION_KEY_SESSION_ID, m.getSessionId()) ); @@ -850,4 +938,29 @@ public class IntroductionIntegrationTest return c0.getMessageParser().parseAcceptMessage(m, body); } + private IntroductionRequest getIntroductionRequest( + IntroductionManager manager, ContactId contactId) + throws DbException { + for (IntroductionMessage im : manager + .getIntroductionMessages(contactId)) { + if (im instanceof IntroductionRequest) { + return (IntroductionRequest) im; + } + } + throw new AssertionError("No IntroductionRequest found"); + } + + private IntroduceeSession getIntroduceeSession(ClientHelper ch, + GroupId introducerGroup) throws DbException, FormatException { + Map<MessageId, BdfDictionary> dicts = + ch.getMessageMetadataAsDictionary(getLocalGroup().getId()); + assertEquals(1, dicts.size()); + BdfDictionary d = dicts.values().iterator().next(); + return c0.getSessionParser().parseIntroduceeSession(introducerGroup, d); + } + + private Group getLocalGroup() { + return contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION); + } + } diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java index a46d37cbedd9836ac766eb9f8308c1df3d437777..b8d5dedaa47b69093f7cfef9c358f849fdf5b4c8 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java @@ -61,5 +61,6 @@ interface IntroductionIntegrationTestComponent MessageEncoder getMessageEncoder(); MessageParser getMessageParser(); + SessionParser getSessionParser(); } diff --git a/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java index a76abd0570c8878dc87859ad913debed207199a7..f918b0a2d8fa02a9388ceaa920d2cfd5d890375c 100644 --- a/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java +++ b/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java @@ -9,6 +9,7 @@ import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.sync.SyncSessionFactory; +import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.client.ClientModule; import org.briarproject.bramble.contact.ContactModule; import org.briarproject.bramble.crypto.CryptoModule; @@ -145,6 +146,8 @@ public interface BriarIntegrationTestComponent { TransportPropertyManager getTransportPropertyManager(); + KeyManager getKeyManager(); + AuthorFactory getAuthorFactory(); BlogFactory getBlogFactory();