summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften')
-rw-r--r--Swiften/Client/ClientSession.cpp9
-rw-r--r--Swiften/SASL/DIGESTMD5ClientAuthenticator.cpp87
-rw-r--r--Swiften/SASL/DIGESTMD5ClientAuthenticator.h34
-rw-r--r--Swiften/SASL/DIGESTMD5Properties.cpp2
-rw-r--r--Swiften/SASL/DIGESTMD5Properties.h2
-rw-r--r--Swiften/SASL/SConscript2
-rw-r--r--Swiften/SASL/UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp62
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);