From 14fbc9a52ffad2573e1955f09b033c714b9f2ff4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sun, 22 Nov 2009 13:26:12 +0100
Subject: Added SCRAM-SHA-1 unit test for final server message.


diff --git a/Swiften/SASL/ClientAuthenticator.h b/Swiften/SASL/ClientAuthenticator.h
index f93f44e..f42a51e 100644
--- a/Swiften/SASL/ClientAuthenticator.h
+++ b/Swiften/SASL/ClientAuthenticator.h
@@ -19,7 +19,7 @@ namespace Swift {
 				this->authzid = authzid;
 			}
 
-			virtual ByteArray getResponse() = 0;
+			virtual ByteArray getResponse() const = 0;
 			virtual bool setChallenge(const ByteArray&) = 0;
 
 			const String& getAuthenticationID() const {
diff --git a/Swiften/SASL/PLAINClientAuthenticator.cpp b/Swiften/SASL/PLAINClientAuthenticator.cpp
index f61f1a0..8f88c3c 100644
--- a/Swiften/SASL/PLAINClientAuthenticator.cpp
+++ b/Swiften/SASL/PLAINClientAuthenticator.cpp
@@ -5,7 +5,7 @@ namespace Swift {
 PLAINClientAuthenticator::PLAINClientAuthenticator() : ClientAuthenticator("PLAIN") {
 }
 
-ByteArray PLAINClientAuthenticator::getResponse() {
+ByteArray PLAINClientAuthenticator::getResponse() const {
 	return ByteArray(getAuthorizationID()) + '\0' + ByteArray(getAuthenticationID()) + '\0' + ByteArray(getPassword());
 }
 
diff --git a/Swiften/SASL/PLAINClientAuthenticator.h b/Swiften/SASL/PLAINClientAuthenticator.h
index bb24af7..854eb30 100644
--- a/Swiften/SASL/PLAINClientAuthenticator.h
+++ b/Swiften/SASL/PLAINClientAuthenticator.h
@@ -7,7 +7,7 @@ namespace Swift {
 		public:
 			PLAINClientAuthenticator();
 
-			virtual ByteArray getResponse();
+			virtual ByteArray getResponse() const;
 			virtual bool setChallenge(const ByteArray&);
 	};
 }
diff --git a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp
index b0a9f96..16c938a 100644
--- a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp
+++ b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp
@@ -17,19 +17,14 @@ SCRAMSHA1ClientAuthenticator::SCRAMSHA1ClientAuthenticator(const String& nonce)
 	// TODO: Normalize getPassword()
 }
 
-ByteArray SCRAMSHA1ClientAuthenticator::getResponse() {
+ByteArray SCRAMSHA1ClientAuthenticator::getResponse() const {
 	if (step == Initial) {
 		return "n,," + getInitialBareClientMessage();
 	}
 	else {
-		ByteArray saltedPassword = PBKDF2::encode(getPassword(), salt, iterations);
 		ByteArray clientKey = HMACSHA1::getResult(saltedPassword, "Client Key");
 		ByteArray storedKey = SHA1::getBinaryHash(clientKey);
-		ByteArray serverKey = HMACSHA1::getResult(saltedPassword, "Server Key");
-
-		ByteArray authMessage = getInitialBareClientMessage() + "," + initialServerMessage + "," + "c=biwsCg==," + "r=" + clientnonce + serverNonce;
 		ByteArray clientSignature = HMACSHA1::getResult(storedKey, authMessage);
-		serverSignature = HMACSHA1::getResult(serverKey, authMessage);
 		ByteArray clientProof = clientKey;
 		for (unsigned int i = 0; i < clientProof.getSize(); ++i) {
 			clientProof[i] ^= clientSignature[i];
@@ -45,16 +40,23 @@ bool SCRAMSHA1ClientAuthenticator::setChallenge(const ByteArray& challenge) {
 
 		// TODO: Check if these values are correct
 		std::map<char, String> keys = parseMap(String(initialServerMessage.getData(), initialServerMessage.getSize()));
-		salt = Base64::decode(keys['s']);
+		ByteArray salt = Base64::decode(keys['s']);
 		String clientServerNonce = keys['r'];
 		serverNonce = clientServerNonce.getSubstring(clientnonce.getUTF8Size(), clientServerNonce.npos());
-		iterations = boost::lexical_cast<int>(keys['i'].getUTF8String());
+		int iterations = boost::lexical_cast<int>(keys['i'].getUTF8String());
+
+		// Compute all the values needed for the server signature
+		saltedPassword = PBKDF2::encode(StringPrep::getPrepared(getPassword(), StringPrep::SASLPrep), salt, iterations);
+		authMessage = getInitialBareClientMessage() + "," + initialServerMessage + "," + "c=biwsCg==," + "r=" + clientnonce + serverNonce;
+		ByteArray serverKey = HMACSHA1::getResult(saltedPassword, "Server Key");
+		serverSignature = HMACSHA1::getResult(serverKey, authMessage);
 
 		step = Proof;
 		return true;
 	}
 	else {
-		return challenge == Base64::encode(ByteArray("v=") + Base64::encode(serverSignature));
+		ByteArray result = ByteArray("v=") + ByteArray(Base64::encode(serverSignature));
+		return challenge == result;
 	}
 }
 
diff --git a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h
index 9504b35..108ac2e 100644
--- a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h
+++ b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h
@@ -11,7 +11,7 @@ namespace Swift {
 		public:
 			SCRAMSHA1ClientAuthenticator(const String& nonce);
 			
-			virtual ByteArray getResponse();
+			virtual ByteArray getResponse() const;
 			virtual bool setChallenge(const ByteArray&);
 
 		private:
@@ -25,9 +25,9 @@ namespace Swift {
 			} step;
 			String clientnonce;
 			ByteArray initialServerMessage;
-			int iterations;
 			ByteArray serverNonce;
-			ByteArray salt;
+			ByteArray authMessage;
+			ByteArray saltedPassword;
 			ByteArray serverSignature;
 	};
 }
diff --git a/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp b/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp
index 9f18ff0..618a748 100644
--- a/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp
+++ b/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp
@@ -11,6 +11,8 @@ class SCRAMSHA1ClientAuthenticatorTest : public CppUnit::TestFixture {
 		CPPUNIT_TEST(testGetInitialResponse);
 		CPPUNIT_TEST(testGetInitialResponse_UsernameHasSpecialChars);
 		CPPUNIT_TEST(testGetFinalResponse);
+		CPPUNIT_TEST(testSetFinalChallenge);
+		CPPUNIT_TEST(testSetFinalChallenge_InvalidChallenge);
 		CPPUNIT_TEST_SUITE_END();
 
 	public:
@@ -44,6 +46,24 @@ class SCRAMSHA1ClientAuthenticatorTest : public CppUnit::TestFixture {
 
 			CPPUNIT_ASSERT_EQUAL(String("c=biwsCg==,r=abcdefghABCDEFGH,p=bVzb1EAf2hXw5Z+QIMYPTy5TOsU="), testling.getResponse().toString());
 		}
+
+		void testSetFinalChallenge() {
+			SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+			testling.setCredentials("user", "pass", "");
+			testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096"));
+			bool result = testling.setChallenge(ByteArray("v=xWI/+vT8aAm0hKpqKIqctIDwtQE="));
+
+			CPPUNIT_ASSERT(result);
+		}
+
+		void testSetFinalChallenge_InvalidChallenge() {
+			SCRAMSHA1ClientAuthenticator testling("abcdefgh");
+			testling.setCredentials("user", "pass", "");
+			testling.setChallenge(ByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096"));
+			bool result = testling.setChallenge(ByteArray("v=e26kI69ICb6zosapLLxrER/631A="));
+
+			CPPUNIT_ASSERT(!result);
+		}
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(SCRAMSHA1ClientAuthenticatorTest);
-- 
cgit v0.10.2-6-g49f6