How to create a custom encryption plugin in mariaDB
I want to create a custom plugin for encryption of tables as per my requirements , I used functions given in mariaDB docs to create the plugin.
I have this below C++ code for my custom encryption plugin and created a shared object(.so) file. but while running the query in mariadb(INSTALL PLUGIN JISA_Encryption_Plugin SONAME 'JISA_Enc_Plugin.so';) , below error is coming
ERROR 1126 (HY000): Can't open shared library 'JISA_Enc_Plugin.so' (errno: 8, API version for ENCRYPTION plugin JISA_Encryption_Plugin not supported by this version of the server)
I am using MariaDB version 10.6.
please check the below code and kindly help me on this.
code:-
- include <cstddef> for size_t
- include <map> for std::map
- include <cstring> for memcpy
- include <string>
- include <fstream>
- include <sstream>
- include <openssl/evp.h>
- include <openssl/aes.h>
- include <curl/curl.h>
- include "nlohmann/json.hpp" Assuming you have this header
using json = nlohmann::json;
- include <iostream>
- include <my_global.h>
- include <typelib.h>
- include <mysql/plugin_ftparser.h>
- include <mysql/plugin_encryption.h>
- include <mysql/service_my_crypt.h>
struct KeyEntry { unsigned int id; unsigned char key[32]; Example size, adjust as necessary unsigned int length; };
std::map<unsigned int, KeyEntry> keys; static char* filename; static char* filekey; static unsigned long encryption_algorithm;
static const char *encryption_algorithm_names[] = { "aes_cbc",
- ifdef HAVE_EncryptAes128Ctr "aes_ctr",
- endif 0 };
static TYPELIB encryption_algorithm_typelib = { array_elements(encryption_algorithm_names)-1, "", encryption_algorithm_names, NULL };
static MYSQL_SYSVAR_STR(filename, filename, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, "Path and name of the key file.", NULL, NULL, "");
static MYSQL_SYSVAR_STR(filekey, filekey, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, "Key to encrypt / decrypt the keyfile.", NULL, NULL, "");
- ifdef HAVE_EncryptAes128Ctr
- define RECOMMENDATION ", aes_ctr is the recommended one"
- else
- define RECOMMENDATION ""
- endif
static MYSQL_SYSVAR_ENUM(encryption_algorithm, encryption_algorithm, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, "Encryption algorithm to use" RECOMMENDATION ".", NULL, NULL, 0, &encryption_algorithm_typelib);
static struct st_mysql_sys_var* settings[] = { MYSQL_SYSVAR(filename), MYSQL_SYSVAR(filekey), MYSQL_SYSVAR(encryption_algorithm), NULL };
Function to read the key file void readKeyFile(const std::string& filename) { std::ifstream file(filename); if (!file.is_open()) { std::cerr << "Failed to open the file: " << filename << std::endl; return; }
std::string line; while (std::getline(file, line)) { std::istringstream iss(line); std::string id_str, key_str;
if (std::getline(iss, id_str, ';') && std::getline(iss, key_str)) { unsigned int id = std::stoi(id_str);
if (key_str.length() != 64) { Each byte is represented by 2 hex digits, 32 bytes * 2 = 64 std::cerr << "Invalid key length for ID " << id << std::endl; continue; }
KeyEntry entry; entry.id = id; entry.length = 32; Fixed length in this example
for (size_t i = 0; i < 32; ++i) { std::string byteString = key_str.substr(i * 2, 2); entry.key[i] = static_cast<unsigned char>(std::stoi(byteString, nullptr, 16)); }
keys[id] = entry; } } }
Function to get key from the keys map static KeyEntry* getKey(unsigned int key_id) { auto it = keys.find(key_id); if (it == keys.end()) { return nullptr; } return &it->second; }
Function to get the latest version of the key static unsigned int get_latest_key_version(unsigned int key_id) { return getKey(key_id) ? 1 : ENCRYPTION_KEY_VERSION_INVALID; }
Function to get the key from the key file static unsigned int get_key(unsigned int key_id, unsigned int key_version, unsigned char* dstbuf, unsigned *buflen) { if (key_version != 1) { return ENCRYPTION_KEY_VERSION_INVALID; }
KeyEntry* entry = getKey(key_id);
if (entry == NULL) { return ENCRYPTION_KEY_VERSION_INVALID; }
if (*buflen < entry->length) {
- buflen = entry->length; return ENCRYPTION_KEY_BUFFER_TOO_SMALL; }
- buflen = entry->length; if (dstbuf) { memcpy(dstbuf, entry->key, entry->length); }
return 0; }
- ifndef HAVE_EncryptAes128Gcm
- ifndef HAVE_EncryptAes128Ctr
- define MY_AES_CTR MY_AES_CBC
- endif
- define MY_AES_GCM MY_AES_CTR
- endif
static inline enum my_aes_mode mode(int flags) { if (encryption_algorithm) if (flags & ENCRYPTION_FLAG_NOPAD) return MY_AES_CTR; else return MY_AES_GCM; else return MY_AES_CBC; }
Implement AES encryption functions using OpenSSL int crypt_ctx_init(void *ctx, const unsigned char *key, unsigned int klen, const unsigned char *iv, unsigned int ivlen, int flags, unsigned int key_id, unsigned int key_version) { EVP_CIPHER_CTX *context = (EVP_CIPHER_CTX *)ctx; const EVP_CIPHER *cipher;
switch (mode(flags)) { case MY_AES_CBC: cipher = EVP_aes_256_cbc(); break; case MY_AES_CTR: cipher = EVP_aes_256_ctr(); break; default: return -1; }
EVP_CIPHER_CTX_init(context); if (EVP_EncryptInit_ex(context, cipher, NULL, key, iv) != 1) { return -1; }
return 0; }
int crypt_ctx_update(void *ctx, const unsigned char *src, unsigned int slen, unsigned char *dst, unsigned int *dlen) { EVP_CIPHER_CTX *context = (EVP_CIPHER_CTX *)ctx; int len;
if (EVP_EncryptUpdate(context, dst, &len, src, slen) != 1) { return -1; }
- dlen = len; return 0; }
int crypt_ctx_finish(void *ctx, unsigned char *dst, unsigned int *dlen) { EVP_CIPHER_CTX *context = (EVP_CIPHER_CTX *)ctx; int len;
if (EVP_EncryptFinal_ex(context, dst, &len) != 1) { return -1; }
- dlen = len; EVP_CIPHER_CTX_cleanup(context); return 0; }
unsigned int encrypted_length(unsigned int slen, unsigned int key_id, unsigned int key_version) { switch (mode(0)) { case MY_AES_CBC: return slen + EVP_CIPHER_block_size(EVP_aes_256_cbc()); case MY_AES_CTR: return slen; CTR mode does not change the length default: return 0; Unsupported mode } }
unsigned int crypt_ctx_size(unsigned int key_id, unsigned int key_version) { return sizeof(EVP_CIPHER_CTX*); }
struct st_mariadb_encryption JISA_Enc_plugin = { MARIA_PLUGIN_INTERFACE_VERSION, get_latest_key_version, get_key, crypt_ctx_size, crypt_ctx_init, crypt_ctx_update, crypt_ctx_finish, encrypted_length };
class Parser { public: Parser(const std::string& filename, const std::string& filekey) : m_filename(filename), m_filekey(filekey) { Additional initialization if needed }
int parse(std::map<unsigned int, KeyEntry>* keys) { readKeyFile(m_filename); Call to read the key file return 0; }
private: std::string m_filename; std::string m_filekey; };
static int fileKeyManagementPluginInit(void *p) { Parser parser(filename, filekey); return parser.parse(&keys); }
static int fileKeyManagementPluginDeinit(void *p) { keys.clear(); return 0; }
int _mysql_plugin_interface_version_ = MARIA_PLUGIN_INTERFACE_VERSION;
struct st_mysql_plugin _mysql_plugin_declarations_[] = { { MariaDB_ENCRYPTION_PLUGIN, &JISA_Enc_plugin, "JISA_Encryption_Plugin", "JISA-Softech", "User key management plugin", PLUGIN_LICENSE_GPL, fileKeyManagementPluginInit, fileKeyManagementPluginDeinit, 0x0100 /* 1.0 */, nullptr, /* status variables */ settings, nullptr, MariaDB_PLUGIN_MATURITY_STABLE }, {0, nullptr, nullptr, nullptr, nullptr, 0, nullptr, nullptr, 0, nullptr, nullptr, nullptr, 0} };
maria_declare_plugin(JISA_Enc_plugin) { MariaDB_ENCRYPTION_PLUGIN, &JISA_Enc_plugin, "JISA_Encryption_Plugin", "JISA-Softech", "User key management plugin", PLUGIN_LICENSE_GPL, fileKeyManagementPluginInit, fileKeyManagementPluginDeinit, 0x0100 /* 1.0 */, NULL, /* status variables */ settings, "1.0", MariaDB_PLUGIN_MATURITY_STABLE } maria_declare_plugin_end;