#ifndef CRYPTO_HPP #define CRYPTO_HPP #include #include #include #include #include #include //Moving these to a seperate namespace for minimal global namespace cluttering does not work with clang++ #include #include #include #include namespace SimpleWeb { //TODO 2017: remove workaround for MSVS 2012 #if _MSC_VER == 1700 //MSVS 2012 has no definition for round() inline double round(double x) { //custom definition of round() for positive numbers return floor(x + 0.5); } #endif class Crypto { const static size_t buffer_size=131072; public: class Base64 { public: static std::string encode(const std::string &ascii) { std::string base64; BIO *bio, *b64; BUF_MEM *bptr=BUF_MEM_new(); b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); bio = BIO_new(BIO_s_mem()); BIO_push(b64, bio); BIO_set_mem_buf(b64, bptr, BIO_CLOSE); //Write directly to base64-buffer to avoid copy int base64_length=static_cast(round(4*ceil((double)ascii.size()/3.0))); base64.resize(base64_length); bptr->length=0; bptr->max=base64_length+1; bptr->data=(char*)&base64[0]; BIO_write(b64, &ascii[0], static_cast(ascii.size())); BIO_flush(b64); //To keep &base64[0] through BIO_free_all(b64) bptr->length=0; bptr->max=0; bptr->data=nullptr; BIO_free_all(b64); return base64; } static std::string decode(const std::string &base64) { std::string ascii; //Resize ascii, however, the size is a up to two bytes too large. ascii.resize((6*base64.size())/8); BIO *b64, *bio; b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); bio = BIO_new_mem_buf((char*)&base64[0], static_cast(base64.size())); bio = BIO_push(b64, bio); int decoded_length = BIO_read(bio, &ascii[0], static_cast(ascii.size())); ascii.resize(decoded_length); BIO_free_all(b64); return ascii; } }; /// Return hex string from bytes in input string. static std::string to_hex_string(const std::string &input) { std::stringstream hex_stream; hex_stream << std::hex << std::internal << std::setfill('0'); for (auto &byte : input) hex_stream << std::setw(2) << static_cast(static_cast(byte)); return hex_stream.str(); } static std::string md5(const std::string &input, size_t iterations=1) { std::string hash; hash.resize(128 / 8); MD5(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); for (size_t c = 1; c < iterations; ++c) MD5(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); 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; hash.resize(160 / 8); SHA1(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); for (size_t c = 1; c < iterations; ++c) SHA1(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); 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; hash.resize(256 / 8); SHA256(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); for (size_t c = 1; c < iterations; ++c) SHA256(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); 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; hash.resize(512 / 8); SHA512(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); for (size_t c = 1; c < iterations; ++c) SHA512(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); return hash; } 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); 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; } }; } #endif /* CRYPTO_HPP */