1*4a64e381SAndroid Build Coastguard Worker #ifndef SIMPLE_WEB_CRYPTO_HPP 2*4a64e381SAndroid Build Coastguard Worker #define SIMPLE_WEB_CRYPTO_HPP 3*4a64e381SAndroid Build Coastguard Worker 4*4a64e381SAndroid Build Coastguard Worker #include <cmath> 5*4a64e381SAndroid Build Coastguard Worker #include <iomanip> 6*4a64e381SAndroid Build Coastguard Worker #include <istream> 7*4a64e381SAndroid Build Coastguard Worker #include <memory> 8*4a64e381SAndroid Build Coastguard Worker #include <sstream> 9*4a64e381SAndroid Build Coastguard Worker #include <string> 10*4a64e381SAndroid Build Coastguard Worker #include <vector> 11*4a64e381SAndroid Build Coastguard Worker 12*4a64e381SAndroid Build Coastguard Worker #include <openssl/buffer.h> 13*4a64e381SAndroid Build Coastguard Worker #include <openssl/evp.h> 14*4a64e381SAndroid Build Coastguard Worker #include <openssl/md5.h> 15*4a64e381SAndroid Build Coastguard Worker #include <openssl/sha.h> 16*4a64e381SAndroid Build Coastguard Worker 17*4a64e381SAndroid Build Coastguard Worker namespace SimpleWeb { 18*4a64e381SAndroid Build Coastguard Worker // TODO 2017: remove workaround for MSVS 2012 19*4a64e381SAndroid Build Coastguard Worker #if _MSC_VER == 1700 // MSVS 2012 has no definition for round() round(double x)20*4a64e381SAndroid Build Coastguard Worker inline double round(double x) noexcept { // Custom definition of round() for positive numbers 21*4a64e381SAndroid Build Coastguard Worker return floor(x + 0.5); 22*4a64e381SAndroid Build Coastguard Worker } 23*4a64e381SAndroid Build Coastguard Worker #endif 24*4a64e381SAndroid Build Coastguard Worker 25*4a64e381SAndroid Build Coastguard Worker class Crypto { 26*4a64e381SAndroid Build Coastguard Worker const static std::size_t buffer_size = 131072; 27*4a64e381SAndroid Build Coastguard Worker 28*4a64e381SAndroid Build Coastguard Worker public: 29*4a64e381SAndroid Build Coastguard Worker class Base64 { 30*4a64e381SAndroid Build Coastguard Worker public: 31*4a64e381SAndroid Build Coastguard Worker /// Returns Base64 encoded string from input string. encode(const std::string & input)32*4a64e381SAndroid Build Coastguard Worker static std::string encode(const std::string &input) noexcept { 33*4a64e381SAndroid Build Coastguard Worker std::string base64; 34*4a64e381SAndroid Build Coastguard Worker 35*4a64e381SAndroid Build Coastguard Worker BIO *bio, *b64; 36*4a64e381SAndroid Build Coastguard Worker auto bptr = BUF_MEM_new(); 37*4a64e381SAndroid Build Coastguard Worker 38*4a64e381SAndroid Build Coastguard Worker b64 = BIO_new(BIO_f_base64()); 39*4a64e381SAndroid Build Coastguard Worker BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); 40*4a64e381SAndroid Build Coastguard Worker bio = BIO_new(BIO_s_mem()); 41*4a64e381SAndroid Build Coastguard Worker BIO_push(b64, bio); 42*4a64e381SAndroid Build Coastguard Worker BIO_set_mem_buf(b64, bptr, BIO_CLOSE); 43*4a64e381SAndroid Build Coastguard Worker 44*4a64e381SAndroid Build Coastguard Worker // Write directly to base64-buffer to avoid copy 45*4a64e381SAndroid Build Coastguard Worker auto base64_length = static_cast<std::size_t>(round(4 * ceil(static_cast<double>(input.size()) / 3.0))); 46*4a64e381SAndroid Build Coastguard Worker base64.resize(base64_length); 47*4a64e381SAndroid Build Coastguard Worker bptr->length = 0; 48*4a64e381SAndroid Build Coastguard Worker bptr->max = base64_length + 1; 49*4a64e381SAndroid Build Coastguard Worker bptr->data = &base64[0]; 50*4a64e381SAndroid Build Coastguard Worker 51*4a64e381SAndroid Build Coastguard Worker if(BIO_write(b64, &input[0], static_cast<int>(input.size())) <= 0 || BIO_flush(b64) <= 0) 52*4a64e381SAndroid Build Coastguard Worker base64.clear(); 53*4a64e381SAndroid Build Coastguard Worker 54*4a64e381SAndroid Build Coastguard Worker // To keep &base64[0] through BIO_free_all(b64) 55*4a64e381SAndroid Build Coastguard Worker bptr->length = 0; 56*4a64e381SAndroid Build Coastguard Worker bptr->max = 0; 57*4a64e381SAndroid Build Coastguard Worker bptr->data = nullptr; 58*4a64e381SAndroid Build Coastguard Worker 59*4a64e381SAndroid Build Coastguard Worker BIO_free_all(b64); 60*4a64e381SAndroid Build Coastguard Worker 61*4a64e381SAndroid Build Coastguard Worker return base64; 62*4a64e381SAndroid Build Coastguard Worker } 63*4a64e381SAndroid Build Coastguard Worker 64*4a64e381SAndroid Build Coastguard Worker /// Returns Base64 decoded string from base64 input. decode(const std::string & base64)65*4a64e381SAndroid Build Coastguard Worker static std::string decode(const std::string &base64) noexcept { 66*4a64e381SAndroid Build Coastguard Worker std::string ascii((6 * base64.size()) / 8, '\0'); // The size is a up to two bytes too large. 67*4a64e381SAndroid Build Coastguard Worker 68*4a64e381SAndroid Build Coastguard Worker BIO *b64, *bio; 69*4a64e381SAndroid Build Coastguard Worker 70*4a64e381SAndroid Build Coastguard Worker b64 = BIO_new(BIO_f_base64()); 71*4a64e381SAndroid Build Coastguard Worker BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); 72*4a64e381SAndroid Build Coastguard Worker // TODO: Remove in 2022 or later 73*4a64e381SAndroid Build Coastguard Worker #if(defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x1000214fL) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2080000fL) 74*4a64e381SAndroid Build Coastguard Worker bio = BIO_new_mem_buf(const_cast<char *>(&base64[0]), static_cast<int>(base64.size())); 75*4a64e381SAndroid Build Coastguard Worker #else 76*4a64e381SAndroid Build Coastguard Worker bio = BIO_new_mem_buf(&base64[0], static_cast<int>(base64.size())); 77*4a64e381SAndroid Build Coastguard Worker #endif 78*4a64e381SAndroid Build Coastguard Worker bio = BIO_push(b64, bio); 79*4a64e381SAndroid Build Coastguard Worker 80*4a64e381SAndroid Build Coastguard Worker auto decoded_length = BIO_read(bio, &ascii[0], static_cast<int>(ascii.size())); 81*4a64e381SAndroid Build Coastguard Worker if(decoded_length > 0) 82*4a64e381SAndroid Build Coastguard Worker ascii.resize(static_cast<std::size_t>(decoded_length)); 83*4a64e381SAndroid Build Coastguard Worker else 84*4a64e381SAndroid Build Coastguard Worker ascii.clear(); 85*4a64e381SAndroid Build Coastguard Worker 86*4a64e381SAndroid Build Coastguard Worker BIO_free_all(b64); 87*4a64e381SAndroid Build Coastguard Worker 88*4a64e381SAndroid Build Coastguard Worker return ascii; 89*4a64e381SAndroid Build Coastguard Worker } 90*4a64e381SAndroid Build Coastguard Worker }; 91*4a64e381SAndroid Build Coastguard Worker 92*4a64e381SAndroid Build Coastguard Worker /// Returns hex string from bytes in input string. to_hex_string(const std::string & input)93*4a64e381SAndroid Build Coastguard Worker static std::string to_hex_string(const std::string &input) noexcept { 94*4a64e381SAndroid Build Coastguard Worker std::stringstream hex_stream; 95*4a64e381SAndroid Build Coastguard Worker hex_stream << std::hex << std::internal << std::setfill('0'); 96*4a64e381SAndroid Build Coastguard Worker for(auto &byte : input) 97*4a64e381SAndroid Build Coastguard Worker hex_stream << std::setw(2) << static_cast<int>(static_cast<unsigned char>(byte)); 98*4a64e381SAndroid Build Coastguard Worker return hex_stream.str(); 99*4a64e381SAndroid Build Coastguard Worker } 100*4a64e381SAndroid Build Coastguard Worker 101*4a64e381SAndroid Build Coastguard Worker /// Return hash value using specific EVP_MD from input string. message_digest(const std::string & str,const EVP_MD * evp_md,std::size_t digest_length)102*4a64e381SAndroid Build Coastguard Worker static std::string message_digest(const std::string &str, const EVP_MD *evp_md, std::size_t digest_length) noexcept { 103*4a64e381SAndroid Build Coastguard Worker std::string md(digest_length, '\0'); 104*4a64e381SAndroid Build Coastguard Worker 105*4a64e381SAndroid Build Coastguard Worker auto ctx = EVP_MD_CTX_create(); 106*4a64e381SAndroid Build Coastguard Worker EVP_MD_CTX_init(ctx); 107*4a64e381SAndroid Build Coastguard Worker EVP_DigestInit_ex(ctx, evp_md, nullptr); 108*4a64e381SAndroid Build Coastguard Worker EVP_DigestUpdate(ctx, str.data(), str.size()); 109*4a64e381SAndroid Build Coastguard Worker EVP_DigestFinal_ex(ctx, reinterpret_cast<unsigned char *>(&md[0]), nullptr); 110*4a64e381SAndroid Build Coastguard Worker EVP_MD_CTX_destroy(ctx); 111*4a64e381SAndroid Build Coastguard Worker 112*4a64e381SAndroid Build Coastguard Worker return md; 113*4a64e381SAndroid Build Coastguard Worker } 114*4a64e381SAndroid Build Coastguard Worker 115*4a64e381SAndroid Build Coastguard Worker /// Return hash value using specific EVP_MD from input stream. stream_digest(std::istream & stream,const EVP_MD * evp_md,std::size_t digest_length)116*4a64e381SAndroid Build Coastguard Worker static std::string stream_digest(std::istream &stream, const EVP_MD *evp_md, std::size_t digest_length) noexcept { 117*4a64e381SAndroid Build Coastguard Worker std::string md(digest_length, '\0'); 118*4a64e381SAndroid Build Coastguard Worker std::unique_ptr<char[]> buffer(new char[buffer_size]); 119*4a64e381SAndroid Build Coastguard Worker std::streamsize read_length; 120*4a64e381SAndroid Build Coastguard Worker 121*4a64e381SAndroid Build Coastguard Worker auto ctx = EVP_MD_CTX_create(); 122*4a64e381SAndroid Build Coastguard Worker EVP_MD_CTX_init(ctx); 123*4a64e381SAndroid Build Coastguard Worker EVP_DigestInit_ex(ctx, evp_md, nullptr); 124*4a64e381SAndroid Build Coastguard Worker while((read_length = stream.read(buffer.get(), buffer_size).gcount()) > 0) 125*4a64e381SAndroid Build Coastguard Worker EVP_DigestUpdate(ctx, buffer.get(), static_cast<std::size_t>(read_length)); 126*4a64e381SAndroid Build Coastguard Worker EVP_DigestFinal_ex(ctx, reinterpret_cast<unsigned char *>(&md[0]), nullptr); 127*4a64e381SAndroid Build Coastguard Worker EVP_MD_CTX_destroy(ctx); 128*4a64e381SAndroid Build Coastguard Worker 129*4a64e381SAndroid Build Coastguard Worker return md; 130*4a64e381SAndroid Build Coastguard Worker } 131*4a64e381SAndroid Build Coastguard Worker 132*4a64e381SAndroid Build Coastguard Worker /// Returns md5 hash value from input string. md5(const std::string & input,std::size_t iterations=1)133*4a64e381SAndroid Build Coastguard Worker static std::string md5(const std::string &input, std::size_t iterations = 1) noexcept { 134*4a64e381SAndroid Build Coastguard Worker auto evp_md = EVP_md5(); 135*4a64e381SAndroid Build Coastguard Worker auto hash = message_digest(input, evp_md, MD5_DIGEST_LENGTH); 136*4a64e381SAndroid Build Coastguard Worker for(std::size_t i = 1; i < iterations; ++i) 137*4a64e381SAndroid Build Coastguard Worker hash = message_digest(hash, evp_md, MD5_DIGEST_LENGTH); 138*4a64e381SAndroid Build Coastguard Worker return hash; 139*4a64e381SAndroid Build Coastguard Worker } 140*4a64e381SAndroid Build Coastguard Worker 141*4a64e381SAndroid Build Coastguard Worker /// Returns md5 hash value from input stream. md5(std::istream & stream,std::size_t iterations=1)142*4a64e381SAndroid Build Coastguard Worker static std::string md5(std::istream &stream, std::size_t iterations = 1) noexcept { 143*4a64e381SAndroid Build Coastguard Worker auto evp_md = EVP_md5(); 144*4a64e381SAndroid Build Coastguard Worker auto hash = stream_digest(stream, evp_md, MD5_DIGEST_LENGTH); 145*4a64e381SAndroid Build Coastguard Worker for(std::size_t i = 1; i < iterations; ++i) 146*4a64e381SAndroid Build Coastguard Worker hash = message_digest(hash, evp_md, MD5_DIGEST_LENGTH); 147*4a64e381SAndroid Build Coastguard Worker return hash; 148*4a64e381SAndroid Build Coastguard Worker } 149*4a64e381SAndroid Build Coastguard Worker 150*4a64e381SAndroid Build Coastguard Worker /// Returns sha1 hash value from input string. sha1(const std::string & input,std::size_t iterations=1)151*4a64e381SAndroid Build Coastguard Worker static std::string sha1(const std::string &input, std::size_t iterations = 1) noexcept { 152*4a64e381SAndroid Build Coastguard Worker auto evp_md = EVP_sha1(); 153*4a64e381SAndroid Build Coastguard Worker auto hash = message_digest(input, evp_md, SHA_DIGEST_LENGTH); 154*4a64e381SAndroid Build Coastguard Worker for(std::size_t i = 1; i < iterations; ++i) 155*4a64e381SAndroid Build Coastguard Worker hash = message_digest(hash, evp_md, SHA_DIGEST_LENGTH); 156*4a64e381SAndroid Build Coastguard Worker return hash; 157*4a64e381SAndroid Build Coastguard Worker } 158*4a64e381SAndroid Build Coastguard Worker 159*4a64e381SAndroid Build Coastguard Worker /// Returns sha1 hash value from input stream. sha1(std::istream & stream,std::size_t iterations=1)160*4a64e381SAndroid Build Coastguard Worker static std::string sha1(std::istream &stream, std::size_t iterations = 1) noexcept { 161*4a64e381SAndroid Build Coastguard Worker auto evp_md = EVP_sha1(); 162*4a64e381SAndroid Build Coastguard Worker auto hash = stream_digest(stream, evp_md, SHA_DIGEST_LENGTH); 163*4a64e381SAndroid Build Coastguard Worker for(std::size_t i = 1; i < iterations; ++i) 164*4a64e381SAndroid Build Coastguard Worker hash = message_digest(hash, evp_md, SHA_DIGEST_LENGTH); 165*4a64e381SAndroid Build Coastguard Worker return hash; 166*4a64e381SAndroid Build Coastguard Worker } 167*4a64e381SAndroid Build Coastguard Worker 168*4a64e381SAndroid Build Coastguard Worker /// Returns sha256 hash value from input string. sha256(const std::string & input,std::size_t iterations=1)169*4a64e381SAndroid Build Coastguard Worker static std::string sha256(const std::string &input, std::size_t iterations = 1) noexcept { 170*4a64e381SAndroid Build Coastguard Worker auto evp_md = EVP_sha256(); 171*4a64e381SAndroid Build Coastguard Worker auto hash = message_digest(input, evp_md, SHA256_DIGEST_LENGTH); 172*4a64e381SAndroid Build Coastguard Worker for(std::size_t i = 1; i < iterations; ++i) 173*4a64e381SAndroid Build Coastguard Worker hash = message_digest(hash, evp_md, SHA256_DIGEST_LENGTH); 174*4a64e381SAndroid Build Coastguard Worker return hash; 175*4a64e381SAndroid Build Coastguard Worker } 176*4a64e381SAndroid Build Coastguard Worker 177*4a64e381SAndroid Build Coastguard Worker /// Returns sha256 hash value from input stream. sha256(std::istream & stream,std::size_t iterations=1)178*4a64e381SAndroid Build Coastguard Worker static std::string sha256(std::istream &stream, std::size_t iterations = 1) noexcept { 179*4a64e381SAndroid Build Coastguard Worker auto evp_md = EVP_sha256(); 180*4a64e381SAndroid Build Coastguard Worker auto hash = stream_digest(stream, evp_md, SHA256_DIGEST_LENGTH); 181*4a64e381SAndroid Build Coastguard Worker for(std::size_t i = 1; i < iterations; ++i) 182*4a64e381SAndroid Build Coastguard Worker hash = message_digest(hash, evp_md, SHA256_DIGEST_LENGTH); 183*4a64e381SAndroid Build Coastguard Worker return hash; 184*4a64e381SAndroid Build Coastguard Worker } 185*4a64e381SAndroid Build Coastguard Worker 186*4a64e381SAndroid Build Coastguard Worker /// Returns sha512 hash value from input string. sha512(const std::string & input,std::size_t iterations=1)187*4a64e381SAndroid Build Coastguard Worker static std::string sha512(const std::string &input, std::size_t iterations = 1) noexcept { 188*4a64e381SAndroid Build Coastguard Worker auto evp_md = EVP_sha512(); 189*4a64e381SAndroid Build Coastguard Worker auto hash = message_digest(input, evp_md, SHA512_DIGEST_LENGTH); 190*4a64e381SAndroid Build Coastguard Worker for(std::size_t i = 1; i < iterations; ++i) 191*4a64e381SAndroid Build Coastguard Worker hash = message_digest(hash, evp_md, SHA512_DIGEST_LENGTH); 192*4a64e381SAndroid Build Coastguard Worker return hash; 193*4a64e381SAndroid Build Coastguard Worker } 194*4a64e381SAndroid Build Coastguard Worker 195*4a64e381SAndroid Build Coastguard Worker /// Returns sha512 hash value from input stream. sha512(std::istream & stream,std::size_t iterations=1)196*4a64e381SAndroid Build Coastguard Worker static std::string sha512(std::istream &stream, std::size_t iterations = 1) noexcept { 197*4a64e381SAndroid Build Coastguard Worker auto evp_md = EVP_sha512(); 198*4a64e381SAndroid Build Coastguard Worker auto hash = stream_digest(stream, evp_md, SHA512_DIGEST_LENGTH); 199*4a64e381SAndroid Build Coastguard Worker for(std::size_t i = 1; i < iterations; ++i) 200*4a64e381SAndroid Build Coastguard Worker hash = message_digest(hash, evp_md, SHA512_DIGEST_LENGTH); 201*4a64e381SAndroid Build Coastguard Worker return hash; 202*4a64e381SAndroid Build Coastguard Worker } 203*4a64e381SAndroid Build Coastguard Worker 204*4a64e381SAndroid Build Coastguard Worker /** 205*4a64e381SAndroid Build Coastguard Worker * Returns PBKDF2 derived key from the given password. 206*4a64e381SAndroid Build Coastguard Worker * 207*4a64e381SAndroid Build Coastguard Worker * @param password The password to derive key from. 208*4a64e381SAndroid Build Coastguard Worker * @param salt The salt to be used in the algorithm. 209*4a64e381SAndroid Build Coastguard Worker * @param iterations Number of iterations to be used in the algorithm. 210*4a64e381SAndroid Build Coastguard Worker * @param key_size Number of bytes of the returned key. 211*4a64e381SAndroid Build Coastguard Worker * 212*4a64e381SAndroid Build Coastguard Worker * @return The PBKDF2 derived key. 213*4a64e381SAndroid Build Coastguard Worker */ pbkdf2(const std::string & password,const std::string & salt,int iterations,int key_size)214*4a64e381SAndroid Build Coastguard Worker static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations, int key_size) noexcept { 215*4a64e381SAndroid Build Coastguard Worker std::string key(static_cast<std::size_t>(key_size), '\0'); 216*4a64e381SAndroid Build Coastguard Worker PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(), 217*4a64e381SAndroid Build Coastguard Worker reinterpret_cast<const unsigned char *>(salt.c_str()), salt.size(), iterations, 218*4a64e381SAndroid Build Coastguard Worker key_size, reinterpret_cast<unsigned char *>(&key[0])); 219*4a64e381SAndroid Build Coastguard Worker return key; 220*4a64e381SAndroid Build Coastguard Worker } 221*4a64e381SAndroid Build Coastguard Worker }; 222*4a64e381SAndroid Build Coastguard Worker } // namespace SimpleWeb 223*4a64e381SAndroid Build Coastguard Worker #endif /* SIMPLE_WEB_CRYPTO_HPP */ 224