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 43f243a0155329feeb0150cf76936fb78e941f18..322e46a6b640cbe17562bd1b1e57f9fb4ddb0b5c 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 @@ -391,7 +391,6 @@ class IntroduceeProtocolEngine mac = crypto.authMac(ourMacKey, s, localAuthor.getId()); signature = crypto.sign(ourMacKey, localAuthor.getPrivateKey()); } catch (GeneralSecurityException e) { - // TODO if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); return abort(txn, s); diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java index b923baac83f0538a5dbe613ae140b77947f2fc7b..ed19fe651a5d1d0693426cd97fbaab89c031151e 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java @@ -343,6 +343,12 @@ 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 (senderIsAlice && s.getState() != B_DECLINED) + return abort(txn, s); + else if (!senderIsAlice && s.getState() != A_DECLINED) + return abort(txn, s); // Mark the response visible in the UI markMessageVisibleInUi(txn, m.getMessageId()); @@ -350,7 +356,6 @@ class IntroducerProtocolEngine messageTracker .trackMessage(txn, m.getGroupId(), m.getTimestamp(), false); - boolean senderIsAlice = senderIsAlice(s, m); Introducee introduceeA, introduceeB; if (senderIsAlice) { introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId()); diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java index cc5c9ab1a239847b7365cb12b39db79207e98461..37f7aa10f63ffb9e25514d3d557139c408dd3433 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java @@ -19,6 +19,9 @@ interface IntroductionCrypto { /** * Returns true if the local author is alice + * + * Alice is the Author whose unique ID has the lower ID, + * comparing the IDs as byte strings. */ boolean isAlice(AuthorId local, AuthorId remote); diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java index db24fda6b2efaafcd5903a4a4088573d5b59c547..f9d53323b08aeb90b6f81ed46018051ce6418c6e 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java @@ -1,6 +1,5 @@ package org.briarproject.briar.introduction; -import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.crypto.CryptoComponent; @@ -68,9 +67,7 @@ class IntroductionCryptoImpl implements IntroductionCrypto { @Override public boolean isAlice(AuthorId local, AuthorId remote) { - byte[] a = local.getBytes(); - byte[] b = remote.getBytes(); - return Bytes.COMPARATOR.compare(new Bytes(a), new Bytes(b)) < 0; + return local.compareTo(remote) < 0; } @Override diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java index 89d26c73fa7f907e99589b2e201fbf34c308c6dd..efb857a0f57699ae41d9bacd541656085fc80eb7 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java @@ -353,7 +353,12 @@ class IntroductionManagerImpl extends ConversationClientImpl try { // Look up the session StoredSession ss = getSession(txn, sessionId); - if (ss == null) throw new IllegalArgumentException(); + if (ss == null) { + // Actions from the UI may be based on stale information. + // The contact might just have been deleted, for example. + // Throwing a DbException here aborts gracefully. + throw new DbException(); + } // Parse the session Contact contact = db.getContact(txn, contactId); GroupId contactGroupId = getContactGroup(contact).getId(); diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java index 362d7e247a7a199d3c94ca0e5651f27ed8a31a9e..929c8bddf91798d3ca284ed9a42308edaa0a5e60 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java @@ -155,7 +155,7 @@ class IntroductionValidator extends BdfMessageValidator { byte[] sessionIdBytes = body.getRaw(1); checkLength(sessionIdBytes, UniqueId.LENGTH); - byte[] previousMessageId = body.getOptionalRaw(2); + byte[] previousMessageId = body.getRaw(2); checkLength(previousMessageId, UniqueId.LENGTH); byte[] mac = body.getOptionalRaw(3); 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 b692492f421fc94552fac3fa60540d16858d1c7e..df0d46b8811e60e6f8f58919ef388a590fd4363e 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 @@ -63,6 +63,7 @@ import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_ 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.introduction.MessageType.DECLINE; import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -681,6 +682,41 @@ public class IntroductionIntegrationTest assertTrue(listener0.aborted); } + /** + * One introducee sends an DECLINE and then another DECLINE message. + * The introducer should notice this and ABORT the session. + */ + @Test + public void testDoubleDecline() throws Exception { + addListeners(false, true); + + // make the introduction + long time = clock.currentTimeMillis(); + introductionManager0 + .makeIntroduction(contact1From0, contact2From0, null, time); + + // sync REQUEST to introducee1 + sync0To1(1, true); + + // save DECLINE from introducee1 + DeclineMessage m = (DeclineMessage) getMessageFor(c1.getClientHelper(), + contact0From1, DECLINE); + + // sync DECLINE back to introducer + sync1To0(1, true); + + // fake a second DECLINE message also from introducee1 + Message msg = c1.getMessageEncoder() + .encodeDeclineMessage(m.getGroupId(), m.getTimestamp() + 1, + 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. @@ -1093,6 +1129,9 @@ public class IntroductionIntegrationTest if (type == ACCEPT) { //noinspection ConstantConditions return c0.getMessageParser().parseAcceptMessage(m, body); + } else if (type == DECLINE) { + //noinspection ConstantConditions + return c0.getMessageParser().parseDeclineMessage(m, body); } else if (type == AUTH) { //noinspection ConstantConditions return c0.getMessageParser().parseAuthMessage(m, body); diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java index b1ad9caa9b6a92e7f11e8cfcad7ba867ebb4ed9b..4f52aa4de9f83d9df7cb4e5b735bf3557e82c524 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java @@ -268,6 +268,21 @@ public class IntroductionValidatorTest extends ValidatorTestCase { validator.validateMessage(message, group, body); } + @Test(expected = FormatException.class) + public void testRejectsInvalidPreviousMsgIdForAuth() throws Exception { + BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(), + 1, getRandomBytes(MAC_BYTES), + signature); + validator.validateMessage(message, group, body); + } + + @Test(expected = FormatException.class) + public void testRejectsPreviousMsgIdNullForAuth() throws Exception { + BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(), null, + getRandomBytes(MAC_BYTES), signature); + validator.validateMessage(message, group, body); + } + @Test(expected = FormatException.class) public void testRejectsTooShortMacForAuth() throws Exception { BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(), @@ -358,6 +373,14 @@ public class IntroductionValidatorTest extends ValidatorTestCase { validator.validateMessage(message, group, body); } + @Test(expected = FormatException.class) + public void testRejectsPreviousMsgIdNullForActivate() throws Exception { + BdfList body = + BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(), null, + mac); + validator.validateMessage(message, group, body); + } + @Test(expected = FormatException.class) public void testRejectsInvalidMacForActivate() throws Exception { BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),