From 67593e2ad05c60cd0cb47f49334e037d70d93f16 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 22 Jun 2021 11:01:40 +0100 Subject: [PATCH] Add transport keys for reintroduced contact. --- .../IntroduceeProtocolEngine.java | 32 ++--- .../IntroductionIntegrationTest.java | 120 ++++++++++++++++++ 2 files changed, 134 insertions(+), 18 deletions(-) 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 7f55f1a2a..dd110a12b 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 @@ -4,6 +4,7 @@ import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ContactGroupFactory; import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.PrivateKey; @@ -449,31 +450,26 @@ class IntroduceeProtocolEngine s.getRemote().acceptTimestamp); if (timestamp == -1) throw new AssertionError(); - Map keys = null; + ContactId contactId; try { - contactManager.addContact(txn, s.getRemote().author, + contactId = contactManager.addContact(txn, s.getRemote().author, localAuthor.getId(), false); - - // Only add transport properties and keys when the contact was added - // This will be changed once we have a way to reset state for peers - // that were contacts already at some point in the past. - Contact c = contactManager.getContact(txn, - s.getRemote().author.getId(), localAuthor.getId()); - - // add the keys to the new contact - keys = keyManager.addRotationKeys(txn, c.getId(), - new SecretKey(s.getMasterKey()), timestamp, - s.getLocal().alice, false); - // add signed transport properties for the contact - transportPropertyManager.addRemoteProperties(txn, c.getId(), + transportPropertyManager.addRemoteProperties(txn, contactId, s.getRemote().transportProperties); } catch (ContactExistsException e) { - // Ignore this, because the other introducee might have deleted us. - // So we still want updated transport properties - // and new transport keys. + // The other introducee might have deleted us and been + // reintroduced. In that case we can ignore the transport + // properties, but we still want the new transport keys as the + // contact will no longer have the old keys + contactId = contactManager.getContact(txn, + s.getRemote().author.getId(), localAuthor.getId()).getId(); } + Map keys = keyManager.addRotationKeys(txn, + contactId, new SecretKey(s.getMasterKey()), timestamp, + s.getLocal().alice, false); + // send ACTIVATE message with a MAC byte[] mac = crypto.activateMac(s); long localTimestamp = getTimestampForInvisibleMessage(s); 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 952576c98..14e2a4dc1 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 @@ -1667,6 +1667,126 @@ public class IntroductionIntegrationTest assertTrue(deleteMessages0From1(emptySet()).allDeleted()); } + @Test + public void testReintroductionAfterBothIntroduceesDeleteEachOther() + throws Exception { + addListeners(true, true); + + // Introduce 1 to 2 + makeIntroduction(true); + updateContactIdsFor1And2(); + + // Sync client versions and transport properties - this shows that + // 1 and 2 share a set of transport keys + sync1To2(1, true); + sync2To1(1, true); + sync1To2(2, true); + sync2To1(1, true); + ack1To2(1); + + // Both introducees delete each other + contactManager1.removeContact(contactId2From1); + contactManager2.removeContact(contactId1From2); + + // Introduce 1 to 2 again + makeIntroduction(true); + updateContactIdsFor1And2(); + + // Sync client versions and transport properties again + sync1To2(1, true); + sync2To1(1, true); + sync1To2(2, true); + sync2To1(1, true); + ack1To2(1); + } + + @Test + public void testReintroductionAfterOneIntroduceeDeletesTheOther() + throws Exception { + addListeners(true, true); + + // Introduce 1 to 2 + makeIntroduction(true); + updateContactIdsFor1And2(); + + // Sync client versions and transport properties - this shows that + // 1 and 2 share a set of transport keys + sync1To2(1, true); + sync2To1(1, true); + sync1To2(2, true); + sync2To1(1, true); + ack1To2(1); + + // 1 deletes 2, but not vice versa + contactManager1.removeContact(contactId2From1); + + // Introduce 1 to 2 again + makeIntroduction(false); + updateContactIdsFor1And2(); + + // Sync client versioning update from 1 to 2 only - this shows that + // although 1 and 2 share transport keys, the connection won't be + // fully functional until we have a way for 1 to reset the state it's + // holding about 2 + sync1To2(1, true); + ack2To1(1); + } + + private void makeIntroduction(boolean expectAddContact2From1) + throws Exception { + // Make introduction + Contact introducee1 = contact1From0; + Contact introducee2 = contact2From0; + introductionManager0.makeIntroduction(introducee1, introducee2, "Hi!"); + + // Request to 1 + sync0To1(1, true); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener1.requestReceived); + + // Request to 2 + sync0To2(1, true); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener2.requestReceived); + + // Response from 1, forwarded + sync1To0(1, true); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener0.response1Received); + sync0To2(1, true); + + // Response and auth from 2, forwarded + sync2To0(2, true); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener0.response2Received); + sync0To1(2, true); + if (expectAddContact2From1) { + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener1.succeeded); + } + assertTrue(contactManager1 + .contactExists(author2.getId(), author1.getId())); + + // Auth and activate from 1, forwarded + sync1To0(2, true); + sync0To2(2, true); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener2.succeeded); + assertTrue(contactManager2 + .contactExists(author1.getId(), author2.getId())); + + // Activate from 2, forwarded + sync2To0(1, true); + sync0To1(1, true); + } + + private void updateContactIdsFor1And2() throws Exception { + contactId2From1 = contactManager1.getContact(author2.getId(), + author1.getId()).getId(); + contactId1From2 = contactManager2.getContact(author1.getId(), + author2.getId()).getId(); + } + private DeletionResult deleteAllMessages1From0() throws DbException { return db0.transactionWithResult(false, txn -> introductionManager0 .deleteAllMessages(txn, contactId1From0));