* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
* File Name : cipherfn.cpp
* Brief :
* Description : encrypt and decrypt functions for MPPDB
*
* History : 2014-10
*
* IDENTIFICATION
* src/gausskernel/cbb/utils/aes/cipherfn.cpp
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "cipher.h"
#include "utils/builtins.h"
#include "utils/aes.h"
#include "utils/evp_cipher.h"
#include "storage/lock/lwlock.h"
#include "port.h"
#include "getopt_long.h"
#include "pgxc/pgxc.h"
#include "gaussdb_version.h"
#ifdef ENABLE_GSS
#include "gssapi/gssapi_krb5.h"
#endif
#include "tde_key_management/tde_key_manager.h"
#include "openssl/rand.h"
#include "openssl/evp.h"
#include "openssl/crypto.h"
#include "utils/evp_cipher.h"
#include "utils/guc_security.h"
#ifdef ENABLE_UT
#define static
const char *fake_tde_get_key(const char *str)
{
return str;
}
#endif
const char* pgname = "gs_encrypt";
void getOBSKeyString(GS_UCHAR** cipherKey);
static GS_UCHAR* getECKeyString(KeyMode mode);
char prefixCipherKey[2] = {'\0'};
char suffixCipherKey[100] = {'\0'};
bool gs_encrypt_aes_speed(GS_UCHAR* plaintext, GS_UCHAR* key, GS_UCHAR* ciphertext, GS_UINT32* cipherlen);
bool gs_decrypt_aes_speed(
GS_UCHAR* ciphertext, GS_UINT32 cipherlen, GS_UCHAR* key, GS_UCHAR* plaintext, GS_UINT32* plainlen);
static char* gs_strdup(const char* s);
static void do_help(void);
static void do_advice(void);
static void free_global_value(void);
static bool is_prefix_in_key_mode(const char* mode);
char* g_fi_ca_path = NULL;
char* g_prefix = NULL;
int g_vector_len = 0;
char* g_key = NULL;
int g_key_len = 0;
char* g_vector = NULL;
#define EC_ENCRYPT_PREFIX "encryptOpt"
#define ENCRYPT_TYPE_SM4 "sm4"
#define ENCRYPT_TYPE_AES128 "aes128"
#define GS_FREE(ptr) \
if (NULL != (ptr)) { \
free((char*)(ptr)); \
ptr = NULL; \
}
#define IsIllegalCharacter(c) ((c) != '/' && !isdigit((c)) && !isalpha((c)) && (c) != '_' && (c) != '-')
* @@GaussDB@@
* Brief : char *gs_strdup(const char *s)
* Description :
* Notes :
*/
static char* gs_strdup(const char* s)
{
char* result = NULL;
result = strdup(s);
if (result == NULL) {
(void)fprintf(stderr, _("%s: out of memory\n\n"), pgname);
exit(1);
}
return result;
}
* decrypt the ciphertext with ase128 algorithms through OpenSSL API
* old version, but still used by the new version in case there are data encrypted
* by gs_encrypt_aes.
*/
bool gs_decrypt_aes(GS_UCHAR* ciphertext, GS_UINT32 cipherlen, GS_UCHAR* key, GS_UCHAR* plaintext, GS_UINT32* plainlen)
{
GS_UINT32 cipherpartlen = 0;
GS_UCHAR* cipherpart = NULL;
GS_UCHAR* randpart = NULL;
bool decryptstatus = false;
errno_t errorno = EOK;
cipherpartlen = cipherlen - RANDOM_LEN;
cipherpart = (GS_UCHAR*)palloc0(cipherpartlen + 1);
randpart = (GS_UCHAR*)palloc0(RANDOM_LEN);
errorno = memcpy_s(cipherpart, cipherpartlen + 1, ciphertext, cipherpartlen);
securec_check(errorno, "\0", "\0");
errorno = memcpy_s(randpart, RANDOM_LEN, ciphertext + cipherpartlen, RANDOM_LEN);
securec_check(errorno, "\0", "\0");
decryptstatus = aes128Decrypt(
cipherpart, cipherpartlen, key, (GS_UINT32)strlen((const char*)key), randpart, plaintext, plainlen);
errorno = memset_s(cipherpart, cipherpartlen + 1, '\0', cipherpartlen + 1);
securec_check(errorno, "\0", "\0");
pfree_ext(cipherpart);
errorno = memset_s(randpart, RANDOM_LEN, '\0', RANDOM_LEN);
securec_check(errorno, "\0", "\0");
pfree_ext(randpart);
if (!decryptstatus) {
return false;
} else {
return true;
}
}
* Target :Encrypt functions for security.
* Description :Encrypt with standard aes128 algorthm using OpenSSL functions.
* Notes :The Key used here must be the same as it was used in decrypt.
* Input :PG_FUNCTION_ARGS, containing plaintext and key
* Output :Pointer of the CipherText which is encrypted and encoded from plaintext
* Revision :Using random initial vector and one fixed salt in one thread
* in order to avoid generating deriveKey everytime.
*/
static bool gs_encrypt_aes128_function(FunctionCallInfo fcinfo, text** outtext)
{
char* key = NULL;
GS_UINT32 keylen = 0;
char* plaintext = NULL;
GS_UINT32 plaintextlen = 0;
GS_UCHAR* ciphertext = NULL;
GS_UINT32 ciphertextlen = 0;
GS_UINT32 retval = 0;
char* encodetext = NULL;
GS_UINT32 encodetextlen = 0;
GS_UINT32 ciphertextlen_max = 0;
errno_t errorno = EOK;
plaintext = text_to_cstring(PG_GETARG_TEXT_P(0));
plaintextlen = strlen(plaintext);
key = text_to_cstring(PG_GETARG_TEXT_P(1));
keylen = strlen(key);
if (!check_input_password(key)) {
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("The encryption key must be %d~%d bytes and contain at least three kinds of characters!",
MIN_KEY_LEN, MAX_KEY_LEN)));
}
* Calculate the max length of ciphertext:
* contain aes-expand length, IV length, Salt length and backup length.
* Add mac text length.
*/
ciphertextlen_max = plaintextlen + RANDOM_LEN * 4 + MAC_LEN;
ciphertext = (GS_UCHAR*)palloc(ciphertextlen_max);
errorno = memset_s(ciphertext, ciphertextlen_max, '\0', ciphertextlen_max);
securec_check(errorno, "\0", "\0");
retval = gs_encrypt_aes_speed((GS_UCHAR*)plaintext, (GS_UCHAR*)key, ciphertext, &ciphertextlen);
errorno = memset_s(plaintext, plaintextlen, '\0', plaintextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(plaintext);
errorno = memset_s(key, keylen, '\0', keylen);
securec_check(errorno, "\0", "\0");
pfree_ext(key);
if (!retval) {
errorno = memset_s(ciphertext, ciphertextlen, '\0', ciphertextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(ciphertext);
ereport(
ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("encrypt the plain text failed!")));
}
encodetext = SEC_encodeBase64((char*)ciphertext, ciphertextlen);
errorno = memset_s(ciphertext, ciphertextlen, '\0', ciphertextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(ciphertext);
if (encodetext == NULL) {
ciphertextlen_max = 0;
ereport(
ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("encode the plain text failed!")));
}
encodetextlen = strlen(encodetext);
*outtext = cstring_to_text(encodetext);
errorno = memset_s(encodetext, encodetextlen, '\0', encodetextlen);
securec_check(errorno, "\0", "\0");
OPENSSL_free(encodetext);
encodetext = NULL;
return true;
}
* Target :Decrypt functions for security.
* Description :Decrypt with standard aes128 algorthm using OpenSSL functions.
* Notes :The Key used here must be the same as it was used in encrypt.
* Input :PG_FUNCTION_ARGS, containing ciphertext and key
* Output :Pointer of the PlainText which is decrypted and decoded from ciphertext
* Revision :Save several derivekeys and salts in one thread
* in order to avoid generating deriveKey everytime.
*/
bool gs_decrypt_aes128_function(FunctionCallInfo fcinfo, text** outtext)
{
GS_UCHAR* key = NULL;
GS_UINT32 keylen = 0;
GS_UCHAR* plaintext = NULL;
GS_UINT32 plaintextlen = 0;
GS_UCHAR* ciphertext = NULL;
GS_UINT32 retval = 0;
GS_UCHAR* decodetext = NULL;
GS_UINT32 decodetextlen = 0;
errno_t errorno = EOK;
decodetext = (GS_UCHAR*)(text_to_cstring(PG_GETARG_TEXT_P(0)));
key = (GS_UCHAR*)(text_to_cstring(PG_GETARG_TEXT_P(1)));
keylen = strlen((const char*)key);
ciphertext = (GS_UCHAR*)(SEC_decodeBase64((char*)decodetext, &decodetextlen));
if ((ciphertext == NULL) || (decodetextlen <= RANDOM_LEN)) {
if (ciphertext != NULL) {
OPENSSL_free(ciphertext);
ciphertext = NULL;
}
errorno = memset_s(decodetext, decodetextlen, '\0', decodetextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(decodetext);
errorno = memset_s(key, keylen, '\0', keylen);
securec_check(errorno, "\0", "\0");
pfree_ext(key);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("Decode the cipher text failed or the ciphertext is too short!")));
}
errorno = memset_s(decodetext, decodetextlen, '\0', decodetextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(decodetext);
plaintext = (GS_UCHAR*)palloc(decodetextlen);
errorno = memset_s(plaintext, decodetextlen, '\0', decodetextlen);
securec_check(errorno, "\0", "\0");
* decrypt the ciphertext to plaintext using the new version function first
* try :old vesion of decryption function if the new one failed.
* if the old one also failed, return alarm.
*/
retval = gs_decrypt_aes_speed(ciphertext, decodetextlen, key, plaintext, &plaintextlen);
if (!retval) {
errorno = memset_s(plaintext, decodetextlen, '\0', decodetextlen);
securec_check(errorno, "\0", "\0");
retval = gs_decrypt_aes(ciphertext, decodetextlen, key, plaintext, &plaintextlen);
(void)fprintf(stderr, _("Try previous version of decrypt-function!\n"));
}
errorno = memset_s(key, keylen, '\0', keylen);
securec_check(errorno, "\0", "\0");
pfree_ext(key);
if (!retval) {
errorno = memset_s(ciphertext, decodetextlen, '\0', decodetextlen);
securec_check(errorno, "\0", "\0");
OPENSSL_free(ciphertext);
ciphertext = NULL;
errorno = memset_s(plaintext, plaintextlen, '\0', plaintextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(plaintext);
ereport(
ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("decrypt the cipher text failed!")));
}
errorno = memset_s(ciphertext, decodetextlen, '\0', decodetextlen);
securec_check(errorno, "\0", "\0");
OPENSSL_free(ciphertext);
ciphertext = NULL;
*outtext = cstring_to_text((char*)plaintext);
errorno = memset_s(plaintext, plaintextlen, '\0', plaintextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(plaintext);
return true;
}
* Generate random and compute plain text length.
* This function use key and random to encrypt plain text
* ATTENTION:
* The cipherlen is an "IN&OUT" param!!!!!!!
*/
bool gs_encrypt_aes_128(
GS_UCHAR* plaintext, GS_UCHAR* key, GS_UINT32 keylen, GS_UCHAR* ciphertext, GS_UINT32* cipherlen)
{
errno_t rc = 0;
errno_t ret = 0;
GS_UINT32 retval = 0;
GS_UINT32 plainlen = 0;
GS_UCHAR init_rand[RANDOM_LEN + 1] = {0};
GS_UCHAR tmp_cipher[CIPHER_LEN + 1] = {0};
bool encryptstatus = false;
char* gausshome = NULL;
char randfile[MAXPGPATH] = {'\0'};
char cipherfile[MAXPGPATH] = {'\0'};
size_t cipherbufferlen = (size_t)(*cipherlen);
bool status = false;
if (NULL == plaintext) {
return false;
}
plainlen = strlen((const char*)plaintext);
if ((NULL == g_prefix && NULL == g_key && NULL == g_vector) ||
(NULL != g_prefix && is_prefix_in_key_mode((const char*)g_prefix))) {
retval = RAND_bytes(init_rand, RANDOM_LEN);
if (retval != 1) {
(void)fprintf(stderr, _("generate random key failed, errcode:%u\n"), retval);
return false;
}
encryptstatus = aes128Encrypt(plaintext, plainlen, key, keylen, init_rand, ciphertext, cipherlen);
if (!encryptstatus) {
return false;
}
if (((size_t)(*cipherlen) + RANDOM_LEN) > cipherbufferlen) {
(void)fprintf(stderr, _("ciphertext buffer is too small\n"));
return false;
}
rc = memcpy_s(ciphertext + (*cipherlen), (Size)RANDOM_LEN, init_rand, (Size)RANDOM_LEN);
securec_check(rc, "\0", "\0");
return true;
}
if (NULL != g_prefix) {
gausshome = gs_getenv_r("GAUSSHOME");
char real_gausshome[PATH_MAX + 1] = {'\0'};
if (gausshome == NULL || realpath(gausshome, real_gausshome) == NULL) {
(void)fprintf(stderr, _("get environment of GAUSSHOME failed.\n"));
exit(1);
}
check_backend_env(real_gausshome);
ret = snprintf_s(randfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s.key.rand", real_gausshome, g_prefix);
securec_check_ss(ret, "\0", "\0");
ret = snprintf_s(cipherfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s.key.cipher", real_gausshome, g_prefix);
securec_check_ss(ret, "\0", "\0");
status = getKeyVectorFromCipherFile(cipherfile, randfile, tmp_cipher, init_rand);
if (!status) {
rc = memset_s(tmp_cipher, sizeof(tmp_cipher), 0, sizeof(tmp_cipher));
securec_check(rc, "\0", "\0");
(void)fprintf(
stderr, _("Failed to decrypt key from transparent encryption random file %s.\n"), randfile);
return false;
}
encryptstatus =
aes128Encrypt(plaintext, plainlen, tmp_cipher, CIPHER_LEN, init_rand, ciphertext, cipherlen);
if (!encryptstatus) {
rc = memset_s(tmp_cipher, sizeof(tmp_cipher), 0, sizeof(tmp_cipher));
securec_check(rc, "\0", "\0");
return false;
}
if (cipherbufferlen < ((size_t)(*cipherlen) + RANDOM_LEN)) {
rc = memset_s(tmp_cipher, sizeof(tmp_cipher), 0, sizeof(tmp_cipher));
securec_check(rc, "\0", "\0");
(void)fprintf(stderr, _("ciphertext buffer is too small.\n"));
return false;
}
rc = memcpy_s(ciphertext + (*cipherlen), (Size)RANDOM_LEN, init_rand, (Size)RANDOM_LEN);
securec_check(rc, "\0", "\0");
return true;
}
encryptstatus = aes128Encrypt(plaintext,
plainlen,
(GS_UCHAR*)g_key,
(GS_UINT32)g_key_len,
(GS_UCHAR*)g_vector,
ciphertext,
cipherlen);
if (!encryptstatus) {
return false;
}
if (((size_t)(*cipherlen) + (size_t)g_vector_len) > cipherbufferlen) {
(void)fprintf(stderr, _("ciphertext buffer is too small\n"));
return false;
}
rc = memcpy_s(ciphertext + (*cipherlen), (Size)g_vector_len, g_vector, (Size)g_vector_len);
securec_check(rc, "\0", "\0");
return true;
}
* Split cipher text to cipher part and random part.
* This function use key and random to decrypt cipher text
*/
bool gs_decrypt_aes_128(GS_UCHAR* ciphertext, GS_UINT32 cipherlen, GS_UCHAR* key, GS_UINT32 keylen, GS_UCHAR* plaintext,
GS_UINT32* plainlen)
{
errno_t rc;
GS_UINT32 cipherpartlen = 0;
GS_UCHAR* cipherpart = NULL;
GS_UCHAR* randpart = NULL;
bool decryptstatus = false;
if (NULL == ciphertext) {
return false;
}
cipherpartlen = cipherlen - RANDOM_LEN;
cipherpart = (GS_UCHAR*)palloc0(cipherpartlen + 1);
randpart = (GS_UCHAR*)palloc0(RANDOM_LEN + 1);
rc = memcpy_s(cipherpart, cipherpartlen + 1, ciphertext, cipherpartlen);
securec_check(rc, "\0", "\0");
rc = memcpy_s(randpart, RANDOM_LEN + 1, ciphertext + cipherpartlen, RANDOM_LEN);
securec_check(rc, "\0", "\0");
decryptstatus = aes128Decrypt(cipherpart, cipherpartlen, key, keylen, randpart, plaintext, plainlen);
rc = memset_s(cipherpart, cipherpartlen + 1, 0, cipherpartlen + 1);
securec_check(rc, "\0", "\0");
pfree_ext(cipherpart);
rc = memset_s(randpart, RANDOM_LEN, 0, RANDOM_LEN);
securec_check(rc, "\0", "\0");
pfree_ext(randpart);
if (!decryptstatus) {
return false;
}
return true;
}
* Encrypt plain text to cipher text.
* This function use aes128 to encrypt plain text,key comes from certificate file
*/
void encryptOBS(char* srcplaintext, char destciphertext[], uint32 destcipherlength)
{
errno_t rc = EOK;
GS_UINT32 ciphertextlen = 512;
GS_UCHAR* ciphertext = NULL;
GS_UCHAR* decipherkey = NULL;
getOBSKeyString(&decipherkey);
ciphertext = (GS_UCHAR*)palloc(ciphertextlen);
rc = memset_s(ciphertext, ciphertextlen, 0, ciphertextlen);
securec_check(rc, "\0", "\0");
if (!gs_encrypt_aes_128((GS_UCHAR*)srcplaintext, decipherkey,
(GS_UINT32)strlen((const char*)decipherkey), ciphertext, &ciphertextlen)) {
rc = memset_s(srcplaintext, strlen(srcplaintext) + 1, 0, strlen(srcplaintext) + 1);
securec_check(rc, "\0", "\0");
rc = memset_s(ciphertext, ciphertextlen, 0, ciphertextlen);
securec_check(rc, "\0", "\0");
pfree_ext(ciphertext);
rc = memset_s(decipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(rc, "\0", "\0");
pfree_ext(decipherkey);
ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("Encrypt OBS AK/SK failed.")));
}
char* encodetext = SEC_encodeBase64((char*)ciphertext, ciphertextlen + RANDOM_LEN);
if (encodetext == NULL || destcipherlength < strlen(encodetext) + 1) {
rc = memset_s(srcplaintext, strlen(srcplaintext) + 1, 0, strlen(srcplaintext) + 1);
securec_check(rc, "\0", "\0");
rc = memset_s(ciphertext, ciphertextlen, 0, ciphertextlen);
securec_check(rc, "\0", "\0");
pfree_ext(ciphertext);
rc = memset_s(decipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(rc, "\0", "\0");
pfree_ext(decipherkey);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("Encrypt OBS AK/SK internal error")));
}
rc = memcpy_s(destciphertext, destcipherlength, encodetext, (strlen(encodetext) + 1));
securec_check(rc, "\0", "\0");
rc = memset_s(encodetext, strlen(encodetext) + 1, 0, strlen(encodetext) + 1);
securec_check(rc, "\0", "\0");
OPENSSL_free(encodetext);
encodetext = NULL;
rc = memset_s(ciphertext, ciphertextlen, 0, ciphertextlen);
securec_check(rc, "\0", "\0");
pfree_ext(ciphertext);
rc = memset_s(decipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(rc, "\0", "\0");
pfree_ext(decipherkey);
}
* Decrypt cipher text to plain text.
* This function use aes128 to decrypt cipher text,key comes from certificate file
*/
void decryptOBS(const char* srcciphertext, char destplaintext[], uint32 destplainlength, const char* obskey)
{
errno_t rc = EOK;
GS_UCHAR* ciphertext = NULL;
GS_UINT32 decodetextlen = 0;
GS_UCHAR* plaintext = NULL;
GS_UINT32 plaintextlen = 0;
GS_UCHAR* decipherkey = NULL;
if (NULL == srcciphertext || NULL == destplaintext) {
return;
}
if (obskey != NULL) {
decipherkey = (GS_UCHAR*)palloc(RANDOM_LEN + 1);
rc = memset_s(decipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(rc, "\0", "\0");
int keysize = strlen(obskey);
rc = memcpy_s(decipherkey, RANDOM_LEN + 1, obskey, keysize);
securec_check(rc, "\0", "\0");
} else {
getOBSKeyString(&decipherkey);
}
ciphertext = (GS_UCHAR*)(SEC_decodeBase64((char*)srcciphertext, &decodetextlen));
plaintext = (GS_UCHAR*)palloc(decodetextlen);
rc = memset_s(plaintext, decodetextlen, 0, decodetextlen);
securec_check(rc, "\0", "\0");
if (!gs_decrypt_aes_128(ciphertext, decodetextlen, decipherkey,
(GS_UINT32)strlen((const char*)decipherkey), plaintext, &plaintextlen)) {
rc = memset_s(decipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(rc, "\0", "\0");
pfree_ext(decipherkey);
ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("Decrypt OBS AK/SK failed.")));
}
if (plaintextlen + 1 > destplainlength) {
rc = memset_s(decipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(rc, "\0", "\0");
pfree_ext(decipherkey);
rc = memset_s(plaintext, decodetextlen, 0, decodetextlen);
securec_check(rc, "\0", "\0");
pfree_ext(plaintext);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("Decrypt OBS AK/SK internal error.")));
}
rc = memcpy_s(destplaintext, destplainlength, plaintext, plaintextlen);
securec_check(rc, "\0", "\0");
destplaintext[plaintextlen] = '\0';
rc = memset_s(plaintext, decodetextlen, 0, decodetextlen);
securec_check(rc, "\0", "\0");
pfree_ext(plaintext);
rc = memset_s(decipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(rc, "\0", "\0");
pfree_ext(decipherkey);
* ciphertext should not be null. If ciphertext is null,
* the processing throwed ERROR after calling gs_decrypt_aes_128
*/
Assert(NULL != ciphertext);
rc = memset_s(ciphertext, decodetextlen, 0, decodetextlen);
securec_check(rc, "\0", "\0");
OPENSSL_free(ciphertext);
ciphertext = NULL;
}
void getOBSKeyString(GS_UCHAR** cipherKey)
{
* Notice: cypherKey will be overwritten.
*/
Assert(NULL == *cipherKey);
int ret = 0;
char* cipherpath = NULL;
char cipherkeyfile[MAXPGPATH] = {'\0'};
char isexistscipherkeyfile[MAXPGPATH] = {'\0'};
* Important: function getenv() is not thread safe.
*/
LWLockAcquire(OBSGetPathLock, LW_SHARED);
cipherpath = gs_getenv_r("GAUSSHOME");
char real_gausshome[PATH_MAX + 1] = {'\0'};
LWLockRelease(OBSGetPathLock);
if (cipherpath == NULL || realpath(cipherpath, real_gausshome) == NULL) {
(void)fprintf(stderr, _("Get environment of GAUSSHOME failed or it is invalid.\n"));
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("Failed to get OBS certificate file.")));
}
cipherpath = real_gausshome;
check_backend_env(cipherpath);
ret = snprintf_s(cipherkeyfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin", cipherpath);
securec_check_ss(ret, "\0", "\0");
ret = snprintf_s(isexistscipherkeyfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/obsserver.key.cipher", cipherpath);
securec_check_ss_c(ret, "\0", "\0");
*cipherKey = (GS_UCHAR*)palloc(RANDOM_LEN + 1);
ret = memset_s(*cipherKey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(ret, "\0", "\0");
if (file_exists(isexistscipherkeyfile)) {
decode_cipher_files(OBS_MODE, NULL, cipherkeyfile, *cipherKey);
} else {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FILE),
errmsg("No key file obsserver.key.cipher"),
errhint("Please create obsserver.key.cipher file with gs_guc, such as : gs_guc generate -S XXX -D $GAUSSHOME/bin -o "
"obsserver")));
}
}
* Target :Encrypt functions for security.
* Description :Generating random salt if there is not a saved one,
* and then use the random salt to encrypt plaintext.
* Input :Pointer of plaintext, key, ciphertext and cipherlen
* Output :successed: true, failed: false.
*/
bool gs_encrypt_aes_speed(GS_UCHAR* plaintext, GS_UCHAR* key, GS_UCHAR* ciphertext, GS_UINT32* cipherlen)
{
GS_UINT32 retval = 0;
GS_UINT32 plainlen = 0;
GS_UCHAR init_rand[RANDOM_LEN] = {0};
bool encryptstatus = false;
errno_t errorno = EOK;
static THR_LOCAL GS_UCHAR random_salt_saved[RANDOM_LEN] = {0};
static THR_LOCAL bool random_salt_tag = false;
static THR_LOCAL GS_UINT64 random_salt_count = 0;
const GS_UINT64 random_salt_count_max = 24000000;
if (random_salt_tag == false || random_salt_count > random_salt_count_max) {
retval = RAND_bytes(init_rand, RANDOM_LEN);
if (retval != 1) {
(void)fprintf(stderr, _("generate random key failed,errcode:%u\n"), retval);
return false;
}
random_salt_tag = true;
errorno = memcpy_s(random_salt_saved, RANDOM_LEN, init_rand, RANDOM_LEN);
securec_check(errorno, "\0", "\0");
random_salt_count = 0;
} else {
errorno = memcpy_s(init_rand, RANDOM_LEN, random_salt_saved, RANDOM_LEN);
securec_check(errorno, "\0", "\0");
random_salt_count++;
}
plainlen = strlen((const char*)plaintext);
encryptstatus = aes128EncryptSpeed(plaintext, plainlen, key, init_rand, ciphertext, cipherlen);
if (!encryptstatus) {
ereport(WARNING, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("aes128EncryptSpeedFailed!")));
return false;
}
GS_UCHAR mac_temp[MAC_LEN] = {0};
errorno = memcpy_s(mac_temp, MAC_LEN, ciphertext + *cipherlen - MAC_LEN, MAC_LEN);
securec_check(errorno, "\0", "\0");
errorno = memcpy_s(ciphertext + *cipherlen - MAC_LEN + RANDOM_LEN, MAC_LEN, mac_temp, MAC_LEN);
securec_check(errorno, "\0", "\0");
GS_UCHAR temp[RANDOM_LEN] = {0};
for (GS_UINT32 i = (*cipherlen - MAC_LEN) / RANDOM_LEN; i >= 1; --i) {
errorno = memcpy_s(temp, RANDOM_LEN, ciphertext + (i - 1) * RANDOM_LEN, RANDOM_LEN);
securec_check(errorno, "\0", "\0");
errorno = memcpy_s(ciphertext + i * RANDOM_LEN, RANDOM_LEN, temp, RANDOM_LEN);
securec_check(errorno, "\0", "\0");
}
errorno = memcpy_s(ciphertext, RANDOM_LEN, init_rand, RANDOM_LEN);
securec_check(errorno, "\0", "\0");
*cipherlen = *cipherlen + RANDOM_LEN;
errorno = memset_s(temp, RANDOM_LEN, '\0', RANDOM_LEN);
securec_check(errorno, "\0", "\0");
return true;
}
* Target :Decrypt functions for security.
* Description :Read random salt from ciphertext,
* and then use the random salt to decrypt plaintext.
* Input :Pointer of plaintext, key, ciphertext and the value of cipherlen and plainlen.
* Output :successed: true, failed: false.
*/
bool gs_decrypt_aes_speed(
GS_UCHAR* ciphertext, GS_UINT32 cipherlen, GS_UCHAR* key, GS_UCHAR* plaintext, GS_UINT32* plainlen)
{
GS_UINT32 cipherpartlen = 0;
GS_UCHAR* cipherpart = NULL;
GS_UCHAR* randpart = NULL;
bool decryptstatus = false;
errno_t errorno = EOK;
if (cipherlen < (3 * RANDOM_LEN + MAC_LEN)) {
(void)fprintf(stderr, _("Cipertext is too short to decrypt\n"));
return false;
}
cipherpartlen = cipherlen - RANDOM_LEN;
cipherpart = (GS_UCHAR*)palloc(cipherpartlen + 1);
randpart = (GS_UCHAR*)palloc(RANDOM_LEN);
errorno = memcpy_s(cipherpart, cipherpartlen + 1, ciphertext + RANDOM_LEN, cipherpartlen);
securec_check(errorno, "\0", "\0");
errorno = memcpy_s(randpart, RANDOM_LEN, ciphertext, RANDOM_LEN);
securec_check(errorno, "\0", "\0");
decryptstatus = aes128DecryptSpeed(cipherpart, cipherpartlen, key, randpart, plaintext, plainlen);
errorno = memset_s(cipherpart, cipherpartlen + 1, '\0', cipherpartlen + 1);
securec_check(errorno, "\0", "\0");
pfree_ext(cipherpart);
errorno = memset_s(randpart, RANDOM_LEN, '\0', RANDOM_LEN);
securec_check(errorno, "\0", "\0");
pfree_ext(randpart);
if (!decryptstatus) {
return false;
} else {
return true;
}
}
static inline const char* GetCipherPrefix(int mode)
{
switch (mode) {
case SOURCE_MODE:
return "datasource";
case USER_MAPPING_MODE:
return "usermapping";
case SUBSCRIPTION_MODE:
return "subscription";
case HADR_MODE:
return "hadr";
default:
ereport(ERROR, (errmsg("unknown key mode: %d", mode)));
return NULL;
}
}
* getECKeyString:
* Get Extension Connector(EC) key string
*
* @IN/OUT cipherKey: get key string and stored in cipherKey
* @RETURN: void
*/
static GS_UCHAR* getECKeyString(KeyMode mode)
{
Assert(mode == SOURCE_MODE || mode == USER_MAPPING_MODE || mode == SUBSCRIPTION_MODE || mode == HADR_MODE);
GS_UCHAR* plainkey = NULL;
char* gshome = NULL;
char cipherdir[MAXPGPATH] = {0};
char cipherfile[MAXPGPATH] = {0};
const char *cipherPrefix = GetCipherPrefix(mode);
int ret = 0;
* Get cipher file and prepare the plain buffer
*/
gshome = gs_getenv_r("GAUSSHOME");
char real_gausshome[PATH_MAX + 1] = {'\0'};
if (gshome == NULL || realpath(gshome, real_gausshome) == NULL) {
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("Failed to get EC certificate file: get env GAUSSHOME failed.")));
}
gshome = real_gausshome;
check_backend_env(gshome);
ret = snprintf_s(cipherdir, MAXPGPATH, MAXPGPATH - 1, "%s/bin", gshome);
securec_check_ss(ret, "\0", "\0");
ret = snprintf_s(cipherfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s.key.cipher", gshome, cipherPrefix);
securec_check_ss_c(ret, "\0", "\0");
gshome = NULL;
plainkey = (GS_UCHAR*)palloc(RANDOM_LEN + 1);
ret = memset_s(plainkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(ret, "\0", "\0");
* Decode cipher file, if not given SOURCE cipher file, we use SERVER mode
*/
if (file_exists(cipherfile)) {
decode_cipher_files(mode, NULL, cipherdir, plainkey);
} else {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FILE),
errmsg("No key file %s.key.cipher", cipherPrefix),
errhint("Please create %s.key.cipher file with gs_guc and gs_ssh, such as :"
"gs_ssh -c \"gs_guc generate -S XXX -D $GAUSSHOME/bin -o %s\"", cipherPrefix, cipherPrefix)));
}
* Note: plainkey is of length RANDOM_LEN + 1
*/
return plainkey;
}
* encryptECString:
* Encrypt Extension Connector(EC) string(username/password, etc.) into cipher text.
* This function use aes128 to encrypt plain text; key comes from certificate file
*
* @IN src_plain_text: source plain text to be encrypted
* @IN mode: key mode
* @RETURN: dest buffer to be filled with encrypted string, caller should reset and free the buffer
*/
char *encryptECString(char* src_plain_text, int mode)
{
#define RANDOM_STR_NUM 4
#define BASE64_INPUT_PER_GROUP_BYTE 3
#define BASE64_OUTPUT_PER_GROUP_BYTE 4
GS_UINT32 ciphertextlen = 0;
GS_UCHAR* cipherkey = NULL;
char* encodetext = NULL;
errno_t ret = EOK;
cipherkey = getECKeyString((KeyMode)mode);
* Calculate the max length of ciphertext:
* 1. contain aes-expand length, IV length, Salt length and backup length. Add mac text length.
* (refer to gs_encrypt_aes128_function)
* 2. calculate then length after base64((len / 3 + 1) * 4)
* 3. add encrypt prefix len and ending symbol len
*/
GS_UINT32 cipherTextMaxLen = (GS_UINT32)(((strlen(src_plain_text) + RANDOM_LEN * RANDOM_STR_NUM + MAC_LEN) /
BASE64_INPUT_PER_GROUP_BYTE + 1) * BASE64_OUTPUT_PER_GROUP_BYTE + strlen(EC_ENCRYPT_PREFIX) + 1);
char *ciphertext = (char*)palloc0(cipherTextMaxLen);
* Step-1: Cipher
* src_text with cipher key -> cipher text
*/
ciphertextlen = cipherTextMaxLen;
if (!gs_encrypt_aes_128((GS_UCHAR*)src_plain_text, cipherkey,
(GS_UINT32)strlen((const char*)cipherkey), (GS_UCHAR*)ciphertext, &ciphertextlen)) {
ret = memset_s(src_plain_text, strlen(src_plain_text), 0, strlen(src_plain_text));
securec_check(ret, "\0", "\0");
ret = memset_s(ciphertext, cipherTextMaxLen, 0, cipherTextMaxLen);
securec_check(ret, "\0", "\0");
ret = memset_s(cipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(ret, "\0", "\0");
pfree_ext(src_plain_text);
pfree_ext(cipherkey);
pfree_ext(ciphertext);
ereport(
ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("encrypt the EC string failed!")));
}
* Step-2: Encode
* cipher text by Base64 -> encode text
*/
encodetext = SEC_encodeBase64(ciphertext, ciphertextlen + RANDOM_LEN);
if (encodetext == NULL || cipherTextMaxLen < strlen(EC_ENCRYPT_PREFIX) + strlen(encodetext) + 1) {
if (encodetext != NULL) {
ret = memset_s(encodetext, strlen(encodetext), 0, strlen(encodetext));
securec_check(ret, "\0", "\0");
OPENSSL_free(encodetext);
encodetext = NULL;
}
ret = memset_s(src_plain_text, strlen(src_plain_text), 0, strlen(src_plain_text));
securec_check(ret, "\0", "\0");
ret = memset_s(ciphertext, cipherTextMaxLen, 0, cipherTextMaxLen);
securec_check(ret, "\0", "\0");
ret = memset_s(cipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(ret, "\0", "\0");
pfree_ext(src_plain_text);
pfree_ext(cipherkey);
pfree_ext(ciphertext);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("Encrypt EC internal error: dest cipher length is too short.")));
}
ret = memcpy_s(ciphertext, cipherTextMaxLen, EC_ENCRYPT_PREFIX, strlen(EC_ENCRYPT_PREFIX));
securec_check(ret, "\0", "\0");
ret = memcpy_s(ciphertext + strlen(EC_ENCRYPT_PREFIX),
cipherTextMaxLen - strlen(EC_ENCRYPT_PREFIX),
encodetext,
strlen(encodetext) + 1);
securec_check(ret, "\0", "\0");
ret = memset_s(encodetext, strlen(encodetext), 0, strlen(encodetext));
securec_check(ret, "\0", "\0");
ret = memset_s(cipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(ret, "\0", "\0");
OPENSSL_free(encodetext);
encodetext = NULL;
pfree_ext(cipherkey);
return ciphertext;
}
* decryptECString:
* Decrypt Extension Connector(EC) string(username/password, etc.) into plain text.
* This function use aes128 to decrypt cipher text; key comes from certificate file
*
* @IN src_cipher_text: source cipher text to be decrypted
* @OUT dest_plain_text: dest buffer to be filled with plain text, this buffer
* should be reset and free by caller
* @IN mode: key mode
* @RETURN: bool, true if encrypt success, false if not
*/
bool decryptECString(const char* src_cipher_text, char** dest_plain_text, int mode)
{
GS_UCHAR* ciphertext = NULL;
GS_UINT32 ciphertextlen = 0;
GS_UCHAR* plaintext = NULL;
GS_UINT32 plaintextlen = 0;
GS_UCHAR* cipherkey = NULL;
errno_t ret = EOK;
if (NULL == src_cipher_text || (!IsECEncryptedString(src_cipher_text) && mode != HADR_MODE)) {
return false;
}
cipherkey = getECKeyString((KeyMode)mode);
if (mode != HADR_MODE) {
src_cipher_text += strlen(EC_ENCRYPT_PREFIX);
}
ciphertext = (GS_UCHAR*)(SEC_decodeBase64(src_cipher_text, &ciphertextlen));
plaintext = (GS_UCHAR*)palloc(ciphertextlen);
ret = memset_s(plaintext, ciphertextlen, 0, ciphertextlen);
securec_check(ret, "\0", "\0");
if (!gs_decrypt_aes_128(ciphertext, ciphertextlen, cipherkey,
(GS_UINT32)strlen((const char*)cipherkey), plaintext, &plaintextlen)) {
ret = memset_s(plaintext, plaintextlen, 0, plaintextlen);
securec_check(ret, "\0", "\0");
ret = memset_s(cipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(ret, "\0", "\0");
ret = memset_s(ciphertext, ciphertextlen, 0, ciphertextlen);
securec_check(ret, "\0", "\0");
pfree_ext(plaintext);
pfree_ext(cipherkey);
OPENSSL_free(ciphertext);
ciphertext = NULL;
ereport(
ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("decrypt the EC string failed!")));
}
*dest_plain_text = (char*)palloc0(plaintextlen + 1);
ret = memcpy_s(*dest_plain_text, plaintextlen + 1, plaintext, plaintextlen);
securec_check(ret, "\0", "\0");
ret = memset_s(plaintext, plaintextlen, 0, plaintextlen);
securec_check(ret, "\0", "\0");
ret = memset_s(cipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(ret, "\0", "\0");
ret = memset_s(ciphertext, ciphertextlen, 0, ciphertextlen);
securec_check(ret, "\0", "\0");
pfree_ext(plaintext);
pfree_ext(cipherkey);
OPENSSL_free(ciphertext);
ciphertext = NULL;
return true;
}
* IsECEncryptedString:
* check the input string whether encypted
*
* @IN src_cipher_text: source cipher text to be decrypted
* @RETURN: bool, true if encrypted, false if not
*/
bool IsECEncryptedString(const char* src_cipher_text)
{
if (NULL == src_cipher_text || strlen(src_cipher_text) <= RANDOM_LEN + strlen(EC_ENCRYPT_PREFIX)) {
return false;
}
if (0 == strncmp(src_cipher_text, EC_ENCRYPT_PREFIX, strlen(EC_ENCRYPT_PREFIX))) {
return true;
} else {
return false;
}
}
* EncryptGenericOptions:
* Encrypt data source or foreign data wrapper generic options, before transformation
*
* @IN src_options: source options to be encrypted
* @RETURN: void
*/
void EncryptGenericOptions(List* options, const char** sensitiveOptionsArray, int arrayLength, int mode)
{
int i;
char* srcString = NULL;
errno_t ret;
ListCell* cell = NULL;
bool isPrint = false;
foreach (cell, options) {
DefElem* def = (DefElem*)lfirst(cell);
Node* arg = def->arg;
if (def->defaction == DEFELEM_DROP || def->arg == NULL || !IsA(def->arg, String))
continue;
srcString = strVal(def->arg);
if (srcString == NULL || strlen(srcString) == 0)
continue;
for (i = 0; i < arrayLength; i++) {
if (pg_strcasecmp(def->defname, sensitiveOptionsArray[i]) == 0) {
if (IsECEncryptedString(srcString)) {
if (!isPrint) {
ereport(NOTICE,
(errmodule(MOD_EC),
errcode(ERRCODE_INVALID_PASSWORD),
errmsg("Using probably encrypted option (prefix='encryptOpt') directly and it is not "
"recommended."),
errhint("The %s object can't be used if the option is not encrypted correctly.",
GetCipherPrefix(mode))));
isPrint = true;
}
break;
}
* current we only check length in subscription mode, cause datasource and user mapping
* could save database A or B password, which we not sure how long it can be.
*/
if (mode == (int)SUBSCRIPTION_MODE && (int)strlen(srcString) > MAX_PASSWORD_LENGTH) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Password can't contain more than %d characters.", MAX_PASSWORD_LENGTH)));
}
char *encryptString = encryptECString(srcString, mode);
def->arg = (Node*)makeString(pstrdup(encryptString));
ret = memset_s(encryptString, strlen(encryptString), 0, strlen(encryptString));
securec_check(ret, "\0", "\0");
pfree_ext(encryptString);
ret = memset_s(srcString, strlen(srcString), 0, strlen(srcString));
securec_check(ret, "\0", "\0");
pfree_ext(srcString);
pfree_ext(arg);
break;
}
}
}
}
* DecryptOptions:
* Decrypt generic options, before transformation
*
* @IN options: source options to be decrypted
* @IN sensitiveOptionsArray: options name to be decrypted
* @IN arrayLength: array length of sensitiveOptionsArray
* @IN mode: key mode
* @RETURN: void
*/
void DecryptOptions(List *options, const char** sensitiveOptionsArray, int arrayLength, int mode)
{
if (options == NULL) {
return;
}
ListCell *cell = NULL;
foreach(cell, options) {
DefElem* def = (DefElem*)lfirst(cell);
if (def->defname == NULL || def->arg == NULL || !IsA(def->arg, String)) {
continue;
}
char *str = strVal(def->arg);
if (str == NULL || strlen(str) == 0) {
continue;
}
for (int i = 0; i < arrayLength; i++) {
if (pg_strcasecmp(def->defname, sensitiveOptionsArray[i]) == 0) {
char *plainText = NULL;
* If decryptECString return false, it means the stored values is not encrypted.
* This happened when user mapping was created in old gaussdb version.
*/
if (decryptECString(str, &plainText, mode)) {
pfree_ext(str);
pfree_ext(def->arg);
def->arg = (Node*)makeString(pstrdup(plainText));
errno_t errCode = memset_s(plainText, strlen(plainText), 0, strlen(plainText));
securec_check(errCode, "\0", "\0");
pfree_ext(plainText);
}
break;
}
}
}
}
char int2hex(uint8 n)
{
char ret;
if (n >= 0x0 && n <= 0x9) {
ret = n + '0';
} else if (n >= 0xa && n <= 0xf) {
ret = (n - 0xa) + 'a';
} else {
ret = '0';
}
return ret;
}
uint8 hex2int(char ch)
{
uint8 ret;
if (ch >= '0' && ch <= '9') {
ret = ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
ret = (ch - 'a') + 10;
} else if (ch >= 'A' && ch <= 'F') {
ret = (ch - 'A') + 10;
} else {
ret = 0;
}
return ret;
}
int GetTdeCipherLenByAlog(uint8 algo)
{
int cipher_len;
switch (algo) {
case TDE_ALGO_AES_128_CTR:
case TDE_ALGO_SM4_CTR:
cipher_len = DEK_CIPHER_AES128_LEN_PAGE;
break;
case TDE_ALGO_AES_256_CTR:
cipher_len = DEK_CIPHER_AES256_LEN_PAGE;
break;
default:
cipher_len = DEK_CIPHER_AES128_LEN_PAGE;
break;
}
return cipher_len;
}
* Description:
* Transform TdePageInfo to TdeInfo format
* @IN tde_page_info: the tde info read from page
* @OUT tde_info: used for encryption and decryption
* @RETURN NONE
*/
void transformTdeInfoFromPage(TdeInfo* tde_info, TdePageInfo* tde_page_info)
{
int i, j;
int cipher_len;
errno_t ret;
const uint8 cmk_id_format[CMK_ID_LEN] = "1234abcd-1234-abcd-1234-1234abcd1234";
cipher_len = GetTdeCipherLenByAlog(tde_page_info->algo);
for (i = 0, j = 0; i < cipher_len && j < DEK_CIPHER_LEN - 1; i++, j += 2) {
tde_info->dek_cipher[j] = int2hex(tde_page_info->dek_cipher[i] >> 4);
tde_info->dek_cipher[j + 1] = int2hex(tde_page_info->dek_cipher[i] & 0x0f);
}
if (j < DEK_CIPHER_LEN - 1) {
tde_info->dek_cipher[j] = '\0';
} else {
tde_info->dek_cipher[DEK_CIPHER_LEN - 1] = '\0';
}
for (i = 0, j = 0; i < CMK_ID_LEN_PAGE && j < CMK_ID_LEN - 1; j++) {
if (cmk_id_format[j] == '-') {
tde_info->cmk_id[j] = '-';
continue;
}
tde_info->cmk_id[j] = int2hex(tde_page_info->cmk_id[i] >> 4);
tde_info->cmk_id[j + 1] = int2hex(tde_page_info->cmk_id[i] & 0x0f);
i++;
j++;
}
if (j < CMK_ID_LEN - 1) {
tde_info->cmk_id[j] = '\0';
} else {
tde_info->cmk_id[CMK_ID_LEN - 1] = '\0';
}
tde_info->algo = tde_page_info->algo;
ret = memcpy_s(tde_info->iv, RANDOM_IV_LEN, tde_page_info->iv, RANDOM_IV_LEN);
securec_check(ret, "\0", "\0");
ret = memcpy_s(tde_info->tag, GCM_TAG_LEN, tde_page_info->tag, GCM_TAG_LEN);
securec_check(ret, "\0", "\0");
}
* Description:
* To reduce storage space usage on page, tde_info.dek_cipher and tde_info.cmk_id formats are converted
* @IN tde_info: used for encryption and decryption
* @OUT tde_page_info: tde info to be written to page
* @RETURN NONE
*/
void transformTdeInfoToPage(TdeInfo* tde_info, TdePageInfo* tde_page_info)
{
int i, j;
int cipher_len;
errno_t ret;
cipher_len = GetTdeCipherLenByAlog(tde_info->algo);
for (i = 0, j = 0; i < cipher_len && j < DEK_CIPHER_LEN - 1; i++, j += 2) {
tde_page_info->dek_cipher[i] = hex2int(tde_info->dek_cipher[j]) << 4 | hex2int(tde_info->dek_cipher[j + 1]);
}
for (i = 0, j = 0; i < CMK_ID_LEN_PAGE && j < CMK_ID_LEN - 1; j++) {
if (tde_info->cmk_id[j] == '-') {
continue;
}
tde_page_info->cmk_id[i] = hex2int(tde_info->cmk_id[j]) << 4 | hex2int(tde_info->cmk_id[j + 1]);
i++;
j++;
}
tde_page_info->algo = tde_info->algo;
ret = memcpy_s(tde_page_info->iv, RANDOM_IV_LEN, tde_info->iv, RANDOM_IV_LEN);
securec_check(ret, "\0", "\0");
ret = memcpy_s(tde_page_info->tag, GCM_TAG_LEN, tde_info->tag, GCM_TAG_LEN);
securec_check(ret, "\0", "\0");
}
* Description:
* encrypt block or CU exclude header and filling,user data must encrypt,if block or cu encrypt failed,
* retry three times, if it still failed then quit process, shoud use panic not fatal for result in hang
* on checkpoint or bgwriter
* @IN plain text and plain text length
* @OUT cipher text and cipher text length
* @RETURN: NONE
*/
void encryptBlockOrCUData(const char* plainText, const size_t plainLength,
char* cipherText, size_t* cipherLength, TdeInfo* tdeInfo)
{
int retryCnt = 3;
unsigned char* iv = tdeInfo->iv;
TdeAlgo algo = (TdeAlgo)tdeInfo->algo;
unsigned char key[KEY_128BIT_LEN] = {0};
const char* plain_key = NULL;
unsigned int i, j;
errno_t ret = 0;
#ifndef ENABLE_UT
TDEKeyManager *tde_key_manager = New(CurrentMemoryContext) TDEKeyManager();
tde_key_manager->init();
plain_key = tde_key_manager->get_key(tdeInfo->cmk_id, tdeInfo->dek_cipher);
#else
plain_key = fake_tde_get_key(plain_key);
#endif
Assert(strlen(plain_key) == KEY_128BIT_LEN * 2);
for (i = 0, j = 0; i < KEY_128BIT_LEN && j < strlen(plain_key); i++, j += 2) {
key[i] = hex2int(plain_key[j]) << 4 | hex2int(plain_key[j + 1]);
}
#ifndef ENABLE_UT
DELETE_EX2(tde_key_manager);
#endif
do {
if (encrypt_partial_mode(plainText, plainLength, cipherText, cipherLength, key, iv, algo)) {
break;
}
retryCnt--;
} while (retryCnt > 0);
ret = memset_s(key, KEY_128BIT_LEN, 0, KEY_128BIT_LEN);
securec_check(ret, "\0", "\0");
if (retryCnt == 0) {
ereport(PANIC,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("encrypt failed after retry three times"),
errdetail("invalid arguments or internal error")));
}
}
* Description:
* decrypt block or CU exclude header and filling,user data must decrypt, if block or cu decrypt failed,
* retry three times, if it still failed then quit process, shoud use panic not fatal for result in hang
* on autovacuum
* @IN cipher text and cipher text length
* @OUT plain text and plain text length
* @RETURN: NONE
*/
void decryptBlockOrCUData(const char* cipherText, const size_t cipherLength,
char* plainText, size_t* plainLength, TdeInfo* tdeInfo)
{
int retryCnt = 3;
unsigned char* iv = tdeInfo->iv;
TdeAlgo algo = (TdeAlgo)tdeInfo->algo;
unsigned char key[KEY_128BIT_LEN] = {0};
const char* plain_key = NULL;
unsigned int i, j;
errno_t ret = 0;
#ifndef ENABLE_UT
TDEKeyManager *tde_key_manager = New(CurrentMemoryContext) TDEKeyManager();
tde_key_manager->init();
plain_key = tde_key_manager->get_key(tdeInfo->cmk_id, tdeInfo->dek_cipher);
#else
plain_key = fake_tde_get_key(plain_key);
#endif
Assert(strlen(plain_key) == KEY_128BIT_LEN * 2);
for (i = 0, j = 0; i < KEY_128BIT_LEN && j < strlen(plain_key); i++, j += 2) {
key[i] = hex2int(plain_key[j]) << 4 | hex2int(plain_key[j + 1]);
}
#ifndef ENABLE_UT
DELETE_EX2(tde_key_manager);
#endif
do {
if (decrypt_partial_mode(cipherText, cipherLength, plainText, plainLength, key, iv, algo)) {
break;
}
retryCnt--;
} while (retryCnt > 0);
ret = memset_s(key, KEY_128BIT_LEN, 0, KEY_128BIT_LEN);
securec_check(ret, "\0", "\0");
if (retryCnt == 0) {
ereport(PANIC,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("decrypt failed after retry three times"),
errdetail("invalid arguments or internal error")));
}
}
static void do_advice(void)
{
(void)printf(_("Try \"%s --help\" for more information.\n"), pgname);
}
static void do_help(void)
{
(void)printf(_("%s is an inferface to encrypt input plaintext string.\n\n"), pgname);
(void)printf(_("Usage:\n"));
(void)printf(_(" %s [OPTION]... PLAINTEXT\n"), pgname);
(void)printf(_("\nGeneral options:\n"));
(void)printf(_(" -?, --help show this help, then exit.\n"));
(void)printf(_(" -V, --version output version information, then exit.\n"));
(void)printf(_(" -k, --key=PASSWORD the password for AES128.\n"));
(void)printf(_(" -v, --vector=VectorValue the random vector for AES128.\n"));
(void)printf(_(" -f, --file-prefix=FilePrefix the cipher files prefix.\n"));
(void)printf(_(" -B, --key-base64=Value the key value encoded in base64.\n"));
(void)printf(_(" -D, --vector-base64=Value the random value encoded in base64.\n"));
(void)printf(_(" PLAINTEXT the plain text you want to encrypt.\n"));
}
* @@GaussDB@@
* Brief : IsLegalPreix()
* Input : prefixStr -> the input parameter value
* Description : check the parameter. It is only support digit/alpha/'-'/'_'
*/
bool IsLegalPreix(const char* prefixStr)
{
int NBytes = int(strlen(prefixStr));
for (int i = 0; i < NBytes; i++) {
if (IsIllegalCharacter(prefixStr[i])) {
return false;
}
}
return true;
}
static void free_global_value(void)
{
GS_FREE(g_prefix);
if (g_key != NULL) {
errno_t rc = memset_s(g_key, strlen(g_key), 0, strlen(g_key));
securec_check_c(rc, "\0", "\0");
}
GS_FREE(g_key);
GS_FREE(g_vector);
}
static bool is_prefix_in_key_mode(const char* mode)
{
int slen = strlen("server");
int clen = strlen("client");
int srclen = strlen("source");
int obslen = strlen("obsserver");
if ((0 == strncmp(mode, "server", slen) && '\0' == mode[slen]) ||
(0 == strncmp(mode, "client", clen) && '\0' == mode[clen]) ||
(0 == strncmp(mode, "source", srclen) && '\0' == mode[srclen]) ||
(0 == strncmp(mode, "obsserver", obslen) && '\0' == mode[obslen])) {
return true;
} else {
return false;
}
}
#define CIPHERTEXT_LEN 512
int encrypte_main(int argc, char* const argv[])
{
#define MAXLOGINFOLEN 1024
static struct option long_options[] = {{"help", no_argument, NULL, '?'},
{"version", no_argument, NULL, 'V'},
{"file-prefix", required_argument, NULL, 'f'},
{"key", required_argument, NULL, 'k'},
{"vector", required_argument, NULL, 'v'},
{"key-base64", required_argument, NULL, 'B'},
{"vector-base64", required_argument, NULL, 'D'},
{NULL, 0, NULL, 0}};
int ret = 0;
int result = 0;
GS_UCHAR* plaintext = NULL;
GS_UCHAR* ciphertext = NULL;
GS_UINT32 ciphertextlen = 0;
char* encodetext = NULL;
GS_UCHAR* decipherkey = NULL;
char* cipherpath = NULL;
char cipherkeyfile[MAXPGPATH] = {'\0'};
char destciphertext[CIPHERTEXT_LEN] = {0};
char isexistscipherkeyfile[MAXPGPATH] = {'\0'};
char isexistsrandfile[MAXPGPATH] = {'\0'};
int c = 0;
int optindex = 0;
errno_t rc = 0;
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("gs_encrypt"));
if (argc > 1) {
if (strncmp(argv[1], "--help", sizeof("--help")) == 0 || strncmp(argv[1], "-?", sizeof("-?")) == 0) {
do_help();
exit(0);
} else if (strncmp(argv[1], "-V", sizeof("-V")) == 0 ||
strncmp(argv[1], "--version", sizeof("--version")) == 0) {
puts("gs_encrypt " DEF_GS_VERSION);
exit(0);
}
}
while ((c = getopt_long(argc, argv, "f:v:k:B:D:", long_options, &optindex)) != -1) {
switch (c) {
case 'f': {
GS_FREE(g_prefix);
g_prefix = gs_strdup(optarg);
if (0 == strlen(g_prefix) || !IsLegalPreix(g_prefix)) {
(void)fprintf(stderr, _("%s: -f only be formed of 'a~z', 'A~Z', '0~9', '-', '_'\n"), pgname);
free_global_value();
do_advice();
exit(1);
}
break;
}
case 'v': {
GS_FREE(g_vector);
g_vector = gs_strdup(optarg);
if (strlen(g_vector) != RANDOM_LEN) {
(void)fprintf(stderr, _("%s: the length of -v must equal to %d\n"), pgname, RANDOM_LEN);
free_global_value();
do_advice();
exit(1);
}
g_vector_len = (int)strlen(g_vector);
break;
}
case 'k': {
if (g_key != NULL) {
rc = memset_s(g_key, strlen(g_key), 0, strlen(g_key));
securec_check_c(rc, "\0", "\0");
}
GS_FREE(g_key);
g_key = gs_strdup(optarg);
if (0 == strlen(optarg) || !mask_single_passwd(optarg)) {
(void)fprintf(stderr, _("%s: mask passwd failed. optarg is null or out of memory!\n"), pgname);
free_global_value();
do_advice();
exit(1);
}
g_key_len = (int)strlen(g_key);
break;
}
case 'B': {
GS_UCHAR* decoded_key = NULL;
GS_UINT32 decodelen = 0;
if (g_key != NULL) {
rc = memset_s(g_key, strlen(g_key), 0, strlen(g_key));
securec_check_c(rc, "\0", "\0");
}
GS_FREE(g_key);
decoded_key = (GS_UCHAR*)SEC_decodeBase64(optarg, &decodelen);
if (0 == strlen(optarg) || !mask_single_passwd(optarg)) {
(void)fprintf(stderr, _("%s: mask base64 encoded key failed. optarg is null or out of memory!\n"),
pgname);
free_global_value();
do_advice();
exit(1);
}
if (decoded_key == NULL || decodelen == 0) {
(void)fprintf(stderr, _("%s: failed to decode base64 encoded key.\n"), pgname);
free_global_value();
do_advice();
exit(1);
}
g_key = gs_strdup((char *)decoded_key);
OPENSSL_free(decoded_key);
decoded_key = NULL;
g_key_len = (int)decodelen;
break;
}
case 'D': {
GS_UCHAR* decoded_vector = NULL;
GS_UINT32 decodelen = 0;
GS_FREE(g_vector);
decoded_vector = (GS_UCHAR*)SEC_decodeBase64(optarg, &decodelen);
if (decoded_vector == NULL || decodelen == 0) {
(void)fprintf(stderr, _("%s: failed to decode base64 encoded vector.\n"), pgname);
free_global_value();
do_advice();
exit(1);
}
if (decodelen != RANDOM_LEN) {
(void)fprintf(
stderr, _("%s: the decoded length of vector must be equal to %d.\n"), pgname, RANDOM_LEN);
free_global_value();
do_advice();
exit(1);
}
g_vector = gs_strdup((char *)decoded_vector);
OPENSSL_free(decoded_vector);
decoded_vector = NULL;
g_vector_len = (int)decodelen;
break;
}
default:
do_help();
exit(1);
}
}
if (optind < argc) {
plaintext = (GS_UCHAR*)gs_strdup(argv[optind]);
if (!mask_single_passwd(argv[optind])) {
(void)fprintf(stderr, _("%s: mask plaintext failed. optarg is null, or out of memory!\n"), pgname);
free_global_value();
do_advice();
exit(1);
}
optind++;
}
if (optind < argc) {
(void)fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), pgname, argv[optind]);
(void)fprintf(stderr, _("Try \"%s --help\" for more information.\n"), pgname);
free_global_value();
GS_FREE(plaintext);
exit(1);
}
if (NULL == plaintext || 0 == strlen((char*)plaintext)) {
(void)fprintf(stderr, _("%s: options PLAINTEXT must be input and it is not null\n"), pgname);
free_global_value();
GS_FREE(plaintext);
exit(1);
}
if ((NULL == g_key && NULL != g_vector) || (NULL != g_key && NULL == g_vector)) {
(void)fprintf(stderr, _("%s: key and vector should be both specified.\n"), pgname);
free_global_value();
GS_FREE(plaintext);
exit(1);
}
if (NULL != g_prefix && (NULL != g_key || NULL != g_vector)) {
(void)fprintf(stderr, _("%s: key/vector and cipher files cannot be used together.\n"), pgname);
free_global_value();
GS_FREE(plaintext);
exit(1);
}
if (g_key != NULL && !check_input_password(g_key)) {
(void)fprintf(stderr, _("%s: The input key must be %d~%d bytes and "
"contain at least three kinds of characters!\n"),
pgname, MIN_KEY_LEN, MAX_KEY_LEN);
free_global_value();
GS_FREE(plaintext);
exit(1);
}
cipherpath = getGaussHome();
if (cipherpath == NULL) {
(void)fprintf(stderr, _("get environment of GAUSSHOME failed.\n"));
free_global_value();
GS_FREE(plaintext);
return -1;
}
char* tmp_cipherpath = (char*)malloc(strlen(cipherpath) + 1);
if (tmp_cipherpath == NULL) {
(void)fprintf(stderr, _("memory is temporarily unavailable.\n"));
free_global_value();
GS_FREE(plaintext);
return -1;
}
rc = strcpy_s(tmp_cipherpath, strlen(cipherpath) + 1, cipherpath);
securec_check(rc, "\0", "\0");
ret = snprintf_s(cipherkeyfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin", cipherpath);
securec_check_ss(ret, "\0", "\0");
if (NULL != g_prefix) {
ret = snprintf_s(
isexistscipherkeyfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s.key.cipher", tmp_cipherpath, g_prefix);
securec_check_ss_c(ret, "\0", "\0");
ret = snprintf_s(isexistsrandfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s.key.rand", tmp_cipherpath, g_prefix);
securec_check_ss_c(ret, "\0", "\0");
if (!file_exists(isexistscipherkeyfile) || !file_exists(isexistsrandfile)) {
(void)fprintf(stderr,
_("%s: options -f is not correct. Failed to get cipher file[%s] and random file[%s]\n"),
pgname,
isexistscipherkeyfile,
isexistsrandfile);
free(tmp_cipherpath);
tmp_cipherpath = NULL;
free_global_value();
GS_FREE(plaintext);
exit(1);
}
} else {
ret =
snprintf_s(isexistscipherkeyfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/obsserver.key.cipher", tmp_cipherpath);
securec_check_ss_c(ret, "\0", "\0");
}
free(tmp_cipherpath);
tmp_cipherpath = NULL;
cipherpath = NULL;
ciphertext = (GS_UCHAR*)malloc(RANDOM_LEN + 1);
if (ciphertext == NULL) {
(void)fprintf(stderr, _("memory is temporarily unavailable.\n"));
result = -1;
goto ENCRYPTE_MAIN_EXIT;
}
ret = memset_s(ciphertext, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(ret, "\0", "\0");
if ((NULL == g_prefix && NULL == g_key && NULL == g_vector) ||
(NULL != g_prefix && is_prefix_in_key_mode((const char*)g_prefix))) {
if (file_exists(isexistscipherkeyfile)) {
decode_cipher_files(OBS_MODE, NULL, cipherkeyfile, ciphertext);
} else {
decode_cipher_files(SERVER_MODE, NULL, cipherkeyfile, ciphertext, true);
}
}
decipherkey = ciphertext;
ciphertext = (GS_UCHAR*)malloc(CIPHERTEXT_LEN);
if (ciphertext == NULL) {
(void)fprintf(stderr, _("memory is temporarily unavailable.\n"));
result = -1;
goto ENCRYPTE_MAIN_EXIT;
}
ret = memset_s(ciphertext, CIPHERTEXT_LEN, 0, CIPHERTEXT_LEN);
securec_check(ret, "\0", "\0");
ciphertextlen = CIPHERTEXT_LEN;
if (!gs_encrypt_aes_128((GS_UCHAR*)plaintext, decipherkey,
(GS_UINT32)strlen((const char*)decipherkey), ciphertext, &ciphertextlen)) {
(void)fprintf(stderr, _("Encrypt input text failed.\n"));
result = -1;
goto ENCRYPTE_MAIN_EXIT;
}
encodetext = SEC_encodeBase64((char*)ciphertext, ciphertextlen + RANDOM_LEN);
if (NULL == encodetext) {
(void)fprintf(stderr, _("Encrypt input text internal error.\n"));
result = -1;
goto ENCRYPTE_MAIN_EXIT;
}
if (CIPHERTEXT_LEN < strlen(encodetext) + 1) {
(void)fprintf(stderr, _("Encrypt input text internal error.\n"));
result = -1;
goto ENCRYPTE_MAIN_EXIT;
}
ret = memcpy_s(destciphertext, CIPHERTEXT_LEN, encodetext, (strlen(encodetext) + 1));
securec_check(ret, "\0", "\0");
#ifndef ENABLE_UT
(void)fprintf(stdout, _("%s\n"), destciphertext);
#endif
ENCRYPTE_MAIN_EXIT:
if (encodetext != NULL) {
ret = memset_s(encodetext, strlen(encodetext) + 1, 0, strlen(encodetext) + 1);
securec_check(ret, "\0", "\0");
OPENSSL_free(encodetext);
encodetext = NULL;
}
if (ciphertext != NULL) {
ret = memset_s(ciphertext, CIPHERTEXT_LEN, 0, CIPHERTEXT_LEN);
securec_check(ret, "\0", "\0");
free(ciphertext);
ciphertext = NULL;
}
if (decipherkey != NULL) {
ret = memset_s(decipherkey, RANDOM_LEN + 1, 0, RANDOM_LEN + 1);
securec_check(ret, "\0", "\0");
free(decipherkey);
decipherkey = NULL;
}
free_global_value();
GS_FREE(plaintext);
return result;
}
* Read key and vector from cipher key and random file.
* key: must be with length of CIPHER_LEN + 1
* vector: must be with length of CIPHER_LEN + 1.
* Which is the random of cipherkeyfile.
*/
bool getKeyVectorFromCipherFile(const char* cipherkeyfile, const char* cipherrndfile, GS_UCHAR* key, GS_UCHAR* vector)
{
GS_UINT32 plainlen = 0;
RandkeyFile rand_file_content;
CipherkeyFile cipher_file_content;
int ret = 0;
if (!ReadContentFromFile(cipherkeyfile, &cipher_file_content, sizeof(cipher_file_content)) ||
!ReadContentFromFile(cipherrndfile, &rand_file_content, sizeof(rand_file_content))) {
ClearCipherKeyFile(&cipher_file_content);
ClearRandKeyFile(&rand_file_content);
(void)fprintf(stderr,
_("Failed to read contents of cipher files of transparent encryption: %s, %s.\n"),
cipherkeyfile,
cipherrndfile);
return false;
}
if (!CipherFileIsValid(&cipher_file_content) || !RandFileIsValid(&rand_file_content)) {
ClearCipherKeyFile(&cipher_file_content);
ClearRandKeyFile(&rand_file_content);
(void)fprintf(
stderr, _("Corrupt cipher files of transparent encryption: %s, %s.\n"), cipherkeyfile, cipherrndfile);
return false;
}
if (!DecryptInputKey(cipher_file_content.cipherkey, CIPHER_LEN, rand_file_content.randkey,
cipher_file_content.key_salt, cipher_file_content.vector_salt, key, &plainlen)) {
ClearCipherKeyFile(&cipher_file_content);
ClearRandKeyFile(&rand_file_content);
return false;
}
ret = memcpy_s(
vector, sizeof(rand_file_content.randkey), rand_file_content.randkey, sizeof(rand_file_content.randkey));
securec_check_c(ret, "", "");
return true;
}
* If any, throw a PANIC error.
*/
char* getGaussHome()
{
char* tmp = NULL;
char* danger_token[] = {";", "`", "\\", "'", "\"", ">", "<", "$", "&", "|", "!", "\n", NULL};
const int MAX_GAUSSHOME_LEN = 4096;
tmp = gs_getenv_r("GAUSSHOME");
char real_gausshome[PATH_MAX + 1] = {'\0'};
if (tmp == NULL || realpath(tmp, real_gausshome) == NULL) {
(void)fprintf(stderr, _("Get environment of GAUSSHOME failed.\n"));
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("Get environment of GAUSSHOME failed.\n")));
return NULL;
}
tmp = real_gausshome;
if (strlen(tmp) > MAX_GAUSSHOME_LEN) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("environment of GAUSSHOME is too long.\n")));
return NULL;
}
char* tmp_tmp = (char*)palloc(strlen(tmp) + 1);
errno_t rc = strcpy_s(tmp_tmp, strlen(tmp) + 1, tmp);
securec_check(rc, "\0", "\0");
if ('\0' == *tmp_tmp) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("$GAUSSHOME is not set or it's NULL.\n")));
return NULL;
}
for (int i = 0; danger_token[i] != NULL; ++i) {
if (strstr(tmp_tmp, danger_token[i])) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Invalid character '%s' in $GAUSSHOME.\n", danger_token[i])));
return NULL;
}
}
return tmp_tmp;
}
static bool gs_encrypt_sm4_function(FunctionCallInfo fcinfo, text** outtext)
{
char* key = NULL;
GS_UINT32 keylen = 0;
char* plaintext = NULL;
GS_UINT32 plaintextlen = 0;
char* ciphertext = NULL;
char* encodestring = NULL;
GS_UINT32 encodetextlen = 0;
GS_UINT32 ciphertextlenmax = 0;
GS_UCHAR user_key[SM4_KEY_LENGTH];
GS_UCHAR userIv[SM4_IV_LENGTH] = {0};
errno_t errorno = EOK;
GS_UINT32 ret = 0;
size_t cipherLength = 0;
key = (text_to_cstring(PG_GETARG_TEXT_P(1)));
keylen = strlen((const char*)key);
if (!check_input_password(key)) {
errorno = memset_s(key, keylen, '\0', keylen);
securec_check(errorno, "\0", "\0");
pfree_ext(key);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("The encryption key must be %d~%d bytes and contain at least three kinds of characters!",
MIN_KEY_LEN, SM4_KEY_LENGTH)));
}
errorno = memcpy_s(user_key, SM4_KEY_LENGTH, (GS_UCHAR*)key, keylen);
securec_check(errorno, "\0", "\0");
if (keylen < SM4_KEY_LENGTH) {
errorno = memset_s(user_key + keylen, SM4_KEY_LENGTH - keylen, '\0', SM4_KEY_LENGTH - keylen);
securec_check(errorno, "\0", "\0");
}
errorno = memset_s(key, keylen, '\0', keylen);
securec_check(errorno, "\0", "\0");
pfree_ext(key);
ret = RAND_bytes(userIv, SM4_IV_LENGTH);
if (ret != 1) {
errorno = memset_s(userIv, SM4_IV_LENGTH, '\0', SM4_IV_LENGTH);
securec_check(errorno, "", "");
ereport(ERROR, (errmsg("generate random sm4 vector failed,errcode:%d", ret)));
}
* Calculate the max length of ciphertext:
*/
plaintext = (char*)(text_to_cstring(PG_GETARG_TEXT_P(0)));
plaintextlen = strlen((const char*)plaintext);
ciphertextlenmax = plaintextlen + SM4_IV_LENGTH + SM4_BLOCK_SIZE - 1;
ciphertext = (char*)palloc(ciphertextlenmax);
errorno = memset_s(ciphertext, ciphertextlenmax, '\0', ciphertextlenmax);
securec_check(errorno, "\0", "\0");
errorno = memcpy_s(ciphertext, SM4_IV_LENGTH, userIv, SM4_IV_LENGTH);
securec_check(errorno, "\0", "\0");
ret = encrypt_partial_mode(
plaintext, plaintextlen, ciphertext + SM4_IV_LENGTH, &cipherLength, user_key, userIv, TDE_ALGO_SM4_CTR);
errorno = memset_s(plaintext, plaintextlen, '\0', plaintextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(plaintext);
if (ret != true) {
errorno = memset_s(ciphertext, cipherLength, '\0', cipherLength);
securec_check(errorno, "\0", "\0");
pfree_ext(ciphertext);
ereport(ERROR,
(errmsg("sm4 encrypt fail"),
errdetail("sm4_ctr_enc_partial_mode fail")));
}
cipherLength = cipherLength + SM4_IV_LENGTH;
encodestring = SEC_encodeBase64((const char*)ciphertext, cipherLength);
errorno = memset_s(ciphertext, cipherLength, '\0', cipherLength);
securec_check(errorno, "\0", "\0");
pfree_ext(ciphertext);
if (encodestring == NULL) {
ciphertextlenmax = 0;
ereport(
ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("encode the plain text failed!")));
}
encodetextlen = strlen((const char*)encodestring);
*outtext = cstring_to_text((const char*)encodestring);
errorno = memset_s(encodestring, encodetextlen, '\0', encodetextlen);
securec_check(errorno, "\0", "\0");
OPENSSL_free(encodestring);
encodestring = NULL;
return true;
}
static bool gs_decrypt_sm4_function(FunctionCallInfo fcinfo, text** outtext)
{
char* key = NULL;
GS_UINT32 keylen = 0;
char* ciphertext = NULL;
GS_UINT32 ciphertextlen = 0;
GS_UINT32 cipherpartlen;
char* encodetext = NULL;
char* cipherpart = NULL;
GS_UCHAR* decodetext = NULL;
GS_UINT32 ret = 0;
GS_UINT32 decodetextlen = 0;
GS_UINT32 ciphertextlenmax = 0;
GS_UCHAR userkey[SM4_KEY_LENGTH];
GS_UCHAR userIv[SM4_IV_LENGTH] = {0};
errno_t errorno = EOK;
size_t encodetextlen = 0;
key = (char*)(text_to_cstring(PG_GETARG_TEXT_P(1)));
keylen = strlen((const char*)key);
if (!check_input_password(key)) {
errorno = memset_s(key, keylen, '\0', keylen);
securec_check(errorno, "\0", "\0");
pfree_ext(key);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("The decryption key must be %d~%d bytes and contain at least three kinds of characters!",
MIN_KEY_LEN, SM4_KEY_LENGTH)));
}
errorno = memcpy_s(userkey, SM4_KEY_LENGTH, (GS_UCHAR*)key, keylen);
securec_check(errorno, "\0", "\0");
if (keylen < SM4_KEY_LENGTH) {
errorno = memset_s(userkey + keylen, SM4_KEY_LENGTH - keylen, '\0', SM4_KEY_LENGTH - keylen);
securec_check(errorno, "\0", "\0");
}
errorno = memset_s(key, keylen, '\0', keylen);
securec_check(errorno, "\0", "\0");
pfree_ext(key);
decodetext = (GS_UCHAR*)(text_to_cstring(PG_GETARG_TEXT_P(0)));
decodetextlen = strlen((const char*)decodetext);
ciphertext = (char*)(SEC_decodeBase64((const char*)decodetext, &ciphertextlen));
if ((ciphertext == NULL) || (ciphertextlen < (SM4_IV_LENGTH + 1))) {
if (ciphertext != NULL) {
OPENSSL_free(ciphertext);
ciphertext = NULL;
}
errorno = memset_s(decodetext, decodetextlen, '\0', decodetextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(decodetext);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("Decode the cipher text failed or the ciphertext is wrong!")));
}
errorno = memset_s(decodetext, decodetextlen, '\0', decodetextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(decodetext);
errorno = memcpy_s(userIv, SM4_IV_LENGTH, ciphertext, SM4_IV_LENGTH);
securec_check(errorno, "\0", "\0");
ciphertextlenmax = ciphertextlen + SM4_BLOCK_SIZE - 1;
encodetext = (char*)palloc(ciphertextlenmax);
errorno = memset_s(encodetext, ciphertextlenmax, '\0', ciphertextlenmax);
securec_check(errorno, "\0", "\0");
cipherpartlen = ciphertextlen - SM4_IV_LENGTH;
cipherpart = (char*)palloc(cipherpartlen + 1);
errorno = memcpy_s(cipherpart, cipherpartlen + 1, ciphertext + SM4_IV_LENGTH, cipherpartlen);
securec_check(errorno, "\0", "\0");
errorno = memset_s(ciphertext, ciphertextlen, '\0', ciphertextlen);
securec_check(errorno, "\0", "\0");
OPENSSL_free(ciphertext);
ciphertext = NULL;
ret = decrypt_partial_mode(
cipherpart, cipherpartlen, encodetext, &encodetextlen, userkey, userIv, TDE_ALGO_SM4_CTR);
if (ret != true) {
errorno = memset_s(cipherpart, cipherpartlen + 1, '\0', cipherpartlen + 1);
securec_check(errorno, "\0", "\0");
pfree_ext(cipherpart);
errorno = memset_s(encodetext, encodetextlen, '\0', encodetextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(encodetext);
ereport(ERROR,
(errmsg("sm4 decrypt fail"),
errdetail("sm4_ctr_dec_partial_mode fail")));
}
errorno = memset_s(cipherpart, cipherpartlen + 1, '\0', cipherpartlen + 1);
securec_check(errorno, "\0", "\0");
pfree_ext(cipherpart);
encodetextlen = strlen((const char*)encodetext);
*outtext = cstring_to_text((char*)encodetext);
errorno = memset_s(encodetext, encodetextlen, '\0', encodetextlen);
securec_check(errorno, "\0", "\0");
pfree_ext(encodetext);
encodetext = NULL;
return true;
}
Datum gs_encrypt(PG_FUNCTION_ARGS)
{
GS_UCHAR* encrypttype = NULL;
text* outtext = NULL;
bool status = false;
if (PG_ARGISNULL(0)) {
fcinfo->isnull = true;
outtext = NULL;
PG_RETURN_TEXT_P(outtext);
}
if (PG_ARGISNULL(1)) {
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("The encryption key can not be empty!")));
}
if (PG_ARGISNULL(2)) {
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("The encryption type can not be empty!")));
}
encrypttype = (GS_UCHAR*)(text_to_cstring(PG_GETARG_TEXT_P(2)));
if (strcmp((const char*)encrypttype, ENCRYPT_TYPE_SM4) == 0) {
status = gs_encrypt_sm4_function(fcinfo, &outtext);
} else if (strcmp((const char*)encrypttype, ENCRYPT_TYPE_AES128) == 0) {
status = gs_encrypt_aes128_function(fcinfo, &outtext);
} else {
pfree_ext(encrypttype);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("Encryption type is wrong!")));
}
pfree_ext(encrypttype);
if (!status) {
outtext = NULL;
}
PG_RETURN_TEXT_P(outtext);
}
Datum gs_decrypt(PG_FUNCTION_ARGS)
{
GS_UCHAR* decrypttype = NULL;
text* outtext = NULL;
bool status = false;
if (PG_ARGISNULL(0)) {
fcinfo->isnull = true;
outtext = NULL;
PG_RETURN_TEXT_P(outtext);
}
if (PG_ARGISNULL(1)) {
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("The decryption key can not be empty!")));
}
if (PG_ARGISNULL(2)) {
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("The decryption type can not be empty!")));
}
decrypttype = (GS_UCHAR*)(text_to_cstring(PG_GETARG_TEXT_P(2)));
if (strcmp((const char*)decrypttype, ENCRYPT_TYPE_SM4) == 0) {
status = gs_decrypt_sm4_function(fcinfo, &outtext);
} else if (strcmp((const char*)decrypttype, ENCRYPT_TYPE_AES128) == 0) {
status = gs_decrypt_aes128_function(fcinfo, &outtext);
} else {
pfree_ext(decrypttype);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("Decryption type is wrong!")));
}
pfree_ext(decrypttype);
if (!status) {
outtext = NULL;
}
PG_RETURN_TEXT_P(outtext);
}
Datum gs_encrypt_aes128(PG_FUNCTION_ARGS)
{
text* outtext = NULL;
bool status = false;
if (PG_ARGISNULL(0)) {
fcinfo->isnull = true;
outtext = NULL;
PG_RETURN_TEXT_P(outtext);
}
if (PG_ARGISNULL(1)) {
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("The encryption key can not be empty!")));
}
status = gs_encrypt_aes128_function(fcinfo, &outtext);
if (!status) {
outtext = NULL;
}
PG_RETURN_TEXT_P(outtext);
}
Datum gs_decrypt_aes128(PG_FUNCTION_ARGS)
{
text* outtext = NULL;
bool status = false;
if (PG_ARGISNULL(0)) {
fcinfo->isnull = true;
outtext = NULL;
PG_RETURN_TEXT_P(outtext);
}
if (PG_ARGISNULL(1)) {
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("The decryption key can not be empty!")));
}
status = gs_decrypt_aes128_function(fcinfo, &outtext);
if (!status) {
outtext = NULL;
}
PG_RETURN_TEXT_P(outtext);
}