Skip to content

Commit a479185

Browse files
author
Joao Gramacho
committed
BUG#28720023 BINLOG_ENCRYPTION IS NOT WORKING WITH WOLFSSL
Problem ------- Binlog encryption feature was designed considering the following OpenSSL library functions behaviors: - EVP_BytesToKey function supports EVP_sha512; - EVP_EncryptUpdate function with padding always encrypts the whole source buffer keeping cipher context; - EVP_DecryptUpdate function with padding always decrypts the whole source buffer keeping cipher context and allows decrypting content to the same buffer as the source content; In current WolfSSL library version supported by MySQL 8.0.14 (3.14), the above mentioned functions behavior is not the same as OpenSSL's. This patch adapts the Rpl_cipher functions to WolfSSL functions behavior.
1 parent 0975d6c commit a479185

11 files changed

+747
-43
lines changed

cmake/ssl.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ MACRO (MYSQL_USE_WOLFSSL)
105105
-DOPENSSL_EXTRA
106106
-DSESSION_CERT
107107
-DWC_NO_HARDEN
108+
-DWOLFSSL_AES_COUNTER
108109
-DWOLFSSL_AES_DIRECT
109110
-DWOLFSSL_ALLOW_TLSV10
110111
-DWOLFSSL_CERT_EXT

share/errmsg-utf8.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8736,6 +8736,9 @@ ER_GRP_TRX_CONSISTENCY_BEGIN_NOT_ALLOWED
87368736
ER_FUNCTIONAL_INDEX_ROW_VALUE_IS_NOT_ALLOWED
87378737
eng "Expression of functional index '%s' cannot refer to a row value."
87388738

8739+
ER_RPL_ENCRYPTION_FAILED_TO_ENCRYPT
8740+
eng "Failed to encrypt content to write into binlog file: %s."
8741+
87398742
#
87408743
# End of 8.0 error messages.
87418744
#
@@ -18740,6 +18743,9 @@ ER_GRP_RPL_GTID_SET_EXTRACT_ERROR_DURING_RECOVERY
1874018743
ER_SECONDARY_ENGINE_PLUGIN
1874118744
eng "%s"
1874218745

18746+
ER_SERVER_RPL_ENCRYPTION_FAILED_TO_ENCRYPT
18747+
eng "Failed to encrypt content to write into binlog file: %s."
18748+
1874318749
#
1874418750
# End of 8.0 error messages intended to be logged to the server error log.
1874518751
#

sql/binlog.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3606,9 +3606,9 @@ bool MYSQL_BIN_LOG::open(PSI_file_key log_file_key, const char *log_name,
36063606
err:
36073607
if (binlog_error_action == ABORT_SERVER) {
36083608
exec_binlog_error_action_abort(
3609-
"Either disk is full or file system is read "
3610-
"only while opening the binlog. Aborting the"
3611-
" server.");
3609+
"Either disk is full, file system is read only or "
3610+
"there was an encryption error while opening the binlog. "
3611+
"Aborting the server.");
36123612
} else
36133613
LogErr(ERROR_LEVEL, ER_BINLOG_CANT_OPEN_FOR_LOGGING, name, errno);
36143614

@@ -4901,9 +4901,9 @@ bool MYSQL_BIN_LOG::open_binlog(
49014901
close_purge_index_file();
49024902
if (binlog_error_action == ABORT_SERVER) {
49034903
exec_binlog_error_action_abort(
4904-
"Either disk is full or file system is read "
4905-
"only while opening the binlog. Aborting the"
4906-
" server.");
4904+
"Either disk is full, file system is read only or "
4905+
"there was an encryption error while opening the binlog. "
4906+
"Aborting the server.");
49074907
} else {
49084908
LogErr(ERROR_LEVEL, ER_BINLOG_CANT_USE_FOR_LOGGING,
49094909
(new_name) ? new_name : name, errno);

sql/binlog_istream.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ const char *Binlog_read_error::get_str() const {
6565
"please check if keyring plugin is loaded";
6666
case READ_ENCRYPTED_LOG_FILE_IS_NOT_SUPPORTED:
6767
return "Reading encrypted log files directly is not supported.";
68+
case ERROR_DECRYPTING_FILE:
69+
return "Failed to decrypt content read from binlog file.";
6870
default:
6971
/* There must be something wrong in the code if it reaches this branch. */
7072
DBUG_ASSERT(0);
@@ -95,7 +97,8 @@ bool Binlog_encryption_istream::open(
9597
m_decryptor = encryption_header->get_decryptor();
9698
if (m_decryptor->open(password, encryption_header->get_header_size())) {
9799
m_decryptor.reset(nullptr);
98-
DBUG_RETURN(true);
100+
DBUG_RETURN(
101+
binlog_read_error->set_type(Binlog_read_error::ERROR_DECRYPTING_FILE));
99102
}
100103

101104
DBUG_RETURN(false);

sql/binlog_istream.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ class Binlog_read_error {
6969
BAD_BINLOG_MAGIC,
7070
INVALID_ENCRYPTION_HEADER,
7171
CANNOT_GET_FILE_PASSWORD,
72-
READ_ENCRYPTED_LOG_FILE_IS_NOT_SUPPORTED
72+
READ_ENCRYPTED_LOG_FILE_IS_NOT_SUPPORTED,
73+
ERROR_DECRYPTING_FILE
7374
};
7475

7576
Binlog_read_error() {}

sql/binlog_ostream.cc

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,27 @@ Binlog_cache_storage::~Binlog_cache_storage() { close(); }
149149

150150
Binlog_encryption_ostream::~Binlog_encryption_ostream() { close(); }
151151

152+
#define THROW_RPL_ENCRYPTION_FAILED_TO_ENCRYPT_ERROR \
153+
char err_msg[MYSQL_ERRMSG_SIZE]; \
154+
ERR_error_string_n(ERR_get_error(), err_msg, MYSQL_ERRMSG_SIZE); \
155+
LogErr(ERROR_LEVEL, ER_SERVER_RPL_ENCRYPTION_FAILED_TO_ENCRYPT, err_msg); \
156+
if (current_thd) { \
157+
if (current_thd->is_error()) current_thd->clear_error(); \
158+
my_error(ER_RPL_ENCRYPTION_FAILED_TO_ENCRYPT, MYF(0), err_msg); \
159+
}
160+
152161
bool Binlog_encryption_ostream::open(
153162
std::unique_ptr<Truncatable_ostream> down_ostream) {
154163
DBUG_ASSERT(down_ostream != nullptr);
155164
m_header = Rpl_encryption_header::get_new_default_header();
156165
const Key_string password_str = m_header->generate_new_file_password();
157166
m_encryptor.reset(nullptr);
158167
m_encryptor = m_header->get_encryptor();
159-
if (m_encryptor->open(password_str, m_header->get_header_size())) return true;
168+
if (m_encryptor->open(password_str, m_header->get_header_size())) {
169+
THROW_RPL_ENCRYPTION_FAILED_TO_ENCRYPT_ERROR;
170+
m_encryptor.reset(nullptr);
171+
return true;
172+
}
160173
m_down_ostream = std::move(down_ostream);
161174
return m_header->serialize(m_down_ostream.get());
162175
}
@@ -170,8 +183,12 @@ bool Binlog_encryption_ostream::open(
170183
m_header = std::move(header);
171184
m_encryptor.reset(nullptr);
172185
m_encryptor = m_header->get_encryptor();
173-
m_encryptor->open(m_header->decrypt_file_password(),
174-
m_header->get_header_size());
186+
if (m_encryptor->open(m_header->decrypt_file_password(),
187+
m_header->get_header_size())) {
188+
THROW_RPL_ENCRYPTION_FAILED_TO_ENCRYPT_ERROR;
189+
m_encryptor.reset(nullptr);
190+
return true;
191+
}
175192

176193
return seek(0);
177194
}
@@ -196,7 +213,11 @@ bool Binlog_encryption_ostream::write(const unsigned char *buffer,
196213
int encrypt_len =
197214
std::min(length, static_cast<my_off_t>(ENCRYPT_BUFFER_SIZE));
198215

199-
if (m_encryptor->encrypt(encrypt_buffer, ptr, encrypt_len)) return true;
216+
if (m_encryptor->encrypt(encrypt_buffer, ptr, encrypt_len)) {
217+
THROW_RPL_ENCRYPTION_FAILED_TO_ENCRYPT_ERROR;
218+
return true;
219+
}
220+
200221
if (m_down_ostream->write(encrypt_buffer, encrypt_len)) return true;
201222

202223
ptr += encrypt_len;

sql/rpl_cipher.cc

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,45 @@
1717
#include <algorithm>
1818
#include "my_byteorder.h"
1919

20+
#define HAVE_BYTESTOKEY_SHA512_HANDLING
21+
#define HAVE_DECRYPTION_INTO_SAME_SOURCE_BUFFER
22+
23+
#ifdef HAVE_WOLFSSL
24+
#undef HAVE_BYTESTOKEY_SHA512_HANDLING
25+
#undef HAVE_DECRYPTION_INTO_SAME_SOURCE_BUFFER
26+
#endif
27+
2028
int Rpl_cipher::get_header_size() { return m_header_size; }
2129

30+
std::unique_ptr<Rpl_cipher> Aes_ctr::get_encryptor() {
31+
std::unique_ptr<Rpl_cipher> cipher(new Aes_ctr_encryptor);
32+
return cipher;
33+
}
34+
35+
std::unique_ptr<Rpl_cipher> Aes_ctr::get_decryptor() {
36+
std::unique_ptr<Rpl_cipher> cipher(new Aes_ctr_decryptor);
37+
return cipher;
38+
}
39+
2240
template <Cipher_type TYPE>
2341
bool Aes_ctr_cipher<TYPE>::open(const Key_string &password, int header_size) {
2442
m_header_size = header_size;
25-
if (EVP_BytesToKey(EVP_aes_256_ctr(), EVP_sha512(), NULL, password.data(),
26-
password.length(), 1, m_file_key, m_iv) == 0)
43+
#ifdef HAVE_BYTESTOKEY_SHA512_HANDLING
44+
if (EVP_BytesToKey(Aes_ctr::get_evp_cipher(), Aes_ctr::get_evp_md(), NULL,
45+
password.data(), password.length(), 1, m_file_key,
46+
m_iv) == 0)
2747
return true;
48+
#else
49+
unsigned char buffer[64];
50+
unsigned int hash_size = 0;
51+
EVP_MD_CTX *ctx = EVP_MD_CTX_create();
52+
EVP_DigestInit_ex(ctx, Aes_ctr::get_evp_md(), nullptr);
53+
EVP_DigestUpdate(ctx, password.data(), password.length());
54+
EVP_DigestFinal_ex(ctx, buffer, &hash_size);
55+
EVP_MD_CTX_destroy(ctx);
56+
memcpy(m_file_key, buffer, FILE_KEY_LENGTH);
57+
memcpy(m_iv, buffer + FILE_KEY_LENGTH, AES_BLOCK_SIZE);
58+
#endif
2859

2960
/*
3061
AES-CTR counter is set to 0. Data stream is always encrypted beginning with
@@ -88,11 +119,11 @@ bool Aes_ctr_cipher<TYPE>::init_cipher(uint64_t offset) {
88119
std::swap(m_iv[11], m_iv[12]);
89120

90121
int res;
91-
/* EVP_EncryptInit_ex() return 1 for success and 0 for failure */
92-
if (TYPE == Cipher_type::ENCRYPT)
93-
res = EVP_EncryptInit_ex(m_ctx, EVP_aes_256_ctr(), NULL, m_file_key, m_iv);
94-
else
95-
res = EVP_DecryptInit_ex(m_ctx, EVP_aes_256_ctr(), NULL, m_file_key, m_iv);
122+
const EVP_CIPHER *cipher_type = Aes_ctr::get_evp_cipher();
123+
124+
/* EVP_CipherInit() returns 1 for success and 0 for failure */
125+
res = EVP_CipherInit(m_ctx, cipher_type, m_file_key, m_iv, TYPE);
126+
96127
DBUG_RETURN(res == 0);
97128
}
98129

@@ -105,34 +136,57 @@ void Aes_ctr_cipher<TYPE>::deinit_cipher() {
105136
template <Cipher_type TYPE>
106137
bool Aes_ctr_cipher<TYPE>::encrypt(unsigned char *dest,
107138
const unsigned char *src, int length) {
108-
int out_len = 0;
139+
DBUG_ENTER("Aes_ctr_cipher::encrypt");
109140

110141
if (TYPE == Cipher_type::DECRYPT) {
111142
/* It should never be called by a decrypt cipher */
112143
DBUG_ASSERT(0);
113-
return true;
144+
DBUG_RETURN(true);
114145
}
115146

116-
if (EVP_EncryptUpdate(m_ctx, dest, &out_len, src, length) == 0) return true;
147+
/* length == 0 : nothing to encrypt */
148+
if (length == 0) DBUG_RETURN(false);
149+
150+
if (EVP_Cipher(m_ctx, dest, (unsigned char *)src, length) == 0)
151+
DBUG_RETURN(true);
117152

118-
DBUG_ASSERT(out_len == length);
119-
return false;
153+
DBUG_RETURN(false);
120154
}
121155

122156
template <Cipher_type TYPE>
123157
bool Aes_ctr_cipher<TYPE>::decrypt(unsigned char *dest,
124158
const unsigned char *src, int length) {
125-
int out_len = 0;
159+
DBUG_ENTER("Aes_ctr_cipher::decrypt");
126160

127161
if (TYPE == Cipher_type::ENCRYPT) {
128162
/* It should never be called by an encrypt cipher */
129163
DBUG_ASSERT(0);
130-
return true;
164+
DBUG_RETURN(true);
165+
}
166+
167+
/* length == 0 : nothing to decrypt */
168+
if (length == 0) DBUG_RETURN(false);
169+
170+
#ifdef HAVE_DECRYPTION_INTO_SAME_SOURCE_BUFFER
171+
if (EVP_Cipher(m_ctx, dest, (unsigned char *)src, length) == 0)
172+
DBUG_RETURN(true);
173+
#else
174+
const int DECRYPTED_BUFFER_SIZE = AES_BLOCK_SIZE * 2048;
175+
unsigned char buffer[DECRYPTED_BUFFER_SIZE];
176+
177+
/* Decrypt in up to DECRYPTED_BUFFER_SIZE chunks */
178+
while (length != 0) {
179+
int chunk_len = std::min(length, DECRYPTED_BUFFER_SIZE);
180+
if (EVP_Cipher(m_ctx, buffer, (unsigned char *)src, chunk_len) == 0)
181+
DBUG_RETURN(true);
182+
memcpy(dest, buffer, chunk_len);
183+
src += chunk_len;
184+
dest += chunk_len;
185+
length -= chunk_len;
131186
}
187+
#endif
132188

133-
if (EVP_DecryptUpdate(m_ctx, dest, &out_len, src, length) == 0) return true;
134-
DBUG_ASSERT(out_len == length);
135-
return false;
189+
DBUG_RETURN(false);
136190
}
137191

138192
template class Aes_ctr_cipher<Cipher_type::ENCRYPT>;

sql/rpl_cipher.h

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define RPL_CIPHER_INCLUDED
1818

1919
#include <openssl/evp.h>
20+
#include <memory>
2021
#include <string>
2122

2223
/**
@@ -134,6 +135,42 @@ class Rpl_cipher {
134135
int m_header_size = 0;
135136
};
136137

138+
/**
139+
@class Aes_ctr
140+
141+
The class provides standards to be used by the Aes_ctr ciphers.
142+
*/
143+
class Aes_ctr {
144+
public:
145+
static const int PASSWORD_LENGTH = 32;
146+
static const int AES_BLOCK_SIZE = 16;
147+
static const int FILE_KEY_LENGTH = 32;
148+
/**
149+
Returns the message digest function to be uses when opening the cipher.
150+
151+
@return SHA-512 message digest.
152+
*/
153+
static const EVP_MD *get_evp_md() { return EVP_sha512(); };
154+
/**
155+
Returns the cipher to be uses when using the cipher.
156+
157+
@return AES-256-CTR.
158+
*/
159+
static const EVP_CIPHER *get_evp_cipher() { return EVP_aes_256_ctr(); };
160+
/**
161+
Returns a new unique Rpl_cipher encryptor.
162+
163+
@return A new Rpl_cipher encryptor.
164+
*/
165+
static std::unique_ptr<Rpl_cipher> get_encryptor();
166+
/**
167+
Returns a new unique Rpl_cipher decryptor.
168+
169+
@return A new Rpl_cipher decryptor.
170+
*/
171+
static std::unique_ptr<Rpl_cipher> get_decryptor();
172+
};
173+
137174
enum Cipher_type { ENCRYPT, DECRYPT };
138175

139176
/**
@@ -145,9 +182,9 @@ enum Cipher_type { ENCRYPT, DECRYPT };
145182
template <Cipher_type TYPE>
146183
class Aes_ctr_cipher : public Rpl_cipher {
147184
public:
148-
static const int PASSWORD_LENGTH = 32;
149-
static const int AES_BLOCK_SIZE = 16;
150-
static const int FILE_KEY_LENGTH = 32;
185+
static const int PASSWORD_LENGTH = Aes_ctr::PASSWORD_LENGTH;
186+
static const int AES_BLOCK_SIZE = Aes_ctr::AES_BLOCK_SIZE;
187+
static const int FILE_KEY_LENGTH = Aes_ctr::FILE_KEY_LENGTH;
151188

152189
virtual ~Aes_ctr_cipher();
153190

0 commit comments

Comments
 (0)