From 4b65bb3fc0cfa55cf0533406877da20fb4da9e7b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Fri, 10 Dec 2010 18:17:16 +0100
Subject: Added TLS channel binding support to SCRAMSHA1ClientAuthenticator.


diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp
index a7f39b6..0398012 100644
--- a/Swiften/Client/ClientSession.cpp
+++ b/Swiften/Client/ClientSession.cpp
@@ -192,10 +192,10 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) {
 				stream->writeElement(boost::shared_ptr<Element>(new AuthRequest("EXTERNAL", "")));
 			}
 			else if (streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1")) {
-				// FIXME: Use a real nonce
 				std::ostringstream s;
 				s << boost::uuids::random_generator()();
-				authenticator = new SCRAMSHA1ClientAuthenticator(s.str());
+				SCRAMSHA1ClientAuthenticator* scramAuthenticator = new SCRAMSHA1ClientAuthenticator(s.str(), false);
+				authenticator = scramAuthenticator;
 				state = WaitingForCredentials;
 				onNeedCredentials();
 			}
diff --git a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp
index 551afd5..4e00397 100644
--- a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp
+++ b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp
@@ -35,7 +35,7 @@ static String escape(const String& s) {
 }
 
 
-SCRAMSHA1ClientAuthenticator::SCRAMSHA1ClientAuthenticator(const String& nonce) : ClientAuthenticator("SCRAM-SHA-1"), step(Initial), clientnonce(nonce) {
+SCRAMSHA1ClientAuthenticator::SCRAMSHA1ClientAuthenticator(const String& nonce, bool useChannelBinding) : ClientAuthenticator("SCRAM-SHA-1"), step(Initial), clientnonce(nonce), useChannelBinding(useChannelBinding) {
 }
 
 boost::optional<ByteArray> SCRAMSHA1ClientAuthenticator::getResponse() const {
@@ -50,7 +50,11 @@ boost::optional<ByteArray> SCRAMSHA1ClientAuthenticator::getResponse() const {
 		for (unsigned int i = 0; i < clientProof.getSize(); ++i) {
 			clientProof[i] ^= clientSignature[i];
 		}
-		ByteArray result = ByteArray("c=") + Base64::encode(getGS2Header()) + ",r=" + clientnonce + serverNonce + ",p=" + Base64::encode(clientProof);
+		ByteArray channelBindData;
+		if (useChannelBinding && tlsChannelBindingData) {
+			channelBindData = *tlsChannelBindingData;
+		}
+		ByteArray result = ByteArray("c=") + Base64::encode(getGS2Header() + channelBindData) + ",r=" + clientnonce + serverNonce + ",p=" + Base64::encode(clientProof);
 		return result;
 	}
 	else {
@@ -146,7 +150,20 @@ ByteArray SCRAMSHA1ClientAuthenticator::getInitialBareClientMessage() const {
 }
 
 ByteArray SCRAMSHA1ClientAuthenticator::getGS2Header() const {
-	return ByteArray("n,") + (getAuthorizationID().isEmpty() ? "" : "a=" + escape(getAuthorizationID())) + ",";
+	ByteArray channelBindingHeader("n");
+	if (tlsChannelBindingData) {
+		if (useChannelBinding) {
+			channelBindingHeader = ByteArray("p=tls-server-end-point");
+		}
+		else {
+			channelBindingHeader = ByteArray("y");
+		}
+	}
+	return channelBindingHeader + ByteArray(",") + (getAuthorizationID().isEmpty() ? "" : "a=" + escape(getAuthorizationID())) + ",";
+}
+
+void SCRAMSHA1ClientAuthenticator::setTLSChannelBindingData(const ByteArray& channelBindingData) {
+	this->tlsChannelBindingData = channelBindingData;
 }
 
 }
diff --git a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h
index 396cc93..b44e6b7 100644
--- a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h
+++ b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h
@@ -7,6 +7,7 @@
 #pragma once
 
 #include <map>
+#include <boost/optional.hpp>
 
 #include "Swiften/Base/String.h"
 #include "Swiften/Base/ByteArray.h"
@@ -15,7 +16,9 @@
 namespace Swift {
 	class SCRAMSHA1ClientAuthenticator : public ClientAuthenticator {
 		public:
-			SCRAMSHA1ClientAuthenticator(const String& nonce);
+			SCRAMSHA1ClientAuthenticator(const String& nonce, bool useChannelBinding = false);
+
+			void setTLSChannelBindingData(const ByteArray& channelBindingData);
 			
 			virtual boost::optional<ByteArray> getResponse() const;
 			virtual bool setChallenge(const boost::optional<ByteArray>&);
@@ -38,5 +41,7 @@ namespace Swift {
 			ByteArray authMessage;
 			ByteArray saltedPassword;
 			ByteArray serverSignature;
+			bool useChannelBinding;
+			boost::optional<ByteArray> tlsChannelBindingData;
 	};
 }
diff --git a/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp b/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp
index b65cdd3..0d12bd3 100644
--- a/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp
+++ b/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp
@@ -18,7 +18,11 @@ class SCRAMSHA1ClientAuthenticatorTest : public CppUnit::TestFixture {
 		CPPUNIT_TEST(testGetInitialResponse_UsernameHasSpecialChars);
 		CPPUNIT_TEST(testGetInitialResponse_WithAuthorizationID);
 		CPPUNIT_TEST(testGetInitialResponse_WithAuthorizationIDWithSpecialChars);
+		CPPUNIT_TEST(testGetInitialResponse_WithoutChannelBindingWithTLSChannelBindingData);
+		CPPUNIT_TEST(testGetInitialResponse_WithChannelBindingWithTLSChannelBindingData);
 		CPPUNIT_TEST(testGetFinalResponse);
+		CPPUNIT_TEST(testGetFinalResponse_WithoutChannelBindingWithTLSChannelBindingData);
+		CPPUNIT_TEST(testGetFinalResponse_WithChannelBindingWithTLSChannelBindingData);
 		CPPUNIT_TEST(testSetChallenge);
 		CPPUNIT_TEST(testSetChallenge_InvalidClientNonce);
 		CPPUNIT_TEST(testSetChallenge_OnlyClientNonce);
@@ -71,6 +75,26 @@ class SCRAMSHA1ClientAuthenticatorTest : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT_EQUAL(String("n,a=a=3Du=2Cth,n=user,r=abcdefghABCDEFGH"), response.toString());
 		}
 
+		void testGetInitialResponse_WithoutChannelBindingWithTLSChannelBindingData() {
+			SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false);
+			testling.setTLSChannelBindingData("xyza");
+			testling.setCredentials("user", "pass", "");
+
+			ByteArray response = *testling.getResponse();
+
+			CPPUNIT_ASSERT_EQUAL(String("y,,n=user,r=abcdefghABCDEFGH"), response.toString());
+		}
+
+		void testGetInitialResponse_WithChannelBindingWithTLSChannelBindingData() {
+			SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", true);
+			testling.setTLSChannelBindingData("xyza");
+			testling.setCredentials("user", "pass", "");
+
+			ByteArray response = *testling.getResponse();
+
+			CPPUNIT_ASSERT_EQUAL(String("p=tls-server-end-point,,n=user,r=abcdefghABCDEFGH"), response.toString());
+		}
+
 		void testGetFinalResponse() {
 			SCRAMSHA1ClientAuthenticator testling("abcdefgh");
 			testling.setCredentials("user", "pass", "");
@@ -81,6 +105,28 @@ class SCRAMSHA1ClientAuthenticatorTest : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT_EQUAL(String("c=biws,r=abcdefghABCDEFGH,p=CZbjGDpIteIJwQNBgO0P8pKkMGY="), response.toString());
 		}
 
+		void testGetFinalResponse_WithoutChannelBindingWithTLSChannelBindingData() {
+			SCRAMSHA1ClientAuthenticator testling("abcdefgh", false);
+			testling.setCredentials("user", "pass", "");
+			testling.setTLSChannelBindingData("xyza");
+			testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096"));
+
+			ByteArray response = *testling.getResponse();
+
+			CPPUNIT_ASSERT_EQUAL(String("c=eSws,r=abcdefghABCDEFGH,p=JNpsiFEcxZvNZ1+FFBBqrYvYxMk="), response.toString());
+		}
+
+		void testGetFinalResponse_WithChannelBindingWithTLSChannelBindingData() {
+			SCRAMSHA1ClientAuthenticator testling("abcdefgh", true);
+			testling.setCredentials("user", "pass", "");
+			testling.setTLSChannelBindingData("xyza");
+			testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096"));
+
+			ByteArray response = *testling.getResponse();
+
+			CPPUNIT_ASSERT_EQUAL(String("c=cD10bHMtc2VydmVyLWVuZC1wb2ludCwseHl6YQ==,r=abcdefghABCDEFGH,p=ycZyNs03w1HlRzFmXl8dlKx3NAU="), response.toString());
+		}
+
 		void testSetFinalChallenge() {
 			SCRAMSHA1ClientAuthenticator testling("abcdefgh");
 			testling.setCredentials("user", "pass", "");
-- 
cgit v0.10.2-6-g49f6