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