From 5c5a1f78b23bfe282ff29fdecfe778580222701f Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 20 Dec 2016 12:21:38 +0100 Subject: [PATCH] Added crypto.hpp for convenient C++ wrappings for commonly used OpenSSL functions --- .gitignore | 1 + crypto.hpp | 139 ++++++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 6 ++ tests/crypto_test.cpp | 68 +++++++++++++++++++++ 4 files changed, 214 insertions(+) create mode 100644 crypto.hpp create mode 100644 tests/crypto_test.cpp diff --git a/.gitignore b/.gitignore index 18ef062..562414a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ http_examples https_examples io_test parse_test +crypto_test diff --git a/crypto.hpp b/crypto.hpp new file mode 100644 index 0000000..f9b1d7e --- /dev/null +++ b/crypto.hpp @@ -0,0 +1,139 @@ +#ifndef CRYPTO_HPP +#define CRYPTO_HPP + +#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 { + 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 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 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 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; + } + }; +} +#endif /* CRYPTO_HPP */ + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b86e421..6c7034e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,3 +15,9 @@ endif() add_test(io_test io_test) add_test(parse_test parse_test) + +if(OPENSSL_FOUND) + add_executable(crypto_test crypto_test.cpp) + target_link_libraries(crypto_test ${OPENSSL_CRYPTO_LIBRARY}) + add_test(crypto_test crypto_test) +endif() diff --git a/tests/crypto_test.cpp b/tests/crypto_test.cpp new file mode 100644 index 0000000..22a84a6 --- /dev/null +++ b/tests/crypto_test.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include + +#include "crypto.hpp" + +using namespace std; +using namespace SimpleWeb; + +const vector > Base64_string_tests = { + {"", ""}, + {"f" , "Zg=="}, + {"fo", "Zm8="}, + {"foo", "Zm9v"}, + {"foob", "Zm9vYg=="}, + {"fooba", "Zm9vYmE="}, + {"foobar", "Zm9vYmFy"} +}; + +const vector > MD5_string_tests = { + {"", "d41d8cd98f00b204e9800998ecf8427e"}, + {"The quick brown fox jumps over the lazy dog", "9e107d9d372bb6826bd81d3542a419d6"} +}; + +const vector > SHA1_string_tests = { + {"", "da39a3ee5e6b4b0d3255bfef95601890afd80709"}, + {"The quick brown fox jumps over the lazy dog", "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"} +}; + +const vector > SHA256_string_tests = { + {"", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, + {"The quick brown fox jumps over the lazy dog", "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"} +}; + +const vector > SHA512_string_tests = { + {"", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"}, + {"The quick brown fox jumps over the lazy dog", "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6"} +}; + +int main() { + //Testing SimpleWeb::Crypt::Base64 + for(auto& string_test: Base64_string_tests) { + assert(Crypto::Base64::encode(string_test.first)==string_test.second); + assert(Crypto::Base64::decode(string_test.second)==string_test.first); + } + + //Testing SimpleWeb::Crypt::MD5 + for(auto& string_test: MD5_string_tests) + assert(Crypto::to_hex_string(Crypto::MD5(string_test.first)) == string_test.second); + + //Testing SimpleWeb::Crypt::SHA1 + for(auto& string_test: SHA1_string_tests) + assert(Crypto::to_hex_string(Crypto::SHA1(string_test.first)) == string_test.second); + + //Testing SimpleWeb::Crypt::SHA256 + for(auto& string_test: SHA256_string_tests) + assert(Crypto::to_hex_string(Crypto::SHA256(string_test.first)) == string_test.second); + + //Testing SimpleWeb::Crypt::SHA512 + for(auto& string_test: SHA512_string_tests) + assert(Crypto::to_hex_string(Crypto::SHA512(string_test.first)) == 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"); +}