Encrypt plaintext with particular algorithm and put result into ciphertext_and_tag. This function get key and nonce and encrypt text with their help. If something went wrong (can't init context or can't encrypt data) it throws exception. It returns length of encrypted text.
| 181 | /// If something went wrong (can't init context or can't encrypt data) it throws exception. |
| 182 | /// It returns length of encrypted text. |
| 183 | size_t decrypt(std::string_view ciphertext, char * plaintext, EncryptionMethod method, const String & key, const String & nonce) |
| 184 | { |
| 185 | int out_len = 0; |
| 186 | int plaintext_len = 0; |
| 187 | |
| 188 | using EVP_CIPHER_CTX_ptr = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>; |
| 189 | const auto ctx = EVP_CIPHER_CTX_ptr(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); |
| 190 | if (!ctx) |
| 191 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_CIPHER_CTX_new failed: {}", getOpenSSLErrors()); |
| 192 | |
| 193 | using EVP_CIPHER_ptr = std::unique_ptr<EVP_CIPHER, decltype(&EVP_CIPHER_free)>; |
| 194 | const auto cipher = EVP_CIPHER_ptr(EVP_CIPHER_fetch(nullptr, getMethod(method), nullptr), EVP_CIPHER_free); |
| 195 | if (!cipher) |
| 196 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_CIPHER_fetch failed: {}", getOpenSSLErrors()); |
| 197 | |
| 198 | if (EVP_DecryptInit_ex(ctx.get(), cipher.get(), nullptr, nullptr, nullptr) != 1) |
| 199 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_DecryptInit_ex failed: {}", getOpenSSLErrors()); |
| 200 | |
| 201 | if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, static_cast<int32_t>(nonce.size()), nullptr) != 1) |
| 202 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_CIPHER_CTX_ctrl failed: {}", getOpenSSLErrors()); |
| 203 | |
| 204 | if (EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, |
| 205 | reinterpret_cast<const uint8_t*>(key.data()), |
| 206 | reinterpret_cast<const uint8_t *>(nonce.data())) != 1) |
| 207 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_DecryptInit_ex failed: {}", getOpenSSLErrors()); |
| 208 | |
| 209 | if (EVP_CIPHER_CTX_ctrl(ctx.get(), |
| 210 | EVP_CTRL_GCM_SET_TAG, |
| 211 | tag_size, |
| 212 | reinterpret_cast<uint8_t *>(const_cast<char *>(ciphertext.data())) + ciphertext.size() - tag_size) != 1) |
| 213 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_CIPHER_CTX_ctrl failed: {}", getOpenSSLErrors()); |
| 214 | |
| 215 | if (EVP_DecryptUpdate(ctx.get(), |
| 216 | reinterpret_cast<uint8_t *>(plaintext), |
| 217 | reinterpret_cast<int32_t *>(&out_len), |
| 218 | reinterpret_cast<const uint8_t *>(ciphertext.data()), |
| 219 | static_cast<int32_t>(ciphertext.size()) - tag_size) != 1) |
| 220 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_DecryptUpdate failed: {}", getOpenSSLErrors()); |
| 221 | |
| 222 | __msan_unpoison(plaintext, out_len); /// OpenSSL uses assembly which evades msan's analysis |
| 223 | |
| 224 | plaintext_len = out_len; |
| 225 | |
| 226 | if (EVP_DecryptFinal_ex(ctx.get(), |
| 227 | reinterpret_cast<uint8_t *>(plaintext) + out_len, |
| 228 | reinterpret_cast<int32_t *>(&out_len)) != 1) |
| 229 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_DecryptFinal_ex failed: {}", getOpenSSLErrors()); |
| 230 | |
| 231 | __msan_unpoison(plaintext, out_len); /// OpenSSL uses assembly which evades msan's analysis |
| 232 | |
| 233 | return plaintext_len + out_len; |
| 234 | } |
| 235 | |
| 236 | /// Register codec in factory |
| 237 | void registerEncryptionCodec(CompressionCodecFactory & factory, EncryptionMethod Method) |
no test coverage detected