diff options
| author | Remko Tronçon <git@el-tramo.be> | 2010-05-08 16:48:21 (GMT) | 
|---|---|---|
| committer | Remko Tronçon <git@el-tramo.be> | 2010-05-08 16:48:21 (GMT) | 
| commit | d233ec7a863fb0b9a6f20ea0aa52c7c0ea38e2fd (patch) | |
| tree | b0ff4203dc76d3995fbf696d0432f15d7f4916a9 | |
| parent | 52bd37a759acc7edbd616c745ff64ac70ae41b9c (diff) | |
| download | swift-d233ec7a863fb0b9a6f20ea0aa52c7c0ea38e2fd.zip swift-d233ec7a863fb0b9a6f20ea0aa52c7c0ea38e2fd.tar.bz2  | |
Added DIGEST-MD5 client authenticator.
| -rw-r--r-- | Swiften/Client/ClientSession.cpp | 9 | ||||
| -rw-r--r-- | Swiften/SASL/DIGESTMD5ClientAuthenticator.cpp | 87 | ||||
| -rw-r--r-- | Swiften/SASL/DIGESTMD5ClientAuthenticator.h | 34 | ||||
| -rw-r--r-- | Swiften/SASL/DIGESTMD5Properties.cpp | 2 | ||||
| -rw-r--r-- | Swiften/SASL/DIGESTMD5Properties.h | 2 | ||||
| -rw-r--r-- | Swiften/SASL/SConscript | 2 | ||||
| -rw-r--r-- | Swiften/SASL/UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp | 62 | 
7 files changed, 196 insertions, 2 deletions
diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp index 7fdb8ac..a255cef 100644 --- a/Swiften/Client/ClientSession.cpp +++ b/Swiften/Client/ClientSession.cpp @@ -29,6 +29,7 @@  #include "Swiften/Elements/ResourceBind.h"  #include "Swiften/SASL/PLAINClientAuthenticator.h"  #include "Swiften/SASL/SCRAMSHA1ClientAuthenticator.h" +#include "Swiften/SASL/DIGESTMD5ClientAuthenticator.h"  #include "Swiften/Session/SessionStream.h"  namespace Swift { @@ -107,6 +108,14 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) {  				state = WaitingForCredentials;  				onNeedCredentials();  			} +			else if (streamFeatures->hasAuthenticationMechanism("DIGEST-MD5")) { +				std::ostringstream s; +				s << boost::uuids::random_generator()(); +				// FIXME: Host should probably be the actual host +				authenticator = new DIGESTMD5ClientAuthenticator(localJID.getDomain(), s.str()); +				state = WaitingForCredentials; +				onNeedCredentials(); +			}  			else if (streamFeatures->hasAuthenticationMechanism("PLAIN")) {  				authenticator = new PLAINClientAuthenticator();  				state = WaitingForCredentials; diff --git a/Swiften/SASL/DIGESTMD5ClientAuthenticator.cpp b/Swiften/SASL/DIGESTMD5ClientAuthenticator.cpp new file mode 100644 index 0000000..d22f295 --- /dev/null +++ b/Swiften/SASL/DIGESTMD5ClientAuthenticator.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/SASL/DIGESTMD5ClientAuthenticator.h" + +#include <cassert> + +#include "Swiften/StringCodecs/MD5.h" +#include "Swiften/StringCodecs/Hexify.h" + +namespace Swift { + +DIGESTMD5ClientAuthenticator::DIGESTMD5ClientAuthenticator(const String& host, const String& nonce) : ClientAuthenticator("DIGEST-MD5"), step(Initial), host(host), cnonce(nonce) { +} + +ByteArray DIGESTMD5ClientAuthenticator::getResponse() const { +	if (step == Initial) { +		return ByteArray(); +	} +	else if (step == Response) { +		String realm; +		if (challenge.getValue("realm")) { +			realm = *challenge.getValue("realm"); +		} +		String qop = "auth"; +		String digestURI = "xmpp/" + host; +		String nc = "00000001"; + +		// Compute the response value +		ByteArray A1 = MD5::getHash(getAuthenticationID() + ":" + realm + ":" + getPassword()) + ":" + *challenge.getValue("nonce") + ":" + cnonce; +		if (!getAuthorizationID().isEmpty()) { +			A1 += ":" + getAuthenticationID(); +		} +		String A2 = "AUTHENTICATE:" + digestURI; + +		String responseValue = Hexify::hexify(MD5::getHash( +				Hexify::hexify(MD5::getHash(A1)) + ":"  +				+ *challenge.getValue("nonce") + ":" + nc + ":" + cnonce + ":" + qop + ":"  +				+ Hexify::hexify(MD5::getHash(A2)))); + +		DIGESTMD5Properties response; +		response.setValue("username", getAuthenticationID()); +		if (!realm.isEmpty()) { +			response.setValue("realm", realm); +		} +		response.setValue("nonce", *challenge.getValue("nonce")); +		response.setValue("cnonce", cnonce); +		response.setValue("nc", "00000001"); +		response.setValue("qop", qop); +		response.setValue("digest-uri", digestURI); +		response.setValue("charset", "utf-8"); +		response.setValue("response", responseValue); +		if (!getAuthorizationID().isEmpty()) { +			response.setValue("authzid", getAuthorizationID()); +		} +		return response.serialize(); +	} +	else { +		return ByteArray(); +	} +} + +bool DIGESTMD5ClientAuthenticator::setChallenge(const ByteArray& challengeData) { +	if (step == Initial) { +		challenge = DIGESTMD5Properties::parse(challengeData); + +		// Sanity checks +		if (!challenge.getValue("nonce")) { +			return false; +		} +		if (!challenge.getValue("charset") || *challenge.getValue("charset") != "utf-8") { +			return false; +		} +		step = Response; +		return true; +	} +	else { +		step = Final; +		// TODO: Check RSPAuth +		return true; +	} +} + +} diff --git a/Swiften/SASL/DIGESTMD5ClientAuthenticator.h b/Swiften/SASL/DIGESTMD5ClientAuthenticator.h new file mode 100644 index 0000000..e360257 --- /dev/null +++ b/Swiften/SASL/DIGESTMD5ClientAuthenticator.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <map> + +#include "Swiften/Base/String.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/SASL/ClientAuthenticator.h" +#include "Swiften/SASL/DIGESTMD5Properties.h" + +namespace Swift { +	class DIGESTMD5ClientAuthenticator : public ClientAuthenticator { +		public: +			DIGESTMD5ClientAuthenticator(const String& host, const String& nonce); +			 +			virtual ByteArray getResponse() const; +			virtual bool setChallenge(const ByteArray&); + +		private: +			enum Step { +				Initial, +				Response, +				Final, +			} step; +			String host; +			String cnonce; +			DIGESTMD5Properties challenge; +	}; +} diff --git a/Swiften/SASL/DIGESTMD5Properties.cpp b/Swiften/SASL/DIGESTMD5Properties.cpp index 0853f90..d20f66e 100644 --- a/Swiften/SASL/DIGESTMD5Properties.cpp +++ b/Swiften/SASL/DIGESTMD5Properties.cpp @@ -106,7 +106,7 @@ ByteArray DIGESTMD5Properties::serialize() const {  	return result;  } -boost::optional<String> DIGESTMD5Properties::getValue(const String& key) { +boost::optional<String> DIGESTMD5Properties::getValue(const String& key) const {  	DIGESTMD5PropertiesMap::const_iterator i = properties.find(key);  	if (i != properties.end()) {  		return i->second.toString(); diff --git a/Swiften/SASL/DIGESTMD5Properties.h b/Swiften/SASL/DIGESTMD5Properties.h index 4d3a4fe..3afd369 100644 --- a/Swiften/SASL/DIGESTMD5Properties.h +++ b/Swiften/SASL/DIGESTMD5Properties.h @@ -17,7 +17,7 @@ namespace Swift {  		public:  			DIGESTMD5Properties(); -			boost::optional<String> getValue(const String& key); +			boost::optional<String> getValue(const String& key) const;  			void setValue(const String& key, const String& value); diff --git a/Swiften/SASL/SConscript b/Swiften/SASL/SConscript index 778a414..0ef9581 100644 --- a/Swiften/SASL/SConscript +++ b/Swiften/SASL/SConscript @@ -9,6 +9,7 @@ objects = myenv.StaticObject([  		"PLAINMessage.cpp",  		"SCRAMSHA1ClientAuthenticator.cpp",  		"DIGESTMD5Properties.cpp", +		"DIGESTMD5ClientAuthenticator.cpp",  	])  swiften_env.Append(SWIFTEN_OBJECTS = [objects])  env.Append(UNITTEST_SOURCES = [ @@ -16,4 +17,5 @@ env.Append(UNITTEST_SOURCES = [  			File("UnitTest/PLAINClientAuthenticatorTest.cpp"),  			File("UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp"),  			File("UnitTest/DIGESTMD5PropertiesTest.cpp"), +			File("UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp"),  	]) diff --git a/Swiften/SASL/UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp b/Swiften/SASL/UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp new file mode 100644 index 0000000..e16c202 --- /dev/null +++ b/Swiften/SASL/UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/SASL/DIGESTMD5ClientAuthenticator.h" +#include "Swiften/Base/ByteArray.h" + +using namespace Swift; + +class DIGESTMD5ClientAuthenticatorTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(DIGESTMD5ClientAuthenticatorTest); +		CPPUNIT_TEST(testGetInitialResponse); +		CPPUNIT_TEST(testGetResponse); +		CPPUNIT_TEST(testGetResponse_WithAuthorizationID); +		/*CPPUNIT_TEST(testSetChallenge); +		CPPUNIT_TEST(testSetChallenge_InvalidBlabBla);*/ +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void testGetInitialResponse() { +			DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh"); + +			ByteArray response = testling.getResponse(); + +			CPPUNIT_ASSERT(response.isEmpty()); +		} + +		void testGetResponse() { +			DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh"); + +			testling.setCredentials("user", "pass", ""); +			testling.setChallenge(ByteArray( +				"realm=\"example.com\"," +				"nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," +				"qop=auth,charset=utf-8,algorithm=md5-sess")); + +			ByteArray response = testling.getResponse(); + +			CPPUNIT_ASSERT_EQUAL(String("charset=utf-8,cnonce=\"abcdefgh\",digest-uri=\"xmpp/xmpp.example.com\",nc=00000001,nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\",qop=auth,realm=\"example.com\",response=088891c800ecff1b842159ad6459104a,username=\"user\""), response.toString()); +		} + +		void testGetResponse_WithAuthorizationID() { +			DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh"); + +			testling.setCredentials("user", "pass", "myauthzid"); +			testling.setChallenge(ByteArray( +				"realm=\"example.com\"," +				"nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," +				"qop=auth,charset=utf-8,algorithm=md5-sess")); + +			ByteArray response = testling.getResponse(); + +			CPPUNIT_ASSERT_EQUAL(String("authzid=\"myauthzid\",charset=utf-8,cnonce=\"abcdefgh\",digest-uri=\"xmpp/xmpp.example.com\",nc=00000001,nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\",qop=auth,realm=\"example.com\",response=4293834432b6e7889a2dee7e8fe7dd06,username=\"user\""), response.toString()); +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(DIGESTMD5ClientAuthenticatorTest);  | 
 Swift