diff --git a/crypto.hpp b/crypto.hpp index f99e31d..268950d 100644 --- a/crypto.hpp +++ b/crypto.hpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include //Moving these to a seperate namespace for minimal global namespace cluttering does not work with clang++ #include @@ -21,6 +23,7 @@ namespace SimpleWeb { #endif class Crypto { + const static size_t buffer_size=131072; public: class Base64 { public: @@ -97,6 +100,23 @@ namespace SimpleWeb { return hash; } + + static std::string md5(std::istream &stream, size_t iterations=1) { + MD5_CTX context; + MD5_Init(&context); + std::streamsize read_length; + std::vector buffer(buffer_size); + while((read_length=stream.read(&buffer[0], buffer_size).gcount())>0) + MD5_Update(&context, buffer.data(), read_length); + std::string hash; + hash.resize(128 / 8); + MD5_Final(reinterpret_cast(&hash[0]), &context); + + for (size_t c = 1; c < iterations; ++c) + MD5(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } static std::string sha1(const std::string &input, size_t iterations=1) { std::string hash; @@ -109,6 +129,23 @@ namespace SimpleWeb { return hash; } + + static std::string sha1(std::istream &stream, size_t iterations=1) { + SHA_CTX context; + SHA1_Init(&context); + std::streamsize read_length; + std::vector buffer(buffer_size); + while((read_length=stream.read(&buffer[0], buffer_size).gcount())>0) + SHA1_Update(&context, buffer.data(), read_length); + std::string hash; + hash.resize(160 / 8); + SHA1_Final(reinterpret_cast(&hash[0]), &context); + + for (size_t c = 1; c < iterations; ++c) + SHA1(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } static std::string sha256(const std::string &input, size_t iterations=1) { std::string hash; @@ -121,6 +158,23 @@ namespace SimpleWeb { return hash; } + + static std::string sha256(std::istream &stream, size_t iterations=1) { + SHA256_CTX context; + SHA256_Init(&context); + std::streamsize read_length; + std::vector buffer(buffer_size); + while((read_length=stream.read(&buffer[0], buffer_size).gcount())>0) + SHA256_Update(&context, buffer.data(), read_length); + std::string hash; + hash.resize(256 / 8); + SHA256_Final(reinterpret_cast(&hash[0]), &context); + + for (size_t c = 1; c < iterations; ++c) + SHA256(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } static std::string sha512(const std::string &input, size_t iterations=1) { std::string hash; @@ -134,15 +188,30 @@ namespace SimpleWeb { return hash; } - /// Returns empty string on error. key_size is in bytes. - static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations = 4096, int key_size = 256 / 8) { + static std::string sha512(std::istream &stream, size_t iterations=1) { + SHA512_CTX context; + SHA512_Init(&context); + std::streamsize read_length; + std::vector buffer(buffer_size); + while((read_length=stream.read(&buffer[0], buffer_size).gcount())>0) + SHA512_Update(&context, buffer.data(), read_length); + std::string hash; + hash.resize(512 / 8); + SHA512_Final(reinterpret_cast(&hash[0]), &context); + + for (size_t c = 1; c < iterations; ++c) + SHA512(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } + + /// key_size is number of bytes of the returned key. + static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations, int key_size) { std::string key; key.resize(key_size); - auto success = PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(), - reinterpret_cast(salt.c_str()), salt.size(), iterations, - key_size, reinterpret_cast(&key[0])); - if (!success) - return std::string(); + PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(), + reinterpret_cast(salt.c_str()), salt.size(), iterations, + key_size, reinterpret_cast(&key[0])); return key; } }; diff --git a/tests/crypto_test.cpp b/tests/crypto_test.cpp index 28684a9..26b8ce5 100644 --- a/tests/crypto_test.cpp +++ b/tests/crypto_test.cpp @@ -42,21 +42,35 @@ int main() { assert(Crypto::Base64::decode(string_test.second)==string_test.first); } - for(auto& string_test: md5_string_tests) + for(auto& string_test: md5_string_tests) { assert(Crypto::to_hex_string(Crypto::md5(string_test.first)) == string_test.second); + stringstream ss(string_test.first); + assert(Crypto::to_hex_string(Crypto::md5(ss)) == string_test.second); + } - for(auto& string_test: sha1_string_tests) + for(auto& string_test: sha1_string_tests) { assert(Crypto::to_hex_string(Crypto::sha1(string_test.first)) == string_test.second); + stringstream ss(string_test.first); + assert(Crypto::to_hex_string(Crypto::sha1(ss)) == string_test.second); + } - for(auto& string_test: sha256_string_tests) + for(auto& string_test: sha256_string_tests) { assert(Crypto::to_hex_string(Crypto::sha256(string_test.first)) == string_test.second); + stringstream ss(string_test.first); + assert(Crypto::to_hex_string(Crypto::sha256(ss)) == string_test.second); + } - for(auto& string_test: sha512_string_tests) + for(auto& string_test: sha512_string_tests) { assert(Crypto::to_hex_string(Crypto::sha512(string_test.first)) == string_test.second); + stringstream ss(string_test.first); + assert(Crypto::to_hex_string(Crypto::sha512(ss)) == string_test.second); + } //Testing iterations assert(Crypto::to_hex_string(Crypto::sha1("Test", 1)) == "640ab2bae07bedc4c163f679a746f7ab7fb5d1fa"); assert(Crypto::to_hex_string(Crypto::sha1("Test", 2)) == "af31c6cbdecd88726d0a9b3798c71ef41f1624d5"); + stringstream ss("Test"); + assert(Crypto::to_hex_string(Crypto::sha1(ss, 2)) == "af31c6cbdecd88726d0a9b3798c71ef41f1624d5"); assert(Crypto::to_hex_string(Crypto::pbkdf2("Password", "Salt", 4096, 128 / 8)) == "f66df50f8aaa11e4d9721e1312ff2e66"); assert(Crypto::to_hex_string(Crypto::pbkdf2("Password", "Salt", 8192, 512 / 8)) == "a941ccbc34d1ee8ebbd1d34824a419c3dc4eac9cbc7c36ae6c7ca8725e2b618a6ad22241e787af937b0960cf85aa8ea3a258f243e05d3cc9b08af5dd93be046c");