diff options
Diffstat (limited to 'Swiften/Crypto')
-rw-r--r-- | Swiften/Crypto/CommonCryptoCryptoProvider.cpp | 167 | ||||
-rw-r--r-- | Swiften/Crypto/CommonCryptoCryptoProvider.h | 24 | ||||
-rw-r--r-- | Swiften/Crypto/CryptoProvider.cpp | 14 | ||||
-rw-r--r-- | Swiften/Crypto/CryptoProvider.h | 39 | ||||
-rw-r--r-- | Swiften/Crypto/Hash.cpp | 12 | ||||
-rw-r--r-- | Swiften/Crypto/Hash.h | 24 | ||||
-rw-r--r-- | Swiften/Crypto/OpenSSLCryptoProvider.cpp | 173 | ||||
-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 | 256 | ||||
-rw-r--r-- | Swiften/Crypto/WindowsCryptoProvider.h | 30 |
14 files changed, 996 insertions, 0 deletions
diff --git a/Swiften/Crypto/CommonCryptoCryptoProvider.cpp b/Swiften/Crypto/CommonCryptoCryptoProvider.cpp new file mode 100644 index 0000000..f1810ba --- /dev/null +++ b/Swiften/Crypto/CommonCryptoCryptoProvider.cpp @@ -0,0 +1,167 @@ +/* + * 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> + class HMACHash : public Hash { + public: + HMACHash(const T& key) : finalized(false) { + CCHmacInit(&context, kCCHmacAlgSHA1, vecptr(key), key.size()); + } + + ~HMACHash() { + } + + 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); + CCHmacFinal(&context, vecptr(result)); + return result; + } + + private: + template<typename ContainerType> + Hash& updateInternal(const ContainerType& data) { + assert(!finalized); + CCHmacUpdate(&context, vecptr(data), boost::numeric_cast<CC_LONG>(data.size())); + return *this; + } + + private: + CCHmacContext context; + bool finalized; + }; +} + +CommonCryptoCryptoProvider::CommonCryptoCryptoProvider() { +} + +CommonCryptoCryptoProvider::~CommonCryptoCryptoProvider() { +} + +Hash* CommonCryptoCryptoProvider::createSHA1() { + return new SHA1Hash(); +} + +Hash* CommonCryptoCryptoProvider::createMD5() { + return new MD5Hash(); +} + +Hash* CommonCryptoCryptoProvider::createHMACSHA1(const SafeByteArray& key) { + return new HMACHash<SafeByteArray>(key); +} + +Hash* CommonCryptoCryptoProvider::createHMACSHA1(const ByteArray& key) { + return new HMACHash<ByteArray>(key); +} + + +bool CommonCryptoCryptoProvider::isMD5AllowedForCrypto() const { + return true; +} + diff --git a/Swiften/Crypto/CommonCryptoCryptoProvider.h b/Swiften/Crypto/CommonCryptoCryptoProvider.h new file mode 100644 index 0000000..8072c0d --- /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 Hash* createHMACSHA1(const SafeByteArray& key) SWIFTEN_OVERRIDE; + virtual Hash* createHMACSHA1(const ByteArray& key) 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..afee8c4 --- /dev/null +++ b/Swiften/Crypto/CryptoProvider.h @@ -0,0 +1,39 @@ +/*
+ * 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/ByteArray.h>
+#include <Swiften/Base/SafeByteArray.h>
+#include <Swiften/Crypto/Hash.h>
+
+namespace Swift {
+ class Hash;
+
+ class CryptoProvider {
+ public:
+ virtual ~CryptoProvider();
+
+ virtual Hash* createSHA1() = 0;
+ virtual Hash* createMD5() = 0;
+ virtual Hash* createHMACSHA1(const SafeByteArray& key) = 0;
+ virtual Hash* createHMACSHA1(const ByteArray& key) = 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();
+ }
+
+ template<typename T, typename U> ByteArray getHMACSHA1(const T& key, const U& data) {
+ return boost::shared_ptr<Hash>(createHMACSHA1(key))->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..afe73aa --- /dev/null +++ b/Swiften/Crypto/OpenSSLCryptoProvider.cpp @@ -0,0 +1,173 @@ +/* + * 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> + class HMACHash : public Hash { + public: + HMACHash(const T& key) : finalized(false) { + HMAC_CTX_init(&context); + HMAC_Init(&context, vecptr(key), boost::numeric_cast<int>(key.size()), EVP_sha1()); + } + + ~HMACHash() { + HMAC_CTX_cleanup(&context); + } + + 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); + unsigned int len = SHA_DIGEST_LENGTH; + HMAC_Final(&context, vecptr(result), &len); + return result; + } + + private: + template<typename ContainerType> + Hash& updateInternal(const ContainerType& data) { + assert(!finalized); + HMAC_Update(&context, vecptr(data), data.size()); + return *this; + } + + private: + HMAC_CTX context; + bool finalized; + }; +} + +OpenSSLCryptoProvider::OpenSSLCryptoProvider() { +} + +OpenSSLCryptoProvider::~OpenSSLCryptoProvider() { +} + +Hash* OpenSSLCryptoProvider::createSHA1() { + return new SHA1Hash(); +} + +Hash* OpenSSLCryptoProvider::createMD5() { + return new MD5Hash(); +} + +Hash* OpenSSLCryptoProvider::createHMACSHA1(const SafeByteArray& key) { + return new HMACHash<SafeByteArray>(key); +} + +Hash* OpenSSLCryptoProvider::createHMACSHA1(const ByteArray& key) { + return new HMACHash<ByteArray>(key); +} + +bool OpenSSLCryptoProvider::isMD5AllowedForCrypto() const { + return true; +} + diff --git a/Swiften/Crypto/OpenSSLCryptoProvider.h b/Swiften/Crypto/OpenSSLCryptoProvider.h new file mode 100644 index 0000000..e9ca37b --- /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 Hash* createHMACSHA1(const SafeByteArray& key) SWIFTEN_OVERRIDE; + virtual Hash* createHMACSHA1(const ByteArray& key) 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..7cc6a46 --- /dev/null +++ b/Swiften/Crypto/WindowsCryptoProvider.cpp @@ -0,0 +1,256 @@ +/*
+ * 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
+ // 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. You can only call 'update' once.
+ template<typename T>
+ class HMACHash : public Hash {
+ public:
+ HMACHash(const T& key, CryptoProvider* crypto) : crypto(crypto), finished(false) {
+ if (key.size() <= BLOCK_SIZE) {
+ paddedKey = key;
+ }
+ else {
+ assign(paddedKey, crypto->getSHA1Hash(key));
+ }
+ paddedKey.resize(BLOCK_SIZE, 0x0);
+ }
+
+ ~HMACHash() {
+ }
+
+ 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() {
+ return result;
+ }
+
+ private:
+ template<typename ContainerType>
+ Hash& updateInternal(const ContainerType& data) {
+ assert(!finished);
+
+ // 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));
+ result = crypto->getSHA1Hash(y);
+
+ finished = true;
+ return *this;
+ }
+
+ private:
+ static const int BLOCK_SIZE = 64;
+ CryptoProvider* crypto;
+ T paddedKey;
+ SafeByteArray data;
+ bool finished;
+ std::vector<unsigned char> result;
+ };
+
+
+}
+
+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();
+}
+
+Hash* WindowsCryptoProvider::createHMACSHA1(const SafeByteArray& key) {
+ return new HMACHash<SafeByteArray>(key, this);
+}
+
+Hash* WindowsCryptoProvider::createHMACSHA1(const ByteArray& key) {
+ return new HMACHash<ByteArray>(key, this);
+}
diff --git a/Swiften/Crypto/WindowsCryptoProvider.h b/Swiften/Crypto/WindowsCryptoProvider.h new file mode 100644 index 0000000..242d4c2 --- /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 Hash* createHMACSHA1(const SafeByteArray& key) SWIFTEN_OVERRIDE;
+ virtual Hash* createHMACSHA1(const ByteArray& key) SWIFTEN_OVERRIDE;
+ virtual bool isMD5AllowedForCrypto() const SWIFTEN_OVERRIDE;
+
+ private:
+ struct Private;
+ boost::shared_ptr<Private> p;
+ };
+}
|