diff --git a/.gitignore b/.gitignore index 30c6ad467..4b69f29e1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /CMakeSettings.json .vs out +build +.vscode diff --git a/README.md b/README.md index 243513d50..e4f955cf1 100644 --- a/README.md +++ b/README.md @@ -1,138 +1,135 @@ # MySQL Connector/C++ -This is a release of MySQL Connector/C++, [the C++ interface](https://dev.mysql.com/doc/dev/connector-cpp/8.0/) for communicating with MySQL servers. +## ABE支持 -For detailed information please visit the official [MySQL Connector/C++ documentation](https://dev.mysql.com/doc/dev/connector-cpp/8.0/). +### 使用 -## Licensing +要使用ABE功能,需引用abe_extern.h头文件。 -Please refer to files README and LICENSE, available in this repository, and [Legal Notices in documentation](https://dev.mysql.com/doc/connector-cpp/8.0/en/preface.html) for further details. +开放接口和使用方法可参考如下示例: -## Download & Install - -MySQL Connector/C++ can be installed from pre-compiled packages that can be downloaded from the [MySQL downloads page](https://dev.mysql.com/downloads/connector/cpp/). -The process of installing of Connector/C++ from a binary distribution is described in [MySQL online manuals](https://dev.mysql.com/doc/connector-cpp/8.0/en/connector-cpp-installation-binary.html) - -### Building from sources - -MySQL Connector/C++ can be installed from the source. Please check [MySQL online manuals](https://dev.mysql.com/doc/connector-cpp/8.0/en/connector-cpp-installation-source.html) - -### GitHub Repository - -This repository contains the MySQL Connector/C++ source code as per latest released version. You should expect to see the same contents here and within the latest released Connector/C++ package. - -## Sample Code - -``` +```cpp #include -#include - -using ::std::cout; -using ::std::endl; -using namespace ::mysqlx; - - -int main(int argc, const char* argv[]) -try { - - const char *url = (argc > 1 ? argv[1] : "mysqlx://root@127.0.0.1"); - - cout << "Creating session on " << url - << " ..." << endl; - - Session sess(url); - - cout <<"Session accepted, creating collection..." < ids = add.getGeneratedIds(); - for (string id : ids) - cout <<"- added doc with id: " << id < 1 and name like 'ba%'").execute(); - - int i = 0; - for (DbDoc doc : docs) - { - cout <<"doc#" < +#include +#include +#include +using namespace mysqlx; +using std::cout; +using std::endl; +using std::vector; + +//数据库连接 +Session get_connect(){ + //注意 mysqlcppconn8 默认使用的端口:33060,若连接3306端口会提示错误 + //使用用户名:testabe + //用户 root 的密码是:123456 + //主机:172.17.0.9 + //端口:33060 + //数据库:company + cout << "start connecting...\n"; + + SessionSettings option("172.17.0.9", 33060, "testabe", "123456"); + Session sess(option); + + cout <<"Done!" <() <<" "; + cout << (*it).get(1).get(); //这个string是mysqlx的string,继承自std::u16string + cout << endl; } - - cout << endl; - } - cout <<"Done!" <() + */ + abe_query query = env.sql("select id,title,abe_dec(data) from share;"); + RowResult rs2 = query.execute(); + // cout << "real_sql = " << query.real_sql << endl; + for (auto it = rs2.begin();it != rs2.end();++it){ + cout << (*it).get(0).get() << "\t"; + cout << (*it).get(1).get() << "\t"; + auto ustr = (*it).get(2).get(); + try{ + cout << env.recover(ustr); + }catch(const Error &e){ + cout << "can't decrypt"; + } + cout << endl; + } + cout << "abe query end." << endl; + } -catch (const char *ex) +int main() { - cout <<"EXCEPTION: " < +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "openssl/crypto.h" +#include "mysqlx/abe/abe_crypto.h" +#include "mysqlx/abe/base64.h" + +namespace mysqlx{ +MYSQLX_ABI_BEGIN(2,0) +namespace abe{ + +void abe_crypto::encrypt(std::string pt, std::string policy, std::string &ct){ + oabe::OpenABEStateContext thread_context; + thread_context.initializeThread(); + oabe::OpenABECryptoContext cpabe("CP-ABE"); + cpabe.importPublicParams(mpk); + cpabe.encrypt(policy.c_str(), pt, ct); + thread_context.shutdownThread(); +} + +void abe_crypto::decrypt(const std::string ct, std::string &pt){ + + if(!check_abe_key()){ + ABE_ERROR("no abe key!"); + } + + oabe::OpenABEStateContext thread_context; + thread_context.initializeThread(); + oabe::OpenABECryptoContext cpabe("CP-ABE"); + cpabe.importPublicParams(mpk); + cpabe.importUserKey(user.user_id.c_str(), user.user_key); + if(!cpabe.decrypt(user.user_id.c_str(), ct, pt)){ + thread_context.shutdownThread(); + ABE_ERROR("abe: can't decrypt."); + } + thread_context.shutdownThread(); +} + +bool abe_crypto::check_abe_key(){ + if(user.user_key == ""){ + return false; + } + return true; +} + + +void abe_crypto::init(std::string mpk_path, std::string key_path, + std::string kms_cert_path, std::string db_cert_path, + std::string rsa_sk_path){ + import_mpk(mpk_path); + import_db_cert(db_cert_path); + import_kms_cert(kms_cert_path); + import_sk(rsa_sk_path); + if(!import_user_key(key_path)){ //abe_user_key可以之后获取 + user.user_key = ""; + } +} + +void abe_crypto::import_mpk(std::string mpk_path){ + //读入mpk + std::ifstream ifs_mpk(mpk_path, std::ios::in); + if(!ifs_mpk){ + ifs_mpk.close(); + ABE_ERROR2("error opening security pameter (mpk) file.\nmpk_path=", mpk_path); + } + ifs_mpk>>mpk; + ifs_mpk.close(); +} + +bool abe_crypto::import_user_key(std::string key_path){ + //读入abe_user_key + std::ifstream ifs_key(key_path, std::ios::in); + if(!ifs_key){ + return false; + } + ifs_key>>user.user_key; + ifs_key.close(); + return true; +} + +void abe_crypto::save_user_key(std::string key_path, std::string key_str_b64){ + std::string pt; + + //key_str为base64编码 + size_t key_str_b64_length = key_str_b64.length(); + char * key_str = (char*)malloc(base64_utils::b64_dec_len(key_str_b64_length)); + size_t key_str_length = base64_utils::b64_decode(key_str_b64.c_str(), key_str_b64_length, (char*)key_str); + // base64_utils::b64_decode(key_str_b64.c_str(), key_str_b64_length, (char*)key_str); + + std::string ct(key_str,key_str_length); + if(!rsa_decrypt(ct, pt)){ + free(key_str); + ABE_ERROR("failed to decrypt abe user key"); + } + free(key_str); + + //写入abe_user_key + std::ofstream ofs_key(key_path, std::ios::out); + if(!ofs_key){ + ABE_ERROR2("error opening user key-file.\nkey_path=" , key_path); + } + ofs_key << pt; + user.user_key = pt; + ofs_key.close(); +} + +void abe_crypto::import_sk(std::string rsa_sk_path){ + // 导入rsa密钥文件并读取密钥 + FILE *hPriKeyFile = fopen(rsa_sk_path.c_str(), "rb"); + if (hPriKeyFile == NULL) + { + ABE_ERROR2("read file failed, file_path=", rsa_sk_path); + } + std::string strRet; + RSA *pRSAPriKey = RSA_new(); + if (PEM_read_RSAPrivateKey(hPriKeyFile, &pRSAPriKey, 0, 0) == NULL) + { // 密钥读取失败 + // assert(false); + RSA_free(pRSAPriKey); + fclose(hPriKeyFile); + ABE_ERROR2("read rsa prikey failed, file_path=", rsa_sk_path); + } + sk = pRSAPriKey; + fclose(hPriKeyFile); +} + +RSA * abe_crypto::import_pk(const std::string cert_path, std::string &err_msg){ + RSA * pk; + // 导入证书文件并读取公钥 + FILE *hPubKeyFile = fopen(cert_path.c_str(), "rb"); + if (hPubKeyFile == NULL) + { + err_msg = "failed to open cert file"; + return NULL; + } + X509 *cert = PEM_read_X509(hPubKeyFile, nullptr, nullptr, nullptr); + if(cert == NULL){ + err_msg = "failed to read publib key from cert file"; + fclose(hPubKeyFile); + return NULL; + } + fclose(hPubKeyFile); + + EVP_PKEY *evp_key = X509_get_pubkey(cert); + if(evp_key == NULL){ + err_msg = "failed to get publib key from cert file"; + X509_free(cert); + return NULL; + } + X509_free(cert); + + pk = EVP_PKEY_get1_RSA(evp_key); + if(pk == NULL){ + err_msg = "failed to get rsa publib key from cert file"; + EVP_PKEY_free(evp_key); + return NULL; + } + EVP_PKEY_free(evp_key); + + return pk; +} + +void abe_crypto::import_db_cert(std::string db_cert_path){ + std::string err_msg; + RSA *pk = import_pk(db_cert_path, err_msg); + if(pk == NULL){ + err_msg += ":" + db_cert_path; + ABE_ERROR(err_msg.c_str()); + } + db_pk = pk; +} + +void abe_crypto::import_kms_cert(std::string kms_cert_path){ + std::string err_msg; + RSA *pk = import_pk(kms_cert_path, err_msg); + if(pk == NULL){ + err_msg += ":" + kms_cert_path; + ABE_ERROR(err_msg.c_str()); + } + kms_pk = pk; +} + +abe_crypto::~abe_crypto(){ + if(kms_pk!= NULL) RSA_free(kms_pk); + if(db_pk!= NULL) RSA_free(db_pk); + if(sk != NULL) RSA_free(sk); +} + +void abe_crypto::verify_sig(RSA *pk, unsigned char * msg, size_t msg_length, unsigned char * sig, size_t sig_length){ + unsigned char digest[SHA512_DIGEST_LENGTH]; + // 对输入进行hash + SHA512(msg, msg_length, digest); + + // 对签名进行认证 + int ret = RSA_verify(NID_sha512, digest, SHA512_DIGEST_LENGTH, sig, sig_length, pk); + if (ret != 1){ + unsigned long ulErr = ERR_get_error(); + char szErrMsg[1024] = {0}; + ERR_error_string(ulErr, szErrMsg); // 格式:error:errId:库:函数:原因 + ABE_ERROR2("abe_key verify error: ", szErrMsg); + } + +} + +void abe_crypto::verify_db_sig(const std::string msg, const std::string sig_b64){ + //sig是base64编码,需要先解码 + size_t sig_b64_length = sig_b64.length(); + unsigned char * sig = (unsigned char*)malloc(base64_utils::b64_dec_len(sig_b64_length)); + size_t sig_length = base64_utils::b64_decode(sig_b64.c_str(), sig_b64_length, (char*)sig); + + try{ + verify_sig(db_pk, (unsigned char *)msg.c_str(), msg.length(), sig, sig_length); + free(sig); + }catch(const ::mysqlx::abe::Error& e){ + free(sig); + ABE_ERROR2("db_sig:", e.what()); + }catch(...){ + free(sig); + ABE_ERROR("nknown exception"); + } +} + +void abe_crypto::verify_kms_sig(const std::string msg_b64, const std::string sig_b64){ + + //msg和sig都是base64编码,需要先解码 + size_t msg_b64_length = msg_b64.length(); + unsigned char * msg = (unsigned char*)malloc(base64_utils::b64_dec_len(msg_b64_length)); + size_t msg_length = base64_utils::b64_decode(msg_b64.c_str(), msg_b64_length, (char*)msg); + + size_t sig_b64_length = sig_b64.length(); + unsigned char * sig = (unsigned char*)malloc(base64_utils::b64_dec_len(sig_b64_length)); + size_t sig_length = base64_utils::b64_decode(sig_b64.c_str(), sig_b64_length, (char*)sig); + + try{ + verify_sig(kms_pk, msg, msg_length, sig, sig_length); + free(msg); + free(sig); + }catch(const ::mysqlx::abe::Error& e){ + free(msg); + free(sig); + ABE_ERROR2("kms_sig:", e.what()); + }catch(...){ + free(msg); + free(sig); + ABE_ERROR("nknown exception"); + } +} + +//注意ct初始化时必须指定长度,否则ct.length会因为0x00而截断 +bool abe_crypto::rsa_decrypt(const std::string ct, std::string &pt){ + int nLen = RSA_size(sk); + char *pDecode = new char[nLen + 1]; + bool flag = true; + // 解密,不限长度,但为RSA_Decrypt_length的整数倍 + if (ct.length() < RSA_Decrypt_length + 1) + { // 一个分组的情况 + int ret = RSA_private_decrypt(ct.length(), (const unsigned char *)ct.c_str(), + (unsigned char *)pDecode, sk, RSA_PKCS1_PADDING); + if (ret >= 0) + { // 解密成功 + pt = std::string((char *)pDecode, ret); + } + else + { // 解密失败 + pt = ""; + flag = false; + } + } + else + { // 多个分组 + for (int i = 0; i < (int)ct.length() / (int)RSA_Decrypt_length; i++) + { + std::string Data = ct.substr(i * RSA_Decrypt_length, RSA_Decrypt_length); + int ret = RSA_private_decrypt(Data.length(), (const unsigned char *)Data.c_str(), + (unsigned char *)pDecode, sk, RSA_PKCS1_PADDING); + if (ret >= 0) + { + pt += std::string(pDecode, ret); + } + else + { // 解密失败 + pt = ""; + flag = false; + break; + } + } + } + + delete[] pDecode; + CRYPTO_cleanup_all_ex_data(); + return flag; +} + +void _initialize_abe(){ + oabe::InitializeOpenABE(); +} + +void _shutdown_abe(){ + oabe::ShutdownOpenABE(); +} + + +}//namespace mysqlx::abe + +MYSQLX_ABI_END(2,0) +}//namespace mysqlx \ No newline at end of file diff --git a/devapi/abe/base64.cc b/devapi/abe/base64.cc new file mode 100644 index 000000000..a13d68f4e --- /dev/null +++ b/devapi/abe/base64.cc @@ -0,0 +1,116 @@ +#include "mysqlx/abe/base64.h" +#include +#include + +namespace mysqlx{ +MYSQLX_ABI_BEGIN(2,0) +namespace abe{ +namespace base64_utils { + +typedef unsigned int uint32; +#define BASE64_ERROR(msg) std::cerr << "base64 error: " << (msg) << std::endl; + +unsigned b64_encode(const char* src, unsigned len, char* dst) +{ + char *p = NULL; + const char *s = NULL, *end = src + len; + int pos = 2; + uint32 buf = 0; + + s = src; + p = dst; + + while (s < end) { + buf |= (unsigned char)*s << (pos << 3); + pos--; + s++; + + /* write it out */ + if (pos < 0) { + *p++ = _base64[(buf >> 18) & 0x3f]; + *p++ = _base64[(buf >> 12) & 0x3f]; + *p++ = _base64[(buf >> 6) & 0x3f]; + *p++ = _base64[buf & 0x3f]; + + pos = 2; + buf = 0; + } + } + if (pos != 2) { + *p++ = _base64[(buf >> 18) & 0x3f]; + *p++ = _base64[(buf >> 12) & 0x3f]; + *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '='; + *p++ = '='; + } + + return p - dst; +} + +unsigned b64_decode(const char* src, unsigned len, char* dst) +{ + const char *srcend = src + len, *s = src; + char* p = dst; + char c; + int b = 0; + uint32 buf = 0; + int pos = 0, end = 0; + + while (s < srcend) { + c = *s++; + + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') + continue; + + if (c == '=') { + /* end sequence */ + if (!end) { + if (pos == 2) + end = 1; + else if (pos == 3) + end = 2; + else + BASE64_ERROR("(unexpected \"=\")"); + } + b = 0; + } else { + b = -1; + if (c > 0 && c < 127) + b = b64lookup[(unsigned char)c]; + if (b < 0) + BASE64_ERROR("symbol"); + } + /* add it to buffer */ + buf = (buf << 6) + b; + pos++; + if (pos == 4) { + *p++ = (buf >> 16) & 255; + if (end == 0 || end > 1) + *p++ = (buf >> 8) & 255; + if (end == 0 || end > 2) + *p++ = buf & 255; + buf = 0; + pos = 0; + } + } + + if (pos != 0) + BASE64_ERROR("end sequence"); + + return p - dst; +} + +unsigned b64_enc_len(unsigned srclen) +{ + return (srclen + 2) / 3 * 4; +} + +unsigned b64_dec_len(unsigned srclen) +{ + return srclen / 4 * 3; +} + +} +}//namespace mysqlx::abe + +MYSQLX_ABI_END(2,0) +}//namespace mysqlx \ No newline at end of file diff --git a/devapi/abe/rewrite.cc b/devapi/abe/rewrite.cc new file mode 100644 index 000000000..443e96f87 --- /dev/null +++ b/devapi/abe/rewrite.cc @@ -0,0 +1,125 @@ +#include "mysqlx/abe/rewrite.h" +#include +#include +#include +#include +// using std::std::string; + + +/*因sql-parser库对mysql语句支持很不友好,且当前并未找到实用的sql解析库 +*(最好是能解析mysql语句,然后可以反序列化,即解析后修改相关项,恢复成sql语句) +* 暂使用正则表达式实现abe_enc/abe_dec的匹配和重写 +* 文本内容理论要求:不能为空 +* policy理论要求:\w(字母,数字,下划线),| 属性分隔符,空白字符如空格等,等号,小数点,百分号 +* 加密函数格式:abe_enc(,) +* 解密函数格式:abe_dec() +*/ +namespace mysqlx{ +MYSQLX_ABI_BEGIN(2,0) +namespace abe{ + +CommandType simple_parse(const std::string sql){ + std::smatch result; + for(auto it: PATTERNS_ALL.mp){ + if(std::regex_match(sql, result, it.second)){ + return it.first; + } + } + return COM_OTHER; +} + + +bool rewrite_plan::insert_handler(std::string &real_sql, const std::string &raw_sql){ + std::regex pattern = PATTERNS_ALL.ABE_ENC_SQL_PATTERN; + std::regex pattern_enc = PATTERNS_ALL.ABE_ENC_PATTERN; + std::string new_sql = raw_sql; + std::smatch result; + while (std::regex_match(new_sql, result, pattern, std::regex_constants::format_first_only)){ + is_enc = true;//需要加密 + //abe_enc只有两个参数,data和policy + struct enc_field temp; + temp.data = result[1]; + temp.policy = result[2]; + + std::string cipher; + crypto->encrypt(temp.data, temp.policy, cipher); + + temp.enc_data = "'" + cipher + "'"; + + new_sql = std::regex_replace(new_sql, pattern_enc, temp.enc_data, std::regex_constants::format_first_only); + enc_plan.push_back(temp); + // std::cout << "new_sql: " << new_sql << std::endl; + } + + real_sql = new_sql; + return true; +} + +bool rewrite_plan::select_handler(std::string &real_sql, const std::string &raw_sql){ + std::regex pattern = PATTERNS_ALL.ABE_DEC_SQL_PATTERN; + std::regex pattern_dec = PATTERNS_ALL.ABE_DEC_PATTERN; + std::smatch result; + std::string new_sql = raw_sql; + while (std::regex_match(new_sql, result, pattern, std::regex_constants::format_first_only)){ + is_dec = true;//需要解密 + + //abe_dec只有一个参数,field_name + struct dec_field temp; + temp.field_name = result[1]; + new_sql = std::regex_replace(new_sql, pattern_dec, temp.field_name, std::regex_constants::format_first_only); + dec_plan.push_back(temp); + // std::cout << "new_sql: " << new_sql << std::endl; + } + real_sql = new_sql; + return true; +} + +bool rewrite_plan::parse_and_rewrite(){ + + std::string sql = raw_sql; + //只有show/select命令需要打印查询结果,其他的不需要 + com_type = simple_parse(sql); + + switch (com_type) + { + case COM_SELECT:{ + // std::cout << "select statement" << std::endl; + + select_handler(real_sql, raw_sql); + // std::cout << "after replace: " << real_sql << std::endl; + break; + } + case COM_INSERT:{ + // std::cout << "insert statement" << std::endl; + insert_handler(real_sql, raw_sql); + // std::cout << "after replace: " << real_sql << std::endl; + break; + } + case COM_GET_ABE_KEY:{ + real_sql = "select owner,encrypted_key,sig_db,sig_db_type,sig_kms,sig_kms_type from mysql.abe_user_key"; + real_sql += " where owner = '" + crypto->user.user_id + "';"; + break; + } + case COM_SHOW: + case COM_SELECT_CURRENT_USER: + case COM_OTHER:{ + real_sql = sql; + break; + } + default: + break; + } + return true; +} + +std::vector rewrite_plan::get_field_name_list() const { + std::vector list; + for(auto item : dec_plan){ + list.push_back(item.field_name); + } + return list; +} + +}//namespace mysqlx::abe +MYSQLX_ABI_END(2,0) +}//namespace mysqlx \ No newline at end of file diff --git a/devapi/abe_extern.cc b/devapi/abe_extern.cc new file mode 100644 index 000000000..508a7ff4e --- /dev/null +++ b/devapi/abe_extern.cc @@ -0,0 +1,183 @@ +#include "mysqlx/abe_extern.h" +#include +#include +#include "mysqlx/abe/rewrite.h" +#include "mysqlx/abe/abe_crypto.h" +#include "mysqlx/xdevapi.h" + +#define SQL_CURRENT_USER_KEY_PRIFIX \ + "select owner,encrypted_key,sig_db,sig_db_type,sig_kms,sig_kms_type \ + from mysql.abe_user_key where owner = '" +#define SQL_CURRENT_USER_KEY_SUFFIX "'" +#define SQL_CURRENT_USER_ATT "select current_abe_attribute()" +#define SQL_CURRENT_USER "select current_user()" + +namespace mysqlx{ +MYSQLX_ABI_BEGIN(2,0) + +using rewrite_plan = mysqlx::abe::rewrite_plan; +using abe_crypto = mysqlx::abe::abe_crypto; + +RowResult abe_query::execute(){ + try{ + parse_and_rewrite(); + + field_name_list = get_field_name_list(); + + RowResult res = sess->sql(real_sql).execute(); + + auto it = res.getColumns().begin(); + auto end = res.getColumns().end(); + unsigned int i=0; + for (;it != end;++it){ + std::string field_name = (*it).getColumnName(); + auto temp = std::find(field_name_list.begin(), field_name_list.end(), field_name); + if(temp != field_name_list.end()){ + //该列是使用了abe_dec的列 + f_flag.push_back(1); + field_num_list.push_back(i); + }else{ + //普通列 + f_flag.push_back(0); + } + i++; + } + return res; + } + CATCH_AND_WRAP +} + +std::string abe_query::recover(const std::string &ct){ + try{ + std::string pt; + crypto->decrypt(ct, pt); + return pt; + } + CATCH_AND_WRAP +} + +std::string abe_env::recover(const std::string &ct){ + try{ + std::string pt; + abe.decrypt(ct, pt); + return pt; + } + CATCH_AND_WRAP +} + +std::string abe_env::get_current_user_key(){ + try{ + std::string str = std::string(SQL_CURRENT_USER_KEY_PRIFIX); + str += abe.user.user_id; + str += std::string(SQL_CURRENT_USER_KEY_SUFFIX); + + RowResult res = sess->sql(str).execute(); + + int row_num = res.count(); + int field_num = res.getColumnCount(); + if(row_num != 1){ + throw_error("It seems that you don't have the abe key, please contact the admininistrator."); + } + if(field_num != rewrite_plan::TABLE_ABE_UER_KEY_FIELD_NUM){ + throw_error("system table 'abe_user_key' error"); + } + + auto it = res.begin(); + std::string key_str = (*it).get(rewrite_plan::F_KEY_NUM).get(); + std::string sig_db = (*it).get(rewrite_plan::F_SIG_DB_NUM).get(); + std::string sig_db_type = (*it).get(rewrite_plan::F_SIG_DB_TYPE_NUM).get(); + std::string sig_kms = (*it).get(rewrite_plan::F_SIG_KMS_NUM).get(); + std::string sig_kms_type = (*it).get(rewrite_plan::F_SIG_KMS_TYPE_NUM).get(); + + std::string namehost = abe.user.user_id; + std::string attrlist = abe.user.user_attr; + abe.verify_db_sig(namehost + attrlist,sig_db); + abe.verify_kms_sig(key_str,sig_kms); + return key_str; + } + CATCH_AND_WRAP +} + +std::string abe_env::get_current_user(){ + try{ + RowResult res = sess->sql(SQL_CURRENT_USER).execute(); + + int field_num = res.count(); + int row_num = res.getColumnCount(); + if(row_num != 1 || field_num != 1){ + throw_error("abe query failed: get current user."); + } + + auto it = res.begin(); + auto str = (*it).get(0).get(); + std::string namehost(str); + return namehost; + } + CATCH_AND_WRAP +} + +std::string abe_env::get_current_user_abe_attribute(){ + try{ + RowResult res = sess->sql(SQL_CURRENT_USER_ATT).execute(); + + int field_num = res.count(); + int row_num = res.getColumnCount(); + if(row_num != 1 || field_num != 1){ + throw_error("abe query failed: get current user abe attribute."); + } + + auto it = res.begin(); + auto str = (*it).get(0).get(); + std::string att(str); + return att; + } + CATCH_AND_WRAP +} + +void abe_env::abe_prepare_queries(const abe_parameters ¶ms){ + try{ + std::string namehost = get_current_user(); + abe.set_name(namehost); + std::string attrlist = get_current_user_abe_attribute(); + abe.set_att(attrlist); + if(!check_abe_key()){ + update_abe_key(params.abe_key_path); + } + } + CATCH_AND_WRAP +} + +void abe_env::update_abe_key(std::string abe_key_path){ + try{ + std::string abe_key = get_current_user_key(); + abe.save_user_key(abe_key_path, abe_key); + } + CATCH_AND_WRAP +} + +void abe_env::init(const abe_parameters ¶ms){ + try{ + abe.init(params.abe_pp_path, params.abe_key_path, + params.kms_cert_path, params.db_cert_path, + params.rsa_sk_path); + abe_prepare_queries(params); + } + CATCH_AND_WRAP +} + +void abe_encrypt(abe_crypto &abe, std::string pt, std::string policy, std::string &ct ){ + try{ + abe.encrypt(pt, policy, ct); + } + CATCH_AND_WRAP +} + +void abe_decrypt(abe_crypto &abe, std::string ct, std::string &pt){ + try{ + abe.decrypt(ct, pt); + } + CATCH_AND_WRAP +} + +MYSQLX_ABI_END(2,0) +}//namespace mysqlx diff --git a/include/mysqlx/CMakeLists.txt b/include/mysqlx/CMakeLists.txt index 05d0210fe..320bbfe70 100644 --- a/include/mysqlx/CMakeLists.txt +++ b/include/mysqlx/CMakeLists.txt @@ -27,10 +27,11 @@ # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(headers common_constants.h common.h xapi.h xdevapi.h) +SET(headers common_constants.h common.h xapi.h xdevapi.h abe_extern.h) check_headers(${headers}) +ADD_HEADERS_DIR(abe) ADD_HEADERS_DIR(common) ADD_HEADERS_DIR(devapi) ADD_HEADERS(${headers}) diff --git a/include/mysqlx/abe/CMakeLists.txt b/include/mysqlx/abe/CMakeLists.txt new file mode 100644 index 000000000..e4bbf555a --- /dev/null +++ b/include/mysqlx/abe/CMakeLists.txt @@ -0,0 +1,7 @@ +SET(headers base64.h abe_crypto.h rewrite.h) + +check_headers(${headers}) + +ADD_HEADERS(${headers}) +ADD_HEADER_CHECKS() +INSTALL(FILES ${headers} DESTINATION ${INSTALL_INCLUDE_DIR}/mysqlx/abe COMPONENT XDevAPIDev) diff --git a/include/mysqlx/abe/abe_crypto.h b/include/mysqlx/abe/abe_crypto.h new file mode 100644 index 000000000..156a7a7bb --- /dev/null +++ b/include/mysqlx/abe/abe_crypto.h @@ -0,0 +1,100 @@ +#ifndef ABE_CRYPTO_H +#define ABE_CRYPTO_H +#include +#include +#include +#include +#include +#include +#include "openssl/crypto.h" +#include "../common/api.h" +#include "../common/error.h" + +namespace mysqlx{ +MYSQLX_ABI_BEGIN(2,0) +namespace abe{ + +//define the abe error class, maintain the independence of abe module +class PUBLIC_API Error : public common::Error +{ +public: + Error(const char *msg) + : common::Error(msg) + {} +}; + +inline +void throw_error(const char *msg) +{ + throw Error(msg); +} + +inline void my_abe_throw_error2(const std::string msg, const std::string comment){ + std::string str = std::string(msg) + std::string(comment); + throw_error(str.c_str()); +} + +inline void my_abe_throw_error3(const std::string msg, const std::string comment1, const std::string comment2){ + std::string str = std::string(msg) + std::string(comment1) + std::string(comment2); + throw_error(str.c_str()); +} + +#define ABE_ERROR(msg) throw_error(msg); +#define ABE_ERROR2(msg,comment) my_abe_throw_error2(msg, comment); +#define ABE_ERROR3(msg,comment1, comment2) my_abe_throw_error3(msg, comment1, comment2); +// #define ABE_LOG(msg) std::cout << (msg) << std::endl; + + +#define RSA_Encrypt_length 245 +#define RSA_Decrypt_length 256 + +struct abe_user{ + std::string user_id; + std::string user_key = ""; + std::string user_attr; +}; + +class PUBLIC_API abe_crypto{ +public: + + abe_crypto(){} + abe_crypto(std::string name){user.user_id = name;}//name或者说user_id,即用户标识,一般和登录的数据库用户同名 + + void init(std::string mpk_path, std::string key_path, std::string kms_cert_path, std::string db_cert_path, std::string rsa_sk_path); + void set_name(std::string namehost){user.user_id = namehost;} + void set_att(std::string att) {user.user_attr = att;} + void import_mpk(std::string mpk_path); + bool import_user_key(std::string key_path); + void save_user_key(std::string key_path, std::string key_str); + bool check_abe_key(); //true: abe_key已存在 + + void encrypt(std::string pt, std::string policy, std::string &ct); + void decrypt(const std::string ct, std::string &pt); + + void import_db_cert(std::string db_cert_path); + void import_kms_cert(std::string kms_cert_path); + void import_sk(std::string rsa_sk_path); + + void verify_db_sig(const std::string msg, const std::string sig_db_b64); + void verify_kms_sig(const std::string msg_b64, const std::string sig_kms_b64); + + struct abe_user user; + ~abe_crypto(); +private: + std::string mpk; + RSA *kms_pk = NULL; + RSA *db_pk = NULL; + RSA *sk = NULL; + void verify_sig(RSA *pk, unsigned char * msg, size_t msg_length, unsigned char * sig, size_t sig_length); + bool rsa_decrypt(const std::string ct, std::string &pt); + RSA * import_pk(const std::string cert_path, std::string &err_msg); +}; + +void PUBLIC_API _initialize_abe(); +void PUBLIC_API _shutdown_abe(); + +}//namespace mysqlx::abe +MYSQLX_ABI_END(2,0) +}//namespace mysqlx + +#endif \ No newline at end of file diff --git a/include/mysqlx/abe/base64.h b/include/mysqlx/abe/base64.h new file mode 100644 index 000000000..98b8ba16f --- /dev/null +++ b/include/mysqlx/abe/base64.h @@ -0,0 +1,152 @@ +#ifndef SQL_ABE_BASE64_H +#define SQL_ABE_BASE64_H +#include "../common/api.h" +namespace mysqlx{ +MYSQLX_ABI_BEGIN(2,0) + +namespace abe{ +namespace base64_utils { + static const char _base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + static const char b64lookup[128] = { + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 62, + -1, + -1, + -1, + 63, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + -1, + -1, + -1, + -1, + -1, + -1, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + -1, + -1, + -1, + -1, + -1, + }; + + unsigned b64_encode(const char* src, unsigned len, char* dst); + unsigned b64_decode(const char* src, unsigned len, char* dst); + unsigned b64_enc_len(unsigned srclen); + unsigned b64_dec_len(unsigned srclen); +} + +}//namespace mysqlx::abe + +MYSQLX_ABI_END(2,0) +}//namespace mysqlx +#endif // SEC_ABE_UTILS_H \ No newline at end of file diff --git a/include/mysqlx/abe/rewrite.h b/include/mysqlx/abe/rewrite.h new file mode 100644 index 000000000..7af80bdde --- /dev/null +++ b/include/mysqlx/abe/rewrite.h @@ -0,0 +1,150 @@ +// #include "SQLParser.h" +// #include "util/sqlhelper.h" +#ifndef REWRITE_H +#define REWRITE_H +#include +#include +#include +#include +#include +#include "abe_crypto.h" +#include "../common/api.h" + + +namespace mysqlx{ +MYSQLX_ABI_BEGIN(2,0) + +namespace abe{ +enum CommandType{ + BEGIN, + COM_SELECT_CURRENT_USER, + COM_GET_ABE_KEY, + COM_SELECT, + COM_INSERT, + COM_SHOW, + END, + + COM_OTHER, + WRONG_SQL +}; + + +struct RegexPatterns{ +public: + std::regex ABE_ENC_PATTERN; + std::regex ABE_ENC_SQL_PATTERN; + std::regex ABE_DEC_PATTERN; + std::regex ABE_DEC_SQL_PATTERN; + + std::unordered_map mp; + + RegexPatterns(){ + const std::string ABE_ANY_R = ".*?"; + const std::string ABE_COMMA_R = "\\s*,\\s*"; + const std::string ABE_SUFIX_R = "\\s*\\)"; + + const std::string ABE_ENC_PREFIX_R = "abe_enc\\(\\s*"; + const std::string ABE_ENC_DATA_R = "['\"`]((?:\\\\\"|\\\\\\(|\\\\\\)|[^\"\\(\\)])+)['\"`]"; // 真实正则:((?:\\"|\\\(|\\\)|[^\"\(\)])+) + const std::string ABE_ENC_POLICY_R = "['\"`]((?:\\\\\"|\\\\\\(|\\\\\\)|[^\"\\(\\)])+)['\"`]"; + const std::string ABE_ENC_REGEX = ABE_ENC_PREFIX_R + ABE_ENC_DATA_R + + ABE_COMMA_R + ABE_ENC_POLICY_R + ABE_SUFIX_R; //加密函数正则 + const std::string ABE_ENC_SQL_REGEX = ABE_ANY_R + ABE_ENC_REGEX + ABE_ANY_R; + + const std::string ABE_DEC_PREFIX_R = "abe_dec\\(\\s*"; + const std::string ABE_DEC_FIELD_R = "[`]*(\\w+)[`]*"; + const std::string ABE_DEC_REGEX = ABE_DEC_PREFIX_R + ABE_DEC_FIELD_R + ABE_SUFIX_R; //解密函数正则 + const std::string ABE_DEC_SQL_REGEX = ABE_ANY_R + ABE_DEC_REGEX + ABE_ANY_R; + + const std::string COM_SELECT_SQL_REGEX = "\\s*select.*"; + const std::string COM_INSERT_SQL_REGEX = "\\s*insert.*"; + const std::string COM_SHOW_SQL_REGEX = "\\s*show.*"; + const std::string COM_SELECT_CURRENT_USER_SQL_REGEX = "\\s*select\\s+current_user().*"; + const std::string GET_ABE_KEY_REGEX = "\\s*show\\s+current_abe_key;\\s*"; + + ABE_ENC_PATTERN = std::regex(ABE_ENC_REGEX, std::regex::icase); + ABE_ENC_SQL_PATTERN = std::regex(ABE_ENC_SQL_REGEX, std::regex::icase); + ABE_DEC_PATTERN = std::regex(ABE_DEC_REGEX, std::regex::icase); + ABE_DEC_SQL_PATTERN = std::regex(ABE_DEC_SQL_REGEX, std::regex::icase); + + mp[COM_SELECT] = std::regex(COM_SELECT_SQL_REGEX, std::regex::icase); + mp[COM_INSERT] = std::regex(COM_INSERT_SQL_REGEX, std::regex::icase); + mp[COM_SHOW] = std::regex(COM_SHOW_SQL_REGEX, std::regex::icase); + mp[COM_SELECT_CURRENT_USER] = std::regex(COM_SELECT_CURRENT_USER_SQL_REGEX, std::regex::icase); + mp[COM_GET_ABE_KEY] = std::regex(GET_ABE_KEY_REGEX, std::regex::icase); + } +}; +static RegexPatterns PATTERNS_ALL; + +//一个改写点,包括abe策略(用户输入)、明文data、密文enc_data +struct enc_field{ + std::string policy; + std::string data; + std::string enc_data; +}; + +//解密改写点 +struct dec_field{ + std::string field_name; //要解密的field_name + int field_num; //要解密的field位置,如select a,b,abe_dec()的abe_dec位置为2(从0开始) +}; + + +class rewrite_plan{ +public: + /* + * input: 用户输入的原始sql语句 + * real_sql: 重写完成后真正要执行的sql语句 + */ + rewrite_plan(std::string input) : raw_sql(input), is_enc(false), is_dec(false) {} + + bool parse_and_rewrite(); + + bool need_print() const{ return (com_type == COM_SELECT || com_type == COM_SHOW);} + bool need_enc() const { return is_enc; } + bool need_dec() const { return is_dec; } + + void set_crypto(struct abe_crypto &c){ + crypto = &c; + } + + void set_crypto(struct abe_crypto *c){ + crypto = c; + } + + //需要解密的列名 + std::vector get_field_name_list() const; + + struct abe_crypto * crypto; //abe算法 + + /* + * 查询,包括select/show,需要输出查询结果 + */ + CommandType com_type; + + //只考虑owner,encrypted_key,sig_db,sig_db_type,sig_kms,sig_kms_type + static constexpr int TABLE_ABE_UER_KEY_FIELD_NUM = 6; + enum abe_user_key_field {F_OWNER_NUM = 0,F_KEY_NUM = 1,F_SIG_DB_NUM = 2, F_SIG_DB_TYPE_NUM = 3, + F_SIG_KMS_NUM = 4, F_SIG_KMS_TYPE_NUM = 5}; + std::string raw_sql; //用户输入sql + std::string real_sql; + +private: + bool is_enc; //true:加密时,一般为插入 + bool is_dec; //true,解密一般为查询 + + std::vector enc_plan; //改写点列表 + + //解密需要: + std::vector dec_plan; + + + bool insert_handler(std::string &real_sql, const std::string &raw_sql); + bool select_handler(std::string &real_sql, const std::string &raw_sql); + + +}; + +}//namespace mysqlx::abe +MYSQLX_ABI_END(2,0) +}//namespace mysqlx +#endif \ No newline at end of file diff --git a/include/mysqlx/abe_extern.h b/include/mysqlx/abe_extern.h new file mode 100644 index 000000000..39001a2a3 --- /dev/null +++ b/include/mysqlx/abe_extern.h @@ -0,0 +1,97 @@ +#ifndef ABE_EXTERN_H +#define ABE_EXTERN_H +#include +#include +#include "abe/rewrite.h" +#include "abe/abe_crypto.h" +#include "xdevapi.h" +// #include "devapi/detail/result.h" +namespace mysqlx { +MYSQLX_ABI_BEGIN(2,0) + +struct abe_parameters{ + std::string rsa_sk_path; //rsa私钥路径,用于解密 + std::string db_cert_path; //db证书,用于验签 + std::string kms_cert_path; //kms证书,用于验签 + + std::string abe_key_path; //abe密钥路径 + std::string abe_pp_path; //abe公共参数路径,也可称mpk +}; + +class PUBLIC_API abe_query : public abe::rewrite_plan{ +public: + abe_query(Session * sess, abe::abe_crypto * abe, std::string sql): rewrite_plan(sql){ + this->sess = sess; + set_crypto(abe); + } + + //重写并执行 + RowResult execute(); + + //解密 + std::string recover(const std::string &ct); + + //需要解密的列名 + std::vector field_name_list; + std::vector field_num_list; + std::vector f_flag;//指示某列是否需要解密 +private: + Session * sess; +}; + +class PUBLIC_API abe_env{ +public: + abe_env(Session &sess){ + this->sess = &sess; + } + + /* + 初始化abe的环境,包括所需abe密钥和证书、私钥等 + */ + void init(const abe_parameters ¶ms); + + /* + 仿照原来的用法 + env.sql("select * ...").execute(); + */ + abe_query sql(std::string input){ + return abe_query(sess, &abe, input); + } + + //解密 + std::string recover(const std::string &ct); + + bool check_abe_key(){ + return abe.check_abe_key(); + } + + /* + init时如果存在abe_key则不重复下载,使用update_abe_key可以强制更新abe_key + */ + void update_abe_key(std::string abe_key_path); + + + Session * sess; + abe::abe_crypto abe; + + void abe_prepare_queries(const abe_parameters ¶ms); + std::string get_current_user(); + std::string get_current_user_abe_attribute(); + std::string get_current_user_key(); +}; + + +void PUBLIC_API abe_encrypt(abe::abe_crypto &abe, std::string pt, std::string policy, std::string &ct ); +void PUBLIC_API abe_decrypt(abe::abe_crypto &abe, std::string ct, std::string &pt); + +void PUBLIC_API initialize_abe(){ + abe::_initialize_abe(); +} +void PUBLIC_API shutdown_abe(){ + abe::_shutdown_abe(); +} + +MYSQLX_ABI_END(2,0) +} +// const std::string abe_rewrite(const std::string &query, const abe_crypto &abe); +#endif //ABE_EXTERN_H \ No newline at end of file diff --git a/include/mysqlx/common/api.h b/include/mysqlx/common/api.h index 9e0525f35..d7c3f03d1 100644 --- a/include/mysqlx/common/api.h +++ b/include/mysqlx/common/api.h @@ -92,6 +92,10 @@ MYSQLX_ABI_BEGIN(2,0) namespace common { } + namespace abe { + + } + MYSQLX_ABI_END(2,0) /*