From 1692e5a6950671fe7b31ef38c088d018c298966c Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Thu, 16 May 2013 15:10:16 +0100
Subject: [PATCH] Invitation protocol was proceeding after confirmation timed
 out.

Both sides now close the connection after exchanging confirmation
results unless both results are positive.
---
 .../invitation/AddContactActivity.java        |  3 +++
 .../sf/briar/invitation/AliceConnector.java   | 21 ++++++++++++-------
 .../net/sf/briar/invitation/BobConnector.java | 21 ++++++++++++-------
 .../net/sf/briar/invitation/Connector.java    | 12 ++++++-----
 .../sf/briar/invitation/ConnectorGroup.java   | 19 +++++++++--------
 5 files changed, 48 insertions(+), 28 deletions(-)

diff --git a/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java b/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java
index 14798d6255..6c22e433c5 100644
--- a/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java
+++ b/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java
@@ -288,9 +288,11 @@ implements InvitationListener {
 		if(code == remoteConfirmationCode) {
 			localMatched = true;
 			if(remoteMatched) setView(new ContactDetailsView(this));
+			else if(remoteCompared) setView(new CodesDoNotMatchView(this));
 			else setView(new WaitForContactView(this));
 			task.localConfirmationSucceeded();
 		} else {
+			localMatched = false;
 			setView(new CodesDoNotMatchView(this));
 			task.localConfirmationFailed();
 		}
@@ -353,6 +355,7 @@ implements InvitationListener {
 		runOnUiThread(new Runnable() {
 			public void run() {
 				remoteCompared = true;
+				remoteMatched = false;
 				if(localMatched)
 					setView(new CodesDoNotMatchView(AddContactActivity.this));
 			}
diff --git a/briar-core/src/net/sf/briar/invitation/AliceConnector.java b/briar-core/src/net/sf/briar/invitation/AliceConnector.java
index 580195cd1c..6337959617 100644
--- a/briar-core/src/net/sf/briar/invitation/AliceConnector.java
+++ b/briar-core/src/net/sf/briar/invitation/AliceConnector.java
@@ -106,23 +106,30 @@ class AliceConnector extends Connector {
 		int aliceCode = codes[0], bobCode = codes[1];
 		group.keyAgreementSucceeded(aliceCode, bobCode);
 		// Exchange confirmation results
+		boolean localMatched, remoteMatched;
 		try {
-			sendConfirmation(w);
-			if(receiveConfirmation(r)) group.remoteConfirmationSucceeded();
-			else group.remoteConfirmationFailed();
+			localMatched = group.waitForLocalConfirmationResult();
+			sendConfirmation(w, localMatched);
+			remoteMatched = receiveConfirmation(r);
 		} catch(IOException e) {
 			if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			tryToClose(conn, true);
 			group.remoteConfirmationFailed();
+			tryToClose(conn, true);
 			return;
 		} catch(InterruptedException e) {
 			if(LOG.isLoggable(WARNING))
 				LOG.warning("Interrupted while waiting for confirmation");
-			tryToClose(conn, true);
 			group.remoteConfirmationFailed();
+			tryToClose(conn, true);
 			Thread.currentThread().interrupt();
 			return;
 		}
+		if(remoteMatched) group.remoteConfirmationSucceeded();
+		else group.remoteConfirmationFailed();
+		if(!(localMatched && remoteMatched)) {
+			tryToClose(conn, false);
+			return;
+		}
 		// The timestamp is taken after exhanging confirmation results
 		long localTimestamp = clock.currentTimeMillis();
 		// Confirmation succeeded - upgrade to a secure connection
@@ -152,13 +159,13 @@ class AliceConnector extends Connector {
 			remoteProps = receiveTransportProperties(r);
 		} catch(GeneralSecurityException e) {
 			if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			tryToClose(conn, true);
 			group.pseudonymExchangeFailed();
+			tryToClose(conn, true);
 			return;
 		} catch(IOException e) {
 			if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			tryToClose(conn, true);
 			group.pseudonymExchangeFailed();
+			tryToClose(conn, true);
 			return;
 		}
 		// The epoch is the minimum of the peers' timestamps
diff --git a/briar-core/src/net/sf/briar/invitation/BobConnector.java b/briar-core/src/net/sf/briar/invitation/BobConnector.java
index 8d7d2ad09f..3f03a65b61 100644
--- a/briar-core/src/net/sf/briar/invitation/BobConnector.java
+++ b/briar-core/src/net/sf/briar/invitation/BobConnector.java
@@ -106,23 +106,30 @@ class BobConnector extends Connector {
 		int aliceCode = codes[0], bobCode = codes[1];
 		group.keyAgreementSucceeded(bobCode, aliceCode);
 		// Exchange confirmation results
+		boolean localMatched, remoteMatched;
 		try {
-			if(receiveConfirmation(r)) group.remoteConfirmationSucceeded();
-			else group.remoteConfirmationFailed();
-			sendConfirmation(w);
+			remoteMatched = receiveConfirmation(r);
+			localMatched = group.waitForLocalConfirmationResult();
+			sendConfirmation(w, localMatched);
 		} catch(IOException e) {
 			if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			tryToClose(conn, true);
 			group.remoteConfirmationFailed();
+			tryToClose(conn, true);
 			return;
 		} catch(InterruptedException e) {
 			if(LOG.isLoggable(WARNING))
 				LOG.warning("Interrupted while waiting for confirmation");
-			tryToClose(conn, true);
 			group.remoteConfirmationFailed();
+			tryToClose(conn, true);
 			Thread.currentThread().interrupt();
 			return;
 		}
+		if(remoteMatched) group.remoteConfirmationSucceeded();
+		else group.remoteConfirmationFailed();
+		if(!(localMatched && remoteMatched)) {
+			tryToClose(conn, false);
+			return;
+		}
 		// The timestamp is taken after exhanging confirmation results
 		long localTimestamp = clock.currentTimeMillis();
 		// Confirmation succeeded - upgrade to a secure connection
@@ -152,13 +159,13 @@ class BobConnector extends Connector {
 			sendTransportProperties(w);
 		} catch(GeneralSecurityException e) {
 			if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			tryToClose(conn, true);
 			group.pseudonymExchangeFailed();
+			tryToClose(conn, true);
 			return;
 		} catch(IOException e) {
 			if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			tryToClose(conn, true);
 			group.pseudonymExchangeFailed();
+			tryToClose(conn, true);
 			return;
 		}
 		// The epoch is the minimum of the peers' timestamps
diff --git a/briar-core/src/net/sf/briar/invitation/Connector.java b/briar-core/src/net/sf/briar/invitation/Connector.java
index c58d9d4b3c..f0fd6b0955 100644
--- a/briar-core/src/net/sf/briar/invitation/Connector.java
+++ b/briar-core/src/net/sf/briar/invitation/Connector.java
@@ -175,21 +175,23 @@ abstract class Connector extends Thread {
 			throw new GeneralSecurityException();
 		}
 		//  Derive the master secret
+		if(LOG.isLoggable(INFO))
+			LOG.info(pluginName + " deriving master secret");
 		return crypto.deriveMasterSecret(key, keyPair, alice);
 	}
 
-	protected void sendConfirmation(Writer w) throws IOException,
-	InterruptedException {
-		boolean matched = group.waitForLocalConfirmationResult();
+	protected void sendConfirmation(Writer w, boolean matched)
+			throws IOException {
 		w.writeBoolean(matched);
 		w.flush();
-		if(LOG.isLoggable(INFO)) LOG.info(pluginName + " sent confirmation");
+		if(LOG.isLoggable(INFO))
+			LOG.info(pluginName + " sent confirmation: " + matched);
 	}
 
 	protected boolean receiveConfirmation(Reader r) throws IOException {
 		boolean matched = r.readBoolean();
 		if(LOG.isLoggable(INFO))
-			LOG.info(pluginName + " received confirmation");
+			LOG.info(pluginName + " received confirmation: " + matched);
 		return matched;
 	}
 
diff --git a/briar-core/src/net/sf/briar/invitation/ConnectorGroup.java b/briar-core/src/net/sf/briar/invitation/ConnectorGroup.java
index 47f0beb605..ee67e4ccd3 100644
--- a/briar-core/src/net/sf/briar/invitation/ConnectorGroup.java
+++ b/briar-core/src/net/sf/briar/invitation/ConnectorGroup.java
@@ -197,15 +197,15 @@ class ConnectorGroup extends Thread implements InvitationTask {
 	public void localConfirmationFailed() {
 		synchronized(this) {
 			localCompared = true;
+			localMatched = false;
 		}
 		localConfirmationLatch.countDown();
 	}
 
 	boolean getAndSetConnected() {
 		boolean redundant = connected.getAndSet(true);
-		if(!redundant) {
+		if(!redundant)
 			for(InvitationListener l : listeners) l.connectionSucceeded();
-		}
 		return redundant;
 	}
 
@@ -222,6 +222,13 @@ class ConnectorGroup extends Thread implements InvitationTask {
 		for(InvitationListener l : listeners) l.keyAgreementFailed();
 	}
 
+	boolean waitForLocalConfirmationResult() throws InterruptedException {
+		localConfirmationLatch.await(CONFIRMATION_TIMEOUT, MILLISECONDS);
+		synchronized(this) {
+			return localMatched;
+		}
+	}
+
 	void remoteConfirmationSucceeded() {
 		synchronized(this) {
 			remoteCompared = true;
@@ -233,17 +240,11 @@ class ConnectorGroup extends Thread implements InvitationTask {
 	void remoteConfirmationFailed() {
 		synchronized(this) {
 			remoteCompared = true;
+			remoteMatched = false;
 		}
 		for(InvitationListener l : listeners) l.remoteConfirmationFailed();
 	}
 
-	boolean waitForLocalConfirmationResult() throws InterruptedException {
-		localConfirmationLatch.await(CONFIRMATION_TIMEOUT, MILLISECONDS);
-		synchronized(this) {
-			return localMatched;
-		}
-	}
-
 	void pseudonymExchangeSucceeded(Author remoteAuthor) {
 		String name = remoteAuthor.getName();
 		synchronized(this) {
-- 
GitLab