This is a read-only copy of the MariaDB Knowledgebase generated on 2024-11-16. For the latest, interactive version please visit https://mariadb.com/kb/.

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;

Content reproduced on this site is the property of its respective owners, and this content is not reviewed in advance by MariaDB. The views, information and opinions expressed by this content do not necessarily represent those of MariaDB or any other party.