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.
| 124 | /// If something went wrong (can't init context or can't encrypt data) it throws exception. |
| 125 | /// It returns length of encrypted text. |
| 126 | size_t encrypt(std::string_view plaintext, char * ciphertext_and_tag, EncryptionMethod method, const String & key, const String & nonce) |
| 127 | { |
| 128 | int out_len = 0; |
| 129 | int ciphertext_len = 0; |
| 130 | |
| 131 | using EVP_CIPHER_CTX_ptr = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>; |
| 132 | const auto ctx = EVP_CIPHER_CTX_ptr(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); |
| 133 | if (!ctx) |
| 134 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_CIPHER_CTX_new failed: {}", getOpenSSLErrors()); |
| 135 | |
| 136 | using EVP_CIPHER_ptr = std::unique_ptr<EVP_CIPHER, decltype(&EVP_CIPHER_free)>; |
| 137 | const auto cipher = EVP_CIPHER_ptr(EVP_CIPHER_fetch(nullptr, getMethod(method), nullptr), EVP_CIPHER_free); |
| 138 | if (!cipher) |
| 139 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_CIPHER_fetch failed: {}", getOpenSSLErrors()); |
| 140 | |
| 141 | if (EVP_EncryptInit_ex(ctx.get(), cipher.get(), nullptr, nullptr, nullptr) != 1) |
| 142 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_EncryptInit_ex failed: {}", getOpenSSLErrors()); |
| 143 | |
| 144 | if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, static_cast<int32_t>(nonce.size()), nullptr) != 1) |
| 145 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_CIPHER_CTX_ctrl failed: {}", getOpenSSLErrors()); |
| 146 | |
| 147 | if (EVP_EncryptInit_ex(ctx.get(), nullptr, nullptr, |
| 148 | reinterpret_cast<const uint8_t*>(key.data()), |
| 149 | reinterpret_cast<const uint8_t *>(nonce.data())) != 1) |
| 150 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_EncryptInit_ex failed: {}", getOpenSSLErrors()); |
| 151 | |
| 152 | if (EVP_EncryptUpdate(ctx.get(), |
| 153 | reinterpret_cast<uint8_t *>(ciphertext_and_tag), |
| 154 | &out_len, |
| 155 | reinterpret_cast<const uint8_t *>(plaintext.data()), |
| 156 | static_cast<int32_t>(plaintext.size())) != 1) |
| 157 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_EncryptUpdate failed: {}", getOpenSSLErrors()); |
| 158 | |
| 159 | __msan_unpoison(ciphertext_and_tag, out_len); /// OpenSSL uses assembly which evades msan's analysis |
| 160 | |
| 161 | ciphertext_len = out_len; |
| 162 | |
| 163 | if (EVP_EncryptFinal_ex(ctx.get(), |
| 164 | reinterpret_cast<uint8_t *>(ciphertext_and_tag) + out_len, |
| 165 | reinterpret_cast<int32_t *>(&out_len)) != 1) |
| 166 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_EncryptFinal_ex failed: {}", getOpenSSLErrors()); |
| 167 | |
| 168 | __msan_unpoison(ciphertext_and_tag, out_len); /// OpenSSL uses assembly which evades msan's analysis |
| 169 | |
| 170 | ciphertext_len += out_len; |
| 171 | |
| 172 | /// Get the tag |
| 173 | if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, tag_size, reinterpret_cast<uint8_t *>(ciphertext_and_tag) + plaintext.size()) != 1) |
| 174 | throw Exception(ErrorCodes::OPENSSL_ERROR, "EVP_CIPHER_CTX_ctrl failed: {}", getOpenSSLErrors()); |
| 175 | |
| 176 | return ciphertext_len + tag_size; |
| 177 | } |
| 178 | |
| 179 | /// Encrypt plaintext with particular algorithm and put result into ciphertext_and_tag. |
| 180 | /// This function get key and nonce and encrypt text with their help. |
no test coverage detected