Added stream versions of the Crypto:: hash functions for calculating hashes from for instance large files

This commit is contained in:
eidheim 2016-12-21 10:07:22 +01:00
commit 92ddf86e15
2 changed files with 94 additions and 11 deletions

View file

@ -5,6 +5,8 @@
#include <cmath> #include <cmath>
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <istream>
#include <vector>
//Moving these to a seperate namespace for minimal global namespace cluttering does not work with clang++ //Moving these to a seperate namespace for minimal global namespace cluttering does not work with clang++
#include <openssl/evp.h> #include <openssl/evp.h>
@ -21,6 +23,7 @@ namespace SimpleWeb {
#endif #endif
class Crypto { class Crypto {
const static size_t buffer_size=131072;
public: public:
class Base64 { class Base64 {
public: public:
@ -97,6 +100,23 @@ namespace SimpleWeb {
return hash; 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<char> 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<unsigned char*>(&hash[0]), &context);
for (size_t c = 1; c < iterations; ++c)
MD5(reinterpret_cast<const unsigned char*>(&hash[0]), hash.size(), reinterpret_cast<unsigned char*>(&hash[0]));
return hash;
}
static std::string sha1(const std::string &input, size_t iterations=1) { static std::string sha1(const std::string &input, size_t iterations=1) {
std::string hash; std::string hash;
@ -109,6 +129,23 @@ namespace SimpleWeb {
return hash; 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<char> 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<unsigned char*>(&hash[0]), &context);
for (size_t c = 1; c < iterations; ++c)
SHA1(reinterpret_cast<const unsigned char*>(&hash[0]), hash.size(), reinterpret_cast<unsigned char*>(&hash[0]));
return hash;
}
static std::string sha256(const std::string &input, size_t iterations=1) { static std::string sha256(const std::string &input, size_t iterations=1) {
std::string hash; std::string hash;
@ -121,6 +158,23 @@ namespace SimpleWeb {
return hash; 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<char> 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<unsigned char*>(&hash[0]), &context);
for (size_t c = 1; c < iterations; ++c)
SHA256(reinterpret_cast<const unsigned char*>(&hash[0]), hash.size(), reinterpret_cast<unsigned char*>(&hash[0]));
return hash;
}
static std::string sha512(const std::string &input, size_t iterations=1) { static std::string sha512(const std::string &input, size_t iterations=1) {
std::string hash; std::string hash;
@ -134,15 +188,30 @@ namespace SimpleWeb {
return hash; return hash;
} }
/// Returns empty string on error. key_size is in bytes. static std::string sha512(std::istream &stream, size_t iterations=1) {
static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations = 4096, int key_size = 256 / 8) { SHA512_CTX context;
SHA512_Init(&context);
std::streamsize read_length;
std::vector<char> 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<unsigned char*>(&hash[0]), &context);
for (size_t c = 1; c < iterations; ++c)
SHA512(reinterpret_cast<const unsigned char*>(&hash[0]), hash.size(), reinterpret_cast<unsigned char*>(&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; std::string key;
key.resize(key_size); key.resize(key_size);
auto success = PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(), PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(),
reinterpret_cast<const unsigned char*>(salt.c_str()), salt.size(), iterations, reinterpret_cast<const unsigned char*>(salt.c_str()), salt.size(), iterations,
key_size, reinterpret_cast<unsigned char*>(&key[0])); key_size, reinterpret_cast<unsigned char*>(&key[0]));
if (!success)
return std::string();
return key; return key;
} }
}; };

View file

@ -42,21 +42,35 @@ int main() {
assert(Crypto::Base64::decode(string_test.second)==string_test.first); 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); 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); 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); 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); 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 //Testing iterations
assert(Crypto::to_hex_string(Crypto::sha1("Test", 1)) == "640ab2bae07bedc4c163f679a746f7ab7fb5d1fa"); assert(Crypto::to_hex_string(Crypto::sha1("Test", 1)) == "640ab2bae07bedc4c163f679a746f7ab7fb5d1fa");
assert(Crypto::to_hex_string(Crypto::sha1("Test", 2)) == "af31c6cbdecd88726d0a9b3798c71ef41f1624d5"); 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", 4096, 128 / 8)) == "f66df50f8aaa11e4d9721e1312ff2e66");
assert(Crypto::to_hex_string(Crypto::pbkdf2("Password", "Salt", 8192, 512 / 8)) == "a941ccbc34d1ee8ebbd1d34824a419c3dc4eac9cbc7c36ae6c7ca8725e2b618a6ad22241e787af937b0960cf85aa8ea3a258f243e05d3cc9b08af5dd93be046c"); assert(Crypto::to_hex_string(Crypto::pbkdf2("Password", "Salt", 8192, 512 / 8)) == "a941ccbc34d1ee8ebbd1d34824a419c3dc4eac9cbc7c36ae6c7ca8725e2b618a6ad22241e787af937b0960cf85aa8ea3a258f243e05d3cc9b08af5dd93be046c");