diff options
Diffstat (limited to 'Swiften/Crypto')
-rw-r--r-- | Swiften/Crypto/CommonCryptoCryptoProvider.cpp | 135 | ||||
-rw-r--r-- | Swiften/Crypto/CommonCryptoCryptoProvider.h | 24 | ||||
-rw-r--r-- | Swiften/Crypto/CryptoProvider.cpp | 14 | ||||
-rw-r--r-- | Swiften/Crypto/CryptoProvider.h | 36 | ||||
-rw-r--r-- | Swiften/Crypto/Hash.cpp | 12 | ||||
-rw-r--r-- | Swiften/Crypto/Hash.h | 24 | ||||
-rw-r--r-- | Swiften/Crypto/OpenSSLCryptoProvider.cpp | 140 | ||||
-rw-r--r-- | Swiften/Crypto/OpenSSLCryptoProvider.h | 24 | ||||
-rw-r--r-- | Swiften/Crypto/PlatformCryptoProvider.cpp | 32 | ||||
-rw-r--r-- | Swiften/Crypto/PlatformCryptoProvider.h | 17 | ||||
-rw-r--r-- | Swiften/Crypto/SConscript | 28 | ||||
-rw-r--r-- | Swiften/Crypto/UnitTest/CryptoProviderTest.cpp | 156 | ||||
-rw-r--r-- | Swiften/Crypto/WindowsCryptoProvider.cpp | 222 | ||||
-rw-r--r-- | Swiften/Crypto/WindowsCryptoProvider.h | 30 |
14 files changed, 894 insertions, 0 deletions
diff --git a/Swiften/Crypto/CommonCryptoCryptoProvider.cpp b/Swiften/Crypto/CommonCryptoCryptoProvider.cpp new file mode 100644 index 0000000..14f9284 --- /dev/null +++ b/Swiften/Crypto/CommonCryptoCryptoProvider.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Swiften/Crypto/CommonCryptoCryptoProvider.h> + +#include <CommonCrypto/CommonDigest.h> +#include <CommonCrypto/CommonHMAC.h> +#include <cassert> + +#include <Swiften/Crypto/Hash.h> +#include <Swiften/Base/ByteArray.h> +#include <boost/numeric/conversion/cast.hpp> + +using namespace Swift; + +namespace { + class SHA1Hash : public Hash { + public: + SHA1Hash() : finalized(false) { + if (!CC_SHA1_Init(&context)) { + assert(false); + } + } + + ~SHA1Hash() { + } + + virtual Hash& update(const ByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual Hash& update(const SafeByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual std::vector<unsigned char> getHash() { + assert(!finalized); + std::vector<unsigned char> result(CC_SHA1_DIGEST_LENGTH); + CC_SHA1_Final(vecptr(result), &context); + return result; + } + + private: + template<typename ContainerType> + Hash& updateInternal(const ContainerType& data) { + assert(!finalized); + if (!CC_SHA1_Update(&context, vecptr(data), boost::numeric_cast<CC_LONG>(data.size()))) { + assert(false); + } + return *this; + } + + private: + CC_SHA1_CTX context; + bool finalized; + }; + + class MD5Hash : public Hash { + public: + MD5Hash() : finalized(false) { + if (!CC_MD5_Init(&context)) { + assert(false); + } + } + + ~MD5Hash() { + } + + virtual Hash& update(const ByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual Hash& update(const SafeByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual std::vector<unsigned char> getHash() { + assert(!finalized); + std::vector<unsigned char> result(CC_MD5_DIGEST_LENGTH); + CC_MD5_Final(vecptr(result), &context); + return result; + } + + private: + template<typename ContainerType> + Hash& updateInternal(const ContainerType& data) { + assert(!finalized); + if (!CC_MD5_Update(&context, vecptr(data), boost::numeric_cast<CC_LONG>(data.size()))) { + assert(false); + } + return *this; + } + + private: + CC_MD5_CTX context; + bool finalized; + }; + + template<typename T> + ByteArray getHMACSHA1Internal(const T& key, const ByteArray& data) { + std::vector<unsigned char> result(CC_SHA1_DIGEST_LENGTH); + CCHmac(kCCHmacAlgSHA1, vecptr(key), key.size(), vecptr(data), boost::numeric_cast<CC_LONG>(data.size()), vecptr(result)); + return result; + } +} + +CommonCryptoCryptoProvider::CommonCryptoCryptoProvider() { +} + +CommonCryptoCryptoProvider::~CommonCryptoCryptoProvider() { +} + +Hash* CommonCryptoCryptoProvider::createSHA1() { + return new SHA1Hash(); +} + +Hash* CommonCryptoCryptoProvider::createMD5() { + return new MD5Hash(); +} + +ByteArray CommonCryptoCryptoProvider::getHMACSHA1(const SafeByteArray& key, const ByteArray& data) { + return getHMACSHA1Internal(key, data); +} + +ByteArray CommonCryptoCryptoProvider::getHMACSHA1(const ByteArray& key, const ByteArray& data) { + return getHMACSHA1Internal(key, data); +} + +bool CommonCryptoCryptoProvider::isMD5AllowedForCrypto() const { + return true; +} + diff --git a/Swiften/Crypto/CommonCryptoCryptoProvider.h b/Swiften/Crypto/CommonCryptoCryptoProvider.h new file mode 100644 index 0000000..f921e17 --- /dev/null +++ b/Swiften/Crypto/CommonCryptoCryptoProvider.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/Base/Override.h> + +namespace Swift { + class CommonCryptoCryptoProvider : public CryptoProvider { + public: + CommonCryptoCryptoProvider(); + ~CommonCryptoCryptoProvider(); + + virtual Hash* createSHA1() SWIFTEN_OVERRIDE; + virtual Hash* createMD5() SWIFTEN_OVERRIDE; + virtual ByteArray getHMACSHA1(const SafeByteArray& key, const ByteArray& data) SWIFTEN_OVERRIDE; + virtual ByteArray getHMACSHA1(const ByteArray& key, const ByteArray& data) SWIFTEN_OVERRIDE; + virtual bool isMD5AllowedForCrypto() const SWIFTEN_OVERRIDE; + }; +} diff --git a/Swiften/Crypto/CryptoProvider.cpp b/Swiften/Crypto/CryptoProvider.cpp new file mode 100644 index 0000000..0189de4 --- /dev/null +++ b/Swiften/Crypto/CryptoProvider.cpp @@ -0,0 +1,14 @@ +/*
+ * Copyright (c) 2013 Remko Tronçon
+ * Licensed under the GNU General Public License.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Crypto/CryptoProvider.h>
+
+#include <boost/shared_ptr.hpp>
+
+using namespace Swift;
+
+CryptoProvider::~CryptoProvider() {
+}
diff --git a/Swiften/Crypto/CryptoProvider.h b/Swiften/Crypto/CryptoProvider.h new file mode 100644 index 0000000..c1e1eb9 --- /dev/null +++ b/Swiften/Crypto/CryptoProvider.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Base/API.h> +#include <Swiften/Base/ByteArray.h> +#include <Swiften/Base/SafeByteArray.h> +#include <Swiften/Crypto/Hash.h> + +namespace Swift { + class Hash; + + class SWIFTEN_API CryptoProvider { + public: + virtual ~CryptoProvider(); + + virtual Hash* createSHA1() = 0; + virtual Hash* createMD5() = 0; + virtual ByteArray getHMACSHA1(const SafeByteArray& key, const ByteArray& data) = 0; + virtual ByteArray getHMACSHA1(const ByteArray& key, const ByteArray& data) = 0; + virtual bool isMD5AllowedForCrypto() const = 0; + + // Convenience + template<typename T> ByteArray getSHA1Hash(const T& data) { + return boost::shared_ptr<Hash>(createSHA1())->update(data).getHash(); + } + + template<typename T> ByteArray getMD5Hash(const T& data) { + return boost::shared_ptr<Hash>(createMD5())->update(data).getHash(); + } + }; +} diff --git a/Swiften/Crypto/Hash.cpp b/Swiften/Crypto/Hash.cpp new file mode 100644 index 0000000..35c7e70 --- /dev/null +++ b/Swiften/Crypto/Hash.cpp @@ -0,0 +1,12 @@ +/*
+ * Copyright (c) 2013 Remko Tronçon
+ * Licensed under the GNU General Public License.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Crypto/Hash.h>
+
+using namespace Swift;
+
+Hash::~Hash() {
+}
diff --git a/Swiften/Crypto/Hash.h b/Swiften/Crypto/Hash.h new file mode 100644 index 0000000..37c6ec8 --- /dev/null +++ b/Swiften/Crypto/Hash.h @@ -0,0 +1,24 @@ +/*
+ * Copyright (c) 2013 Remko Tronçon
+ * Licensed under the GNU General Public License.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/SafeByteArray.h>
+
+namespace Swift {
+ class Hash {
+ public:
+ virtual ~Hash();
+
+ virtual Hash& update(const ByteArray& data) = 0;
+ virtual Hash& update(const SafeByteArray& data) = 0;
+
+ virtual std::vector<unsigned char> getHash() = 0;
+ };
+}
diff --git a/Swiften/Crypto/OpenSSLCryptoProvider.cpp b/Swiften/Crypto/OpenSSLCryptoProvider.cpp new file mode 100644 index 0000000..9b1d544 --- /dev/null +++ b/Swiften/Crypto/OpenSSLCryptoProvider.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Swiften/Crypto/OpenSSLCryptoProvider.h> + +#include <openssl/sha.h> +#include <openssl/md5.h> +#include <openssl/hmac.h> +#include <cassert> +#include <boost/numeric/conversion/cast.hpp> + +#include <Swiften/Crypto/Hash.h> +#include <Swiften/Base/ByteArray.h> + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +using namespace Swift; + +namespace { + class SHA1Hash : public Hash { + public: + SHA1Hash() : finalized(false) { + if (!SHA1_Init(&context)) { + assert(false); + } + } + + ~SHA1Hash() { + } + + virtual Hash& update(const ByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual Hash& update(const SafeByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual std::vector<unsigned char> getHash() { + assert(!finalized); + std::vector<unsigned char> result(SHA_DIGEST_LENGTH); + SHA1_Final(vecptr(result), &context); + return result; + } + + private: + template<typename ContainerType> + Hash& updateInternal(const ContainerType& data) { + assert(!finalized); + if (!SHA1_Update(&context, vecptr(data), data.size())) { + assert(false); + } + return *this; + } + + private: + SHA_CTX context; + bool finalized; + }; + + class MD5Hash : public Hash { + public: + MD5Hash() : finalized(false) { + if (!MD5_Init(&context)) { + assert(false); + } + } + + ~MD5Hash() { + } + + virtual Hash& update(const ByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual Hash& update(const SafeByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual std::vector<unsigned char> getHash() { + assert(!finalized); + std::vector<unsigned char> result(MD5_DIGEST_LENGTH); + MD5_Final(vecptr(result), &context); + return result; + } + + private: + template<typename ContainerType> + Hash& updateInternal(const ContainerType& data) { + assert(!finalized); + if (!MD5_Update(&context, vecptr(data), data.size())) { + assert(false); + } + return *this; + } + + private: + MD5_CTX context; + bool finalized; + }; + + + template<typename T> + ByteArray getHMACSHA1Internal(const T& key, const ByteArray& data) { + unsigned int len = SHA_DIGEST_LENGTH; + std::vector<unsigned char> result(len); + HMAC(EVP_sha1(), vecptr(key), boost::numeric_cast<int>(key.size()), vecptr(data), data.size(), vecptr(result), &len); + return result; + } +} + +OpenSSLCryptoProvider::OpenSSLCryptoProvider() { +} + +OpenSSLCryptoProvider::~OpenSSLCryptoProvider() { +} + +Hash* OpenSSLCryptoProvider::createSHA1() { + return new SHA1Hash(); +} + +Hash* OpenSSLCryptoProvider::createMD5() { + return new MD5Hash(); +} + +ByteArray OpenSSLCryptoProvider::getHMACSHA1(const SafeByteArray& key, const ByteArray& data) { + return getHMACSHA1Internal(key, data); +} + +ByteArray OpenSSLCryptoProvider::getHMACSHA1(const ByteArray& key, const ByteArray& data) { + return getHMACSHA1Internal(key, data); +} + +bool OpenSSLCryptoProvider::isMD5AllowedForCrypto() const { + return true; +} + diff --git a/Swiften/Crypto/OpenSSLCryptoProvider.h b/Swiften/Crypto/OpenSSLCryptoProvider.h new file mode 100644 index 0000000..9440aee --- /dev/null +++ b/Swiften/Crypto/OpenSSLCryptoProvider.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/Base/Override.h> + +namespace Swift { + class OpenSSLCryptoProvider : public CryptoProvider { + public: + OpenSSLCryptoProvider(); + ~OpenSSLCryptoProvider(); + + virtual Hash* createSHA1() SWIFTEN_OVERRIDE; + virtual Hash* createMD5() SWIFTEN_OVERRIDE; + virtual ByteArray getHMACSHA1(const SafeByteArray& key, const ByteArray& data) SWIFTEN_OVERRIDE; + virtual ByteArray getHMACSHA1(const ByteArray& key, const ByteArray& data) SWIFTEN_OVERRIDE; + virtual bool isMD5AllowedForCrypto() const SWIFTEN_OVERRIDE; + }; +} diff --git a/Swiften/Crypto/PlatformCryptoProvider.cpp b/Swiften/Crypto/PlatformCryptoProvider.cpp new file mode 100644 index 0000000..ab0fa7b --- /dev/null +++ b/Swiften/Crypto/PlatformCryptoProvider.cpp @@ -0,0 +1,32 @@ +/*
+ * Copyright (c) 2013 Remko Tronçon
+ * Licensed under the GNU General Public License.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/Crypto/PlatformCryptoProvider.h>
+
+#include <cassert>
+
+#include <Swiften/Base/Platform.h>
+#if defined(SWIFTEN_PLATFORM_WIN32)
+#include <Swiften/Crypto/WindowsCryptoProvider.h>
+#elif defined(HAVE_COMMONCRYPTO_CRYPTO_PROVIDER)
+#include <Swiften/Crypto/CommonCryptoCryptoProvider.h>
+#elif defined(HAVE_OPENSSL_CRYPTO_PROVIDER)
+#include <Swiften/Crypto/OpenSSLCryptoProvider.h>
+#endif
+
+using namespace Swift;
+
+CryptoProvider* PlatformCryptoProvider::create() {
+#if defined(SWIFTEN_PLATFORM_WIN32)
+ return new WindowsCryptoProvider();
+#elif defined(HAVE_COMMONCRYPTO_CRYPTO_PROVIDER)
+ return new CommonCryptoCryptoProvider();
+#elif defined(HAVE_OPENSSL_CRYPTO_PROVIDER)
+ return new OpenSSLCryptoProvider();
+#endif
+ assert(false);
+ return NULL;
+}
diff --git a/Swiften/Crypto/PlatformCryptoProvider.h b/Swiften/Crypto/PlatformCryptoProvider.h new file mode 100644 index 0000000..0721887 --- /dev/null +++ b/Swiften/Crypto/PlatformCryptoProvider.h @@ -0,0 +1,17 @@ +/*
+ * Copyright (c) 2013 Remko Tronçon
+ * Licensed under the GNU General Public License.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+
+namespace Swift {
+ class CryptoProvider;
+
+ namespace PlatformCryptoProvider {
+ SWIFTEN_API CryptoProvider* create();
+ }
+}
diff --git a/Swiften/Crypto/SConscript b/Swiften/Crypto/SConscript new file mode 100644 index 0000000..ce4bdae --- /dev/null +++ b/Swiften/Crypto/SConscript @@ -0,0 +1,28 @@ +Import("swiften_env", "env")
+
+
+objects = swiften_env.SwiftenObject([
+ "CryptoProvider.cpp",
+ "Hash.cpp"
+])
+
+myenv = swiften_env.Clone()
+if myenv["PLATFORM"] == "win32" :
+ objects += myenv.SwiftenObject(["WindowsCryptoProvider.cpp"])
+if myenv.get("HAVE_OPENSSL", False) :
+ myenv.Append(CPPDEFINES = ["HAVE_OPENSSL_CRYPTO_PROVIDER"])
+ objects += myenv.SwiftenObject(["OpenSSLCryptoProvider.cpp"])
+if myenv["PLATFORM"] == "darwin" and myenv["target"] == "native" :
+ myenv.Append(CPPDEFINES = ["HAVE_COMMONCRYPTO_CRYPTO_PROVIDER"])
+ objects += myenv.SwiftenObject(["CommonCryptoCryptoProvider.cpp"])
+
+objects += myenv.SwiftenObject(["PlatformCryptoProvider.cpp"])
+
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
+
+if env["TEST"] :
+ test_env = myenv.Clone()
+ test_env.UseFlags(swiften_env["CPPUNIT_FLAGS"])
+ env.Append(UNITTEST_OBJECTS = test_env.SwiftenObject([
+ File("UnitTest/CryptoProviderTest.cpp"),
+ ]))
diff --git a/Swiften/Crypto/UnitTest/CryptoProviderTest.cpp b/Swiften/Crypto/UnitTest/CryptoProviderTest.cpp new file mode 100644 index 0000000..1e2275a --- /dev/null +++ b/Swiften/Crypto/UnitTest/CryptoProviderTest.cpp @@ -0,0 +1,156 @@ +/*
+ * Copyright (c) 2010-2013 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/Platform.h>
+#include <QA/Checker/IO.h>
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#ifdef SWIFTEN_PLATFORM_WIN32
+#include <Swiften/Crypto/WindowsCryptoProvider.h>
+#endif
+#ifdef HAVE_OPENSSL_CRYPTO_PROVIDER
+#include <Swiften/Crypto/OpenSSLCryptoProvider.h>
+#endif
+#ifdef HAVE_OPENSSL_CRYPTO_PROVIDER
+#include <Swiften/Crypto/CommonCryptoCryptoProvider.h>
+#endif
+#include <Swiften/Crypto/Hash.h>
+
+using namespace Swift;
+
+template <typename CryptoProviderType>
+class CryptoProviderTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(CryptoProviderTest);
+
+ CPPUNIT_TEST(testGetSHA1Hash);
+ CPPUNIT_TEST(testGetSHA1Hash_TwoUpdates);
+ CPPUNIT_TEST(testGetSHA1Hash_NoData);
+ CPPUNIT_TEST(testGetSHA1HashStatic);
+ CPPUNIT_TEST(testGetSHA1HashStatic_Twice);
+ CPPUNIT_TEST(testGetSHA1HashStatic_NoData);
+
+ CPPUNIT_TEST(testGetMD5Hash_Empty);
+ CPPUNIT_TEST(testGetMD5Hash_Alphabet);
+ CPPUNIT_TEST(testMD5Incremental);
+
+ CPPUNIT_TEST(testGetHMACSHA1);
+ CPPUNIT_TEST(testGetHMACSHA1_KeyLongerThanBlockSize);
+
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ provider = new CryptoProviderType();
+ }
+
+ void tearDown() {
+ delete provider;
+ }
+
+ ////////////////////////////////////////////////////////////
+ // SHA-1
+ ////////////////////////////////////////////////////////////
+
+ void testGetSHA1Hash() {
+ boost::shared_ptr<Hash> sha = boost::shared_ptr<Hash>(provider->createSHA1());
+ sha->update(createByteArray("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<"));
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), sha->getHash());
+ }
+
+ void testGetSHA1Hash_TwoUpdates() {
+ boost::shared_ptr<Hash> sha = boost::shared_ptr<Hash>(provider->createSHA1());
+ sha->update(createByteArray("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<"));
+ sha->update(createByteArray("http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<"));
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), sha->getHash());
+ }
+
+ void testGetSHA1Hash_NoData() {
+ boost::shared_ptr<Hash> sha = boost::shared_ptr<Hash>(provider->createSHA1());
+ sha->update(std::vector<unsigned char>());
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09"), sha->getHash());
+ }
+
+ void testGetSHA1HashStatic() {
+ ByteArray result(provider->getSHA1Hash(createByteArray("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<")));
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), result);
+ }
+
+
+ void testGetSHA1HashStatic_Twice() {
+ ByteArray input(createByteArray("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<"));
+ provider->getSHA1Hash(input);
+ ByteArray result(provider->getSHA1Hash(input));
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), result);
+ }
+
+ void testGetSHA1HashStatic_NoData() {
+ ByteArray result(provider->getSHA1Hash(ByteArray()));
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09"), result);
+ }
+
+
+ ////////////////////////////////////////////////////////////
+ // MD5
+ ////////////////////////////////////////////////////////////
+
+ void testGetMD5Hash_Empty() {
+ ByteArray result(provider->getMD5Hash(createByteArray("")));
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\x09\x98\xec\xf8\x42\x7e", 16), result);
+ }
+
+ void testGetMD5Hash_Alphabet() {
+ ByteArray result(provider->getMD5Hash(createByteArray("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")));
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\xd1\x74\xab\x98\xd2\x77\xd9\xf5\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", 16), result);
+ }
+
+ void testMD5Incremental() {
+ boost::shared_ptr<Hash> testling = boost::shared_ptr<Hash>(provider->createMD5());
+ testling->update(createByteArray("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+ testling->update(createByteArray("abcdefghijklmnopqrstuvwxyz0123456789"));
+
+ ByteArray result = testling->getHash();
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\xd1\x74\xab\x98\xd2\x77\xd9\xf5\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", 16), result);
+ }
+
+
+ ////////////////////////////////////////////////////////////
+ // HMAC-SHA1
+ ////////////////////////////////////////////////////////////
+
+ void testGetHMACSHA1() {
+ ByteArray result(provider->getHMACSHA1(createSafeByteArray("foo"), createByteArray("foobar")));
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\xa4\xee\xba\x8e\x63\x3d\x77\x88\x69\xf5\x68\xd0\x5a\x1b\x3d\xc7\x2b\xfd\x4\xdd"), result);
+ }
+
+ void testGetHMACSHA1_KeyLongerThanBlockSize() {
+ ByteArray result(provider->getHMACSHA1(createSafeByteArray("---------|---------|---------|---------|---------|----------|---------|"), createByteArray("foobar")));
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\xd6""n""\x8f""P|1""\xd3"",""\x6"" ""\xb9\xe3""gg""\x8e\xcf"" ]+""\xa"), result);
+ }
+
+ private:
+ CryptoProviderType* provider;
+};
+
+#ifdef SWIFTEN_PLATFORM_WIN32
+CPPUNIT_TEST_SUITE_REGISTRATION(CryptoProviderTest<WindowsCryptoProvider>);
+#endif
+#ifdef HAVE_OPENSSL_CRYPTO_PROVIDER
+CPPUNIT_TEST_SUITE_REGISTRATION(CryptoProviderTest<OpenSSLCryptoProvider>);
+#endif
+#ifdef HAVE_COMMONCRYPTO_CRYPTO_PROVIDER
+CPPUNIT_TEST_SUITE_REGISTRATION(CryptoProviderTest<CommonCryptoCryptoProvider>);
+#endif
diff --git a/Swiften/Crypto/WindowsCryptoProvider.cpp b/Swiften/Crypto/WindowsCryptoProvider.cpp new file mode 100644 index 0000000..9ca4c14 --- /dev/null +++ b/Swiften/Crypto/WindowsCryptoProvider.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2012 Kevin Smith + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Swiften/Crypto/WindowsCryptoProvider.h> + +#include <Windows.h> +#define SECURITY_WIN32 +#include <security.h> +#include <Wincrypt.h> +#include <cassert> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Crypto/Hash.h> +#include <Swiften/Base/ByteArray.h> +#include <Swiften/Base/Algorithm.h> +#include <Swiften/Base/WindowsRegistry.h> + +using namespace Swift; + +struct WindowsCryptoProvider::Private { + HCRYPTPROV context; +}; + +namespace { + class WindowsHash : public Hash { + public: + WindowsHash(HCRYPTPROV context, ALG_ID algorithm) : hash(NULL) { + if (!CryptCreateHash(context, algorithm, 0, 0, &hash)) { + assert(false); + } + } + + ~WindowsHash() { + CryptDestroyHash(hash); + } + + virtual Hash& update(const ByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual Hash& update(const SafeByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual std::vector<unsigned char> getHash() { + std::vector<unsigned char> result; + DWORD hashLength = sizeof(DWORD); + DWORD hashSize; + CryptGetHashParam(hash, HP_HASHSIZE, reinterpret_cast<BYTE*>(&hashSize), &hashLength, 0); + result.resize(static_cast<size_t>(hashSize)); + if (!CryptGetHashParam(hash, HP_HASHVAL, vecptr(result), &hashSize, 0)) { + assert(false); + } + result.resize(static_cast<size_t>(hashSize)); + return result; + } + + private: + template<typename ContainerType> + Hash& updateInternal(const ContainerType& data) { + if (!CryptHashData(hash, const_cast<BYTE*>(vecptr(data)), data.size(), 0)) { + assert(false); + } + return *this; + } + + private: + HCRYPTHASH hash; + }; + +#if 0 // NOT YET DONE + // Haven't tested the code below properly yet, but figured out after writing + // it that PLAINTEXTKEYBLOB doesn't work on XP or 2k, and the workaround is a + // bit too ugly to try this now. So, using our own algorithm for now. See + // http://support.microsoft.com/kb/228786/en-us + + // MSDN describes this as PLAINTEXTKEYBLOB, but this struct doesn't exist, + // and seems to even conflict with the PLAINTEXTKEYBLOB constant. Redefining + // here. + struct PlainTextKeyBlob { + BLOBHEADER hdr; + DWORD dwKeySize; + }; + + class HMACHash : public Hash { + public: + template<typename T> + HMACHash(HCRYPTPROV context, const T& rawKey) : hash(NULL) { + // Import raw key + T blobData(sizeof(PlainTextKeyBlob) + rawKey.size()); + PlainTextKeyBlob* blob = reinterpret_cast<PlainTextKeyBlob*>(vecptr(blobData)); + blob->hdr.bType = PLAINTEXTKEYBLOB; + blob->hdr.bVersion = CUR_BLOB_VERSION; + blob->hdr.reserved = 0; + blob->hdr.aiKeyAlg = CALG_RC2; + blob->dwKeySize = rawKey.size(); + std::copy(rawKey.begin(), rawKey.end(), blobData.begin() + sizeof(PlainTextKeyBlob)); + HCRYPTKEY key; + if (!CryptImportKey(context, vecptr(blobData), blobData.size(), 0, CRYPT_IPSEC_HMAC_KEY, &key)) { + assert(false); + return; + } + + // Create hash + if (!CryptCreateHash(context, CALG_HMAC, key, 0, &hash)) { + assert(false); + return; + } + ZeroMemory(&info, sizeof(info)); + info.HashAlgid = CALG_SHA1; + } + + ~HMACHash() { + CryptDestroyHash(hash); + } + + virtual Hash& update(const ByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual Hash& update(const SafeByteArray& data) SWIFTEN_OVERRIDE { + return updateInternal(data); + } + + virtual std::vector<unsigned char> getHash() { + std::vector<unsigned char> result; + DWORD hashLength = sizeof(DWORD); + DWORD hashSize; + CryptGetHashParam(hash, HP_HASHSIZE, reinterpret_cast<BYTE*>(&hashSize), &hashLength, 0); + result.resize(static_cast<size_t>(hashSize)); + if (!CryptGetHashParam(hash, HP_HASHVAL, vecptr(result), &hashSize, 0)) { + assert(false); + } + result.resize(static_cast<size_t>(hashSize)); + return result; + } + + private: + template<typename ContainerType> + Hash& updateInternal(const ContainerType& data) { + if (!CryptHashData(hash, const_cast<BYTE*>(vecptr(data)), data.size(), 0)) { + assert(false); + } + return *this; + } + + private: + HCRYPTHASH hash; + HMAC_INFO info; + }; +#endif + + // Simple implementation. + template<typename T> + ByteArray getHMACSHA1Internal(const T& key, const ByteArray& data, CryptoProvider* crypto) { + static const int BLOCK_SIZE = 64; + + T paddedKey; + if (key.size() <= BLOCK_SIZE) { + paddedKey = key; + } + else { + assign(paddedKey, crypto->getSHA1Hash(key)); + } + paddedKey.resize(BLOCK_SIZE, 0x0); + + // Create the first value + T x(paddedKey); + for (unsigned int i = 0; i < x.size(); ++i) { + x[i] ^= 0x36; + } + append(x, data); + + // Create the second value + T y(paddedKey); + for (unsigned int i = 0; i < y.size(); ++i) { + y[i] ^= 0x5c; + } + append(y, crypto->getSHA1Hash(x)); + return crypto->getSHA1Hash(y); + } +} + +WindowsCryptoProvider::WindowsCryptoProvider() { + p = boost::make_shared<Private>(); + if (!CryptAcquireContext(&p->context, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + assert(false); + } +} + +WindowsCryptoProvider::~WindowsCryptoProvider() { + CryptReleaseContext(p->context, 0); +} + +Hash* WindowsCryptoProvider::createSHA1() { + return new WindowsHash(p->context, CALG_SHA1); +} + +Hash* WindowsCryptoProvider::createMD5() { + return new WindowsHash(p->context, CALG_MD5); +} + +bool WindowsCryptoProvider::isMD5AllowedForCrypto() const { + return !WindowsRegistry::isFIPSEnabled(); +} + +ByteArray WindowsCryptoProvider::getHMACSHA1(const SafeByteArray& key, const ByteArray& data) { + return getHMACSHA1Internal(key, data, this); +} + +ByteArray WindowsCryptoProvider::getHMACSHA1(const ByteArray& key, const ByteArray& data) { + return getHMACSHA1Internal(key, data, this); +} diff --git a/Swiften/Crypto/WindowsCryptoProvider.h b/Swiften/Crypto/WindowsCryptoProvider.h new file mode 100644 index 0000000..9418fc0 --- /dev/null +++ b/Swiften/Crypto/WindowsCryptoProvider.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/Base/Override.h> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> + +namespace Swift { + class WindowsCryptoProvider : public CryptoProvider, public boost::noncopyable { + public: + WindowsCryptoProvider(); + ~WindowsCryptoProvider(); + + virtual Hash* createSHA1() SWIFTEN_OVERRIDE; + virtual Hash* createMD5() SWIFTEN_OVERRIDE; + virtual ByteArray getHMACSHA1(const SafeByteArray& key, const ByteArray& data) SWIFTEN_OVERRIDE; + virtual ByteArray getHMACSHA1(const ByteArray& key, const ByteArray& data) SWIFTEN_OVERRIDE; + virtual bool isMD5AllowedForCrypto() const SWIFTEN_OVERRIDE; + + private: + struct Private; + boost::shared_ptr<Private> p; + }; +} |