2725 lines
96 KiB
C++
2725 lines
96 KiB
C++
/*
|
|
* 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"
|
|
#include "gssapi/gssapi_krb5.h"
|
|
|
|
#include "keymanagement/KeyManager.h"
|
|
|
|
#include "openssl/rand.h"
|
|
#include "openssl/evp.h"
|
|
#include "openssl/crypto.h"
|
|
#include "commands/defrem.h"
|
|
|
|
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);
|
|
|
|
GS_UCHAR* trans_encrypt_dek = NULL;
|
|
GS_UCHAR* trans_encrypt_iv = NULL;
|
|
|
|
// Fi root certificate path
|
|
char* g_fi_ca_path = NULL;
|
|
|
|
// tde algo
|
|
TDE_ALGO g_tde_algo = TDE_ALGO_NULL;
|
|
|
|
// the prefix of the output cipher/randfile
|
|
char* g_prefix = NULL;
|
|
int g_vector_len = 0;
|
|
// key text
|
|
char* g_key = NULL;
|
|
int g_key_len = 0;
|
|
// vector text
|
|
char* g_vector = NULL;
|
|
|
|
#define EC_ENCRYPT_PREFIX "encryptOpt"
|
|
|
|
#define ENCRYPT_TYPE_SM4 "sm4"
|
|
#define ENCRYPT_TYPE_AES128 "aes128"
|
|
|
|
// free the malloc memory
|
|
#define GS_FREE(ptr) \
|
|
if (NULL != (ptr)) { \
|
|
free((char*)(ptr)); \
|
|
ptr = NULL; \
|
|
}
|
|
|
|
// check the character value
|
|
#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;
|
|
|
|
/* split the cipher text to cipherpart(ciphertext + vectorsalt) and randpart for decrypt */
|
|
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");
|
|
|
|
/* the real decrypt operation */
|
|
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.
|
|
*/
|
|
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);
|
|
|
|
/* The input key must shorter than RANDOM_LEN(16) */
|
|
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;
|
|
|
|
/* refer combo_encrypt_len function ,cipherlen = plainlen + 64 */
|
|
ciphertext = (GS_UCHAR*)palloc(ciphertextlen_max);
|
|
errorno = memset_s(ciphertext, ciphertextlen_max, '\0', ciphertextlen_max);
|
|
securec_check(errorno, "\0", "\0");
|
|
|
|
/* encrypt the plaintext to ciphertext */
|
|
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!")));
|
|
}
|
|
|
|
/* encode the ciphertext for nice show and decrypt operation */
|
|
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);
|
|
|
|
/* decode the ciphertext for decrypt operation */
|
|
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) {
|
|
/* reset plaintext */
|
|
errorno = memset_s(plaintext, decodetextlen, '\0', decodetextlen);
|
|
securec_check(errorno, "\0", "\0");
|
|
/* try different decrypt function */
|
|
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);
|
|
|
|
/* the result of do getKeyVectorFromCipherFile */
|
|
bool status = false;
|
|
|
|
if (NULL == plaintext) {
|
|
return false;
|
|
}
|
|
plainlen = strlen((const char*)plaintext);
|
|
|
|
/* default branch should do decode */
|
|
/* -f is in key mode */
|
|
if ((NULL == g_prefix && NULL == g_key && NULL == g_vector) ||
|
|
(NULL != g_prefix && is_prefix_in_key_mode((const char*)g_prefix))) {
|
|
/* get a random values as salt for encrypt */
|
|
retval = RAND_priv_bytes(init_rand, RANDOM_LEN);
|
|
if (retval != 1) {
|
|
(void)fprintf(stderr, _("generate random key failed, errcode:%u\n"), retval);
|
|
return false;
|
|
}
|
|
/* the real encrypt operation */
|
|
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;
|
|
}
|
|
|
|
/* add init_rand to the end of ciphertext for decrypt */
|
|
rc = memcpy_s(ciphertext + (*cipherlen), (Size)RANDOM_LEN, init_rand, (Size)RANDOM_LEN);
|
|
securec_check(rc, "\0", "\0");
|
|
return true;
|
|
}
|
|
|
|
/* -f is not in key mode */
|
|
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");
|
|
|
|
/* get key from file */
|
|
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;
|
|
}
|
|
/* add init_rand to the end of ciphertext for decrypt */
|
|
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;
|
|
}
|
|
|
|
/* add init_rand to the end of ciphertext for decrypt */
|
|
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;
|
|
}
|
|
/* split the cipher text to cipher(cipher + vector_salt) part and rand part for decrypt */
|
|
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");
|
|
|
|
/* the real decrypt operation */
|
|
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.")));
|
|
}
|
|
|
|
/* 1 byte for \0 */
|
|
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");
|
|
// proccess for unit test failed for no obsserver key
|
|
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;
|
|
|
|
/* use saved random salt unless unavailable */
|
|
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;
|
|
|
|
/* A limitation of using times of one random salt */
|
|
const GS_UINT64 random_salt_count_max = 24000000;
|
|
|
|
if (random_salt_tag == false || random_salt_count > random_salt_count_max) {
|
|
/* get a random values as salt for encrypt */
|
|
retval = RAND_priv_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);
|
|
|
|
/* the real encrypt operation */
|
|
encryptstatus = aes128EncryptSpeed(plaintext, plainlen, key, init_rand, ciphertext, cipherlen);
|
|
if (!encryptstatus) {
|
|
ereport(WARNING, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("aes128EncryptSpeedFailed!")));
|
|
return false;
|
|
}
|
|
|
|
/* add init_rand to the head of ciphertext for decrypt */
|
|
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;
|
|
|
|
/* split the ciphertext to cipherpart and randpart for decrypt */
|
|
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");
|
|
|
|
/* the real decrypt operation */
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
GS_UCHAR* plainkey = NULL;
|
|
char* gshome = NULL;
|
|
char cipherdir[MAXPGPATH] = {0};
|
|
char cipherfile[MAXPGPATH] = {0};
|
|
const char *cipherPrefix = (mode == SOURCE_MODE ? "datasource" : "usermapping");
|
|
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;
|
|
|
|
/* Alloc plain key buffer and clear it */
|
|
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
|
|
* @OUT dest_cipher_text: dest buffer to be filled with encrypted string, this buffer
|
|
* should be given by caller
|
|
* @IN dest_cipher_length: dest buffer length which is given by the caller
|
|
* @RETURN: void
|
|
*/
|
|
void encryptECString(char* src_plain_text, char* dest_cipher_text, uint32 dest_cipher_length, bool isDataSource)
|
|
{
|
|
GS_UINT32 ciphertextlen = 0;
|
|
GS_UCHAR ciphertext[1024];
|
|
GS_UCHAR* cipherkey = NULL;
|
|
char* encodetext = NULL;
|
|
errno_t ret = EOK;
|
|
|
|
if (NULL == src_plain_text) {
|
|
return;
|
|
}
|
|
|
|
/* First, get encrypt key */
|
|
cipherkey = getECKeyString(isDataSource ? SOURCE_MODE : USER_MAPPING_MODE);
|
|
|
|
/* Clear cipher buffer which will be used later */
|
|
ret = memset_s(ciphertext, sizeof(ciphertext), 0, sizeof(ciphertext));
|
|
securec_check(ret, "\0", "\0");
|
|
|
|
/*
|
|
* Step-1: Cipher
|
|
* src_text with cipher key -> cipher text
|
|
*/
|
|
ciphertextlen = (GS_UINT32)sizeof(ciphertext);
|
|
if (!gs_encrypt_aes_128((GS_UCHAR*)src_plain_text, cipherkey,
|
|
(GS_UINT32)strlen((const char*)cipherkey), 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, sizeof(ciphertext), 0, sizeof(ciphertext));
|
|
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);
|
|
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((char*)ciphertext, ciphertextlen + RANDOM_LEN);
|
|
|
|
/* Check dest buffer length */
|
|
if (encodetext == NULL || dest_cipher_length < 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, sizeof(ciphertext), 0, sizeof(ciphertext));
|
|
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);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
|
|
errmsg("Encrypt EC internal error: dest cipher length is too short.")));
|
|
}
|
|
|
|
/* Copy the encrypt string into the dest buffer */
|
|
ret = memcpy_s(dest_cipher_text, dest_cipher_length, EC_ENCRYPT_PREFIX, strlen(EC_ENCRYPT_PREFIX));
|
|
securec_check(ret, "\0", "\0");
|
|
ret = memcpy_s(dest_cipher_text + strlen(EC_ENCRYPT_PREFIX),
|
|
dest_cipher_length - strlen(EC_ENCRYPT_PREFIX),
|
|
encodetext,
|
|
strlen(encodetext) + 1);
|
|
securec_check(ret, "\0", "\0");
|
|
|
|
/* Clear buffer for safety's sake */
|
|
ret = memset_s(encodetext, strlen(encodetext), 0, strlen(encodetext));
|
|
securec_check(ret, "\0", "\0");
|
|
ret = memset_s(ciphertext, sizeof(ciphertext), 0, sizeof(ciphertext));
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* 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 given by caller
|
|
* @IN dest_plain_length: dest buffer length which is given by the caller
|
|
* @RETURN: bool, true if encrypt success, false if not
|
|
*/
|
|
bool decryptECString(const char* src_cipher_text, char* dest_plain_text, uint32 dest_plain_length, bool isDataSource)
|
|
{
|
|
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)) {
|
|
return false;
|
|
}
|
|
|
|
/* Get key string */
|
|
cipherkey = getECKeyString(isDataSource ? SOURCE_MODE : USER_MAPPING_MODE);
|
|
|
|
/* Step-1: Decode */
|
|
ciphertext = (GS_UCHAR*)(SEC_decodeBase64((char*)(src_cipher_text + strlen(EC_ENCRYPT_PREFIX)), &ciphertextlen));
|
|
plaintext = (GS_UCHAR*)palloc(ciphertextlen);
|
|
ret = memset_s(plaintext, ciphertextlen, 0, ciphertextlen);
|
|
securec_check(ret, "\0", "\0");
|
|
|
|
/* Step-2: Decipher */
|
|
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!")));
|
|
}
|
|
|
|
/* Check dest buffer length */
|
|
if (plaintextlen > dest_plain_length) {
|
|
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 EC internal error: dest plain length is too short.")));
|
|
}
|
|
|
|
/* Copy the decrypted string into the dest buffer */
|
|
ret = memcpy_s(dest_plain_text, dest_plain_length, plaintext, plaintextlen);
|
|
securec_check(ret, "\0", "\0");
|
|
|
|
/* Clear the buffer for safety's sake */
|
|
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, bool isDataSource)
|
|
{
|
|
int i;
|
|
char* srcString = NULL;
|
|
char encryptString[EC_CIPHER_TEXT_LENGTH];
|
|
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;
|
|
|
|
/* Get src string to be encrypted */
|
|
srcString = strVal(def->arg);
|
|
/* For empty value, we do not encrypt */
|
|
if (srcString == NULL || strlen(srcString) == 0)
|
|
continue;
|
|
|
|
for (i = 0; i < arrayLength; i++) {
|
|
if (0 == pg_strcasecmp(def->defname, sensitiveOptionsArray[i])) {
|
|
/* For string with prefix='encryptOpt' (probably encrypted), we do not encrypt again */
|
|
if (IsECEncryptedString(srcString)) {
|
|
/* We report warning only once in a CREATE/ALTER stmt. */
|
|
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.",
|
|
isDataSource ? "DATA SOURCE" : "USER MAPPING")));
|
|
isPrint = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* Encrypt the src string */
|
|
encryptECString(srcString, encryptString, EC_CIPHER_TEXT_LENGTH, isDataSource);
|
|
|
|
/* Substitute the src */
|
|
def->arg = (Node*)makeString(pstrdup(encryptString));
|
|
|
|
/* Clear the encrypted string */
|
|
ret = memset_s(encryptString, sizeof(encryptString), 0, sizeof(encryptString));
|
|
securec_check(ret, "\0", "\0");
|
|
|
|
/* Clear the src string */
|
|
ret = memset_s(srcString, strlen(srcString), 0, strlen(srcString));
|
|
securec_check(ret, "\0", "\0");
|
|
pfree_ext(srcString);
|
|
pfree_ext(arg);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* encryptBlockAndCU:
|
|
* 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)
|
|
{
|
|
int retryCnt = 0;
|
|
GS_UINT32 ret = 0;
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
if ((trans_encrypt_dek == NULL || *trans_encrypt_dek == '\0') ||
|
|
(trans_encrypt_iv == NULL || *trans_encrypt_iv == '\0')) {
|
|
ereport(PANIC,
|
|
(errmsg("it's an encrypted cluster, but parameter not initialized!"),
|
|
errdetail("decrypt DEK or IV is empty or both length is not equal 16")));
|
|
}
|
|
|
|
do {
|
|
if (g_tde_algo == TDE_ALGO_SM4_CTR_128) {
|
|
ret = sm4_ctr_enc_partial_mode(
|
|
plainText, plainLength, cipherText, cipherLength, trans_encrypt_dek, trans_encrypt_iv);
|
|
} else {
|
|
ret = aes_ctr_enc_partial_mode(
|
|
plainText, plainLength, cipherText, cipherLength, trans_encrypt_dek, trans_encrypt_iv);
|
|
}
|
|
if (ret != 0) {
|
|
retryCnt++;
|
|
} else {
|
|
if (plainLength != *cipherLength) {
|
|
ereport(PANIC,
|
|
(errmsg("encrypt failed, return code is %u!", ret),
|
|
errdetail("cipher length is not correct, \
|
|
plian length is %lu, cipher length is %lu",
|
|
plainLength,
|
|
*cipherLength)));
|
|
}
|
|
return;
|
|
}
|
|
if (retryCnt == 2) {
|
|
ereport(PANIC,
|
|
(errmsg("encrypt failed after retry three times, error code is %u!", ret),
|
|
errdetail("ret code is Invalid arguments or \
|
|
Invalid IV length or Internal error ")));
|
|
}
|
|
} while (retryCnt < 3);
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
int retryCnt = 0;
|
|
GS_UINT32 ret = 0;
|
|
if ((trans_encrypt_dek == NULL || *trans_encrypt_dek == '\0') ||
|
|
(trans_encrypt_iv == NULL || *trans_encrypt_iv == '\0')) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmsg("it's an encrypted cluster, but parameter not initialized!"),
|
|
errdetail("decrypt DEK or IV is empty or both length is not equal 16")));
|
|
}
|
|
do {
|
|
if (g_tde_algo == TDE_ALGO_SM4_CTR_128) {
|
|
ret = sm4_ctr_dec_partial_mode(
|
|
cipherText, cipherLength, plainText, plainLength, trans_encrypt_dek, trans_encrypt_iv);
|
|
} else {
|
|
ret = aes_ctr_dec_partial_mode(
|
|
cipherText, cipherLength, plainText, plainLength, trans_encrypt_dek, trans_encrypt_iv);
|
|
}
|
|
if (ret != 0) {
|
|
retryCnt++;
|
|
} else {
|
|
if (cipherLength != *plainLength) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_STRING_DATA_LENGTH_MISMATCH),
|
|
errmsg("decrypt failed, return code is %u!", ret),
|
|
errdetail("plainLength length is not correct, cipher length is %lu,plian length is %lu",
|
|
cipherLength,
|
|
*plainLength)));
|
|
}
|
|
return;
|
|
}
|
|
if (retryCnt == 2) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("decrypt failed after retry three times, error code is %u!", ret),
|
|
errdetail("ret code is Invalid arguments or Invalid IV length or Internal error ")));
|
|
}
|
|
} while (retryCnt < 3);
|
|
}
|
|
|
|
/* Get the FI root cert */
|
|
void add_fi_ca_cert()
|
|
{
|
|
if (g_fi_ca_path != NULL) {
|
|
return;
|
|
}
|
|
|
|
char* node_agent_env = gs_getenv_r("NODE_AGENT_HOME");
|
|
char real_node_agent_home[PATH_MAX + 1] = {'\0'};
|
|
if (node_agent_env == NULL || realpath(node_agent_env, real_node_agent_home) == NULL) {
|
|
(void)fprintf(stderr, _("Transparent encryption get environment variable NODE_AGENT_HOME failed.\n"));
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
|
|
errmsg("Transparent encryption Getting environment variable NODE_AGENT_HOME failed.\n")));
|
|
}
|
|
node_agent_env = real_node_agent_home;
|
|
check_backend_env(node_agent_env);
|
|
|
|
g_fi_ca_path = (char*)palloc0(MAXPGPATH);
|
|
int ret =
|
|
snprintf_s(g_fi_ca_path, MAXPGPATH, MAXPGPATH - 1, "%s/security/cert/subcert/certFile/ca.crt", node_agent_env);
|
|
securec_check_ss_c(ret, "\0", "\0");
|
|
}
|
|
|
|
/* Using MPPDB_KRB5_FILE_PATH instead of KRB5_CONF. Add the FI root cert. */
|
|
void init_the_kerberos_env()
|
|
{
|
|
char* gaussdb_krb5_env = gs_getenv_r("MPPDB_KRB5_FILE_PATH");
|
|
|
|
char real_mppdb_krb5_file_path[PATH_MAX + 1] = {'\0'};
|
|
if (gaussdb_krb5_env == NULL || realpath(gaussdb_krb5_env, real_mppdb_krb5_file_path) == NULL) {
|
|
(void)fprintf(stderr, _("Transparent encryption Getting environment variable MPPDB_KRB5_FILE_PATH failed.\n"));
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
|
|
errmsg("Transparent encryption Getting environment variable MPPDB_KRB5_FILE_PATH failed.\n")));
|
|
}
|
|
gaussdb_krb5_env = real_mppdb_krb5_file_path;
|
|
|
|
check_backend_env(gaussdb_krb5_env);
|
|
krb5_set_profile_path(gaussdb_krb5_env);
|
|
|
|
add_fi_ca_cert();
|
|
}
|
|
|
|
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(_(" -N, --key-name=Value the cluster key name in KMS.\n"));
|
|
(void)printf(_(" -U, --kms-url-=Value the kms internet address. for "
|
|
"example,\"kms://https@host2;host3:29800/kms\".\n"));
|
|
(void)printf(_(" -I, --init=Value init the tde key. "
|
|
"the value must be SM4-CTR-128 or AES-CTR-128.\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++) {
|
|
/* check whether the character is correct */
|
|
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");
|
|
int sm4len = strlen("SM4-CTR-128");
|
|
int aeslen = strlen("AES-CTR-128");
|
|
|
|
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]) ||
|
|
(0 == strncmp(mode, "SM4-CTR-128", sm4len) && '\0' == mode[sm4len]) ||
|
|
(0 == strncmp(mode, "AES-CTR-128", aeslen) && '\0' == mode[aeslen])) {
|
|
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'},
|
|
{"init", required_argument, NULL, 'I'},
|
|
{"key-name", required_argument, NULL, 'N'},
|
|
{"kms-url", required_argument, NULL, 'U'},
|
|
{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* kmsurl = NULL;
|
|
char* keyname = NULL;
|
|
char* tdealgo = 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"));
|
|
|
|
/* support --help and --version even if invoked as root */
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* process command-line options */
|
|
while ((c = getopt_long(argc, argv, "f:v:k:B:D:I:N:U:", 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);
|
|
// the length of g_vector must equal to 16
|
|
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);
|
|
}
|
|
// the length of g_vector must equal to 16
|
|
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;
|
|
}
|
|
case 'N': {
|
|
GS_FREE(keyname);
|
|
keyname = gs_strdup(optarg);
|
|
if (keyname == NULL) {
|
|
(void)fprintf(stderr, _("%s: Key name is wrong, it must be usable!\n"), pgname);
|
|
free_global_value();
|
|
do_advice();
|
|
exit(1);
|
|
}
|
|
break;
|
|
}
|
|
case 'U': {
|
|
GS_FREE(kmsurl);
|
|
kmsurl = gs_strdup(optarg);
|
|
if (strlen(kmsurl) == 0) {
|
|
(void)fprintf(
|
|
stderr, _("%s: KMS url is wrong, it must be an accessible network address.\n"), pgname);
|
|
free_global_value();
|
|
do_advice();
|
|
exit(1);
|
|
}
|
|
break;
|
|
}
|
|
case 'I': {
|
|
GS_FREE(tdealgo);
|
|
tdealgo = gs_strdup(optarg);
|
|
if (!is_prefix_in_key_mode(tdealgo)) {
|
|
(void)fprintf(stderr,
|
|
_("%s: Init TDE failed. The algorithm is wrong, it must be SM4-CTR-128 or AES-CTR-128.\n"),
|
|
pgname);
|
|
free_global_value();
|
|
do_advice();
|
|
exit(1);
|
|
}
|
|
|
|
} break;
|
|
default:
|
|
do_help();
|
|
exit(1);
|
|
}
|
|
}
|
|
/* init the key */
|
|
if (tdealgo != NULL) {
|
|
if (keyname == NULL) {
|
|
(void)fprintf(stderr, _("%s: Init TDE failed. Key name is wrong, you must add -N keyname.\n"), pgname);
|
|
do_advice();
|
|
exit(1);
|
|
}
|
|
|
|
init_the_kerberos_env();
|
|
KeyManager table_a_key("0", "0"); // next version for different tabe and database.
|
|
char* gausshome = getGaussHome();
|
|
if (gausshome == NULL) {
|
|
(void)fprintf(stderr, _("%s: Getting environment variable $GAUSSHOME error, please check.\n"), pgname);
|
|
exit(1);
|
|
}
|
|
table_a_key.setfile(gausshome);
|
|
|
|
if (kmsurl != NULL) {
|
|
g_instance.attr.attr_common.transparent_encrypt_kms_url = kmsurl;
|
|
}
|
|
|
|
if (!table_a_key.init_Key(tdealgo, keyname)) {
|
|
(void)fprintf(stderr, _("%s: Init key failed, please check net.\n"), pgname);
|
|
do_advice();
|
|
exit(1);
|
|
}
|
|
|
|
if (getAndCheckTranEncryptDEK() && trans_encrypt_dek != NULL) {
|
|
(void)fprintf(stdout, _("%s: Init DEK succeeded.\n"), pgname);
|
|
} else {
|
|
(void)fprintf(stderr, _("%s: Init DEK failed.\n"), pgname);
|
|
exit(1);
|
|
}
|
|
exit(0);
|
|
}
|
|
if (tdealgo != NULL) {
|
|
free(tdealgo);
|
|
tdealgo = NULL;
|
|
}
|
|
if (kmsurl != NULL) {
|
|
free(kmsurl);
|
|
kmsurl = NULL;
|
|
}
|
|
if (keyname != NULL) {
|
|
free(keyname);
|
|
keyname = NULL;
|
|
}
|
|
/* Get plaintext from command line */
|
|
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++;
|
|
}
|
|
|
|
/* Complain if any arguments remain */
|
|
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);
|
|
}
|
|
|
|
/* check the parameter value */
|
|
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");
|
|
/* set the g_prefix default value */
|
|
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");
|
|
|
|
/* default branch should do decode */
|
|
/* -f is in key mode */
|
|
if ((NULL == g_prefix && NULL == g_key && NULL == g_vector) ||
|
|
(NULL != g_prefix && is_prefix_in_key_mode((const char*)g_prefix))) {
|
|
/* get key from file */
|
|
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");
|
|
|
|
(void)fprintf(stdout, _("%s\n"), destciphertext);
|
|
|
|
ENCRYPTE_MAIN_EXIT:
|
|
/* clean all footprint for security. */
|
|
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;
|
|
}
|
|
|
|
/* getTransEncryptKeyString:
|
|
* Get the key which used to encrypted ak/sk in transparent encryption.
|
|
* cipherKey: [input] Pointer to the buffer pointer which stores the key in trans_encrypt.key.cipher.
|
|
* rndm: [input] Pointer to the buffer pointer which stores the vector(random) value in
|
|
* trans_encrypt.key.rand.
|
|
* NOTICE: The caller should free the memory allocated to *cipherKey and *rndm
|
|
*/
|
|
bool getTransEncryptKeyString(GS_UCHAR** cipherKey, GS_UCHAR** rndm)
|
|
{
|
|
int ret = 0;
|
|
char* gausshome = NULL;
|
|
char cipherkeyfile[MAXPGPATH] = {'\0'};
|
|
char cipherrndfile[MAXPGPATH] = {'\0'};
|
|
|
|
gausshome = getGaussHome();
|
|
ret = snprintf_s(cipherrndfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/trans_encrypt" RAN_KEY_FILE, gausshome);
|
|
securec_check_ss_c(ret, "\0", "\0");
|
|
ret = snprintf_s(cipherkeyfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/trans_encrypt" CIPHER_KEY_FILE, gausshome);
|
|
securec_check_ss_c(ret, "\0", "\0");
|
|
pfree_ext(gausshome);
|
|
gausshome = NULL;
|
|
*cipherKey = (GS_UCHAR*)malloc(CIPHER_LEN + 1);
|
|
if (*cipherKey == NULL) {
|
|
(void)fprintf(stderr, _("Out of memory while getting transparent encryption key string."));
|
|
return false;
|
|
}
|
|
ret = memset_s(*cipherKey, CIPHER_LEN + 1, 0, CIPHER_LEN + 1);
|
|
securec_check_c(ret, "\0", "\0");
|
|
|
|
/* Here we use CIPHER_LEN, because that CipherkeyFile.vector is with length of CIPHER_LEN+1 */
|
|
*rndm = (GS_UCHAR*)malloc(CIPHER_LEN + 1);
|
|
if (NULL == *rndm) {
|
|
(void)fprintf(stderr, _("Out of memory while getting transparent encryption key string."));
|
|
free(*cipherKey);
|
|
*cipherKey = NULL;
|
|
return false;
|
|
}
|
|
ret = memset_s(*rndm, CIPHER_LEN + 1, 0, CIPHER_LEN + 1);
|
|
securec_check_c(ret, "\0", "\0");
|
|
|
|
if (!file_exists(cipherkeyfile)) {
|
|
(void)fprintf(stderr, _("%s not exists."), cipherkeyfile);
|
|
free(*cipherKey);
|
|
free(*rndm);
|
|
*cipherKey = NULL;
|
|
*rndm = NULL;
|
|
return false;
|
|
} else if (!file_exists(cipherrndfile)) {
|
|
(void)fprintf(stderr, _("%s not exists."), cipherrndfile);
|
|
free(*cipherKey);
|
|
free(*rndm);
|
|
*cipherKey = NULL;
|
|
*rndm = NULL;
|
|
return false;
|
|
} else if (!getKeyVectorFromCipherFile(cipherkeyfile, cipherrndfile, *cipherKey, *rndm)) {
|
|
(void)fprintf(stderr, _("Failed to decrypt key from transparent encryption cipher file %s.\n"), cipherrndfile);
|
|
free(*cipherKey);
|
|
free(*rndm);
|
|
*cipherKey = NULL;
|
|
*rndm = NULL;
|
|
return false;
|
|
}
|
|
|
|
(void)fprintf(stderr, _("Successfully to decrypt %s.\n"), cipherkeyfile);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* AK: '0-9A-Za-z'
|
|
* SK: '0-9A-Za-z'
|
|
*/
|
|
bool getAkSkForTransEncrypt(char* ak, int akbuflen, char* sk, int skbuflen)
|
|
{
|
|
#define MAX_AK_SK_FILE_SIZE 2048
|
|
|
|
GS_UCHAR* key = NULL;
|
|
GS_UCHAR* vector = NULL;
|
|
char* gausshome = NULL;
|
|
char akskfile[MAXPGPATH] = {'\0'};
|
|
FILE* fp = NULL;
|
|
int ret;
|
|
size_t cnt;
|
|
GS_UINT32 akskenplainlen = 0;
|
|
char akskfilecontent[MAX_AK_SK_FILE_SIZE] = {'\0'};
|
|
char akskplaintext[MAX_AK_SK_FILE_SIZE] = {'\0'};
|
|
char* enterlocation = NULL;
|
|
int aklen, sklen;
|
|
GS_UCHAR* decodestring = NULL;
|
|
GS_UINT32 decodelen = 0;
|
|
|
|
gausshome = getGaussHome();
|
|
|
|
ret = snprintf_s(akskfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/trans_encrypt_ak_sk.key", gausshome);
|
|
securec_check_ss_c(ret, "\0", "\0");
|
|
|
|
pfree_ext(gausshome);
|
|
gausshome = NULL;
|
|
|
|
if (!file_exists(akskfile)) {
|
|
(void)fprintf(stderr, _("AK/SK file for transparent encryption does not exist: %s\n"), akskfile);
|
|
return false;
|
|
}
|
|
|
|
if (-1 == access(akskfile, R_OK)) {
|
|
(void)fprintf(stderr, _("Permission denied to access AK/SK file for transparent encryption: %s\n"), akskfile);
|
|
return false;
|
|
}
|
|
|
|
canonicalize_path(akskfile);
|
|
|
|
fp = fopen(akskfile, "rb");
|
|
if (NULL == fp) {
|
|
(void)fprintf(stderr, _("Failed to open AK/SK file for transparent encryption: %s\n"), akskfile);
|
|
return false;
|
|
}
|
|
|
|
cnt = fread(akskfilecontent, 1, sizeof(akskfilecontent), fp);
|
|
(void)fclose(fp);
|
|
fp = NULL;
|
|
|
|
/* akskfile is just an encrypted file of ak&sk. Its length should be less than MAX_AK_SK_FILE_SIZE. */
|
|
if (cnt >= sizeof(akskfilecontent)) {
|
|
(void)fprintf(stderr, _("Corrupted AK/SK file(too long) for transparent encryption: %s\n"), akskfile);
|
|
return false;
|
|
}
|
|
|
|
akskfilecontent[MAX_AK_SK_FILE_SIZE - 1] = '\0';
|
|
|
|
if (!getTransEncryptKeyString(&key, &vector)) {
|
|
return false;
|
|
}
|
|
|
|
decodestring = (GS_UCHAR*)SEC_decodeBase64((char*)akskfilecontent, &decodelen);
|
|
|
|
if (decodestring == NULL ||
|
|
!gs_decrypt_aes_128((GS_UCHAR*)decodestring, (GS_UINT32)decodelen, (GS_UCHAR*)key,
|
|
CIPHER_LEN, (GS_UCHAR*)akskplaintext, &akskenplainlen)) {
|
|
(void)fprintf(stderr, _("Failed to decrypt AK/SK file for transparent encryption: %s\n"), akskfile);
|
|
free(key);
|
|
free(vector);
|
|
key = NULL;
|
|
vector = NULL;
|
|
if (decodestring != NULL) {
|
|
OPENSSL_free(decodestring);
|
|
decodestring = NULL;
|
|
}
|
|
return false;
|
|
}
|
|
free(key);
|
|
free(vector);
|
|
OPENSSL_free(decodestring);
|
|
decodestring = NULL;
|
|
key = NULL;
|
|
vector = NULL;
|
|
/* akskplaintext: "ak\nsk" */
|
|
enterlocation = strchr(akskplaintext, '|');
|
|
|
|
if (enterlocation == NULL) {
|
|
(void)fprintf(stderr, _("Failed to split AK/SK file content for transparent encryption: %s\n"), akskfile);
|
|
return false;
|
|
}
|
|
|
|
aklen = enterlocation - akskplaintext;
|
|
sklen = akskplaintext + akskenplainlen - enterlocation - 1;
|
|
if (aklen > (akbuflen - 1) || 0 == aklen) {
|
|
(void)fprintf(stderr, _("Invalid AK length for transparent encryption: %s\n"), akskfile);
|
|
return false;
|
|
}
|
|
|
|
if (sklen > (skbuflen - 1) || 0 == sklen) {
|
|
(void)fprintf(stderr, _("Invalid SK length for transparent encryption: %s\n"), akskfile);
|
|
return false;
|
|
}
|
|
|
|
ret = strncpy_s(ak, akbuflen, akskplaintext, aklen);
|
|
securec_check_c(ret, "\0", "\0");
|
|
ak[aklen] = '\0';
|
|
|
|
ret = strncpy_s(sk, skbuflen, enterlocation + 1, sklen);
|
|
securec_check_c(ret, "\0", "\0");
|
|
sk[sklen] = '\0';
|
|
|
|
for (int i = 0; ak[i]; i++) {
|
|
if (NULL == strchr(AK_VALID_CHRS, ak[i])) {
|
|
(void)fprintf(stderr, _("Illegal character(%c) in AK for transparent encryption: %s\n"), ak[i], akskfile);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; sk[i]; i++) {
|
|
if (NULL == strchr(SK_VALID_CHRS, sk[i])) {
|
|
(void)fprintf(stderr, _("Illegal character(%c) in SK for transparent encryption: %s\n"), sk[i], akskfile);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
(void)fprintf(stderr, _("Successfully to decrypt ak/sk for transparent encryption.\n"));
|
|
|
|
return true;
|
|
|
|
#undef MAX_AK_SK_FILE_SIZE
|
|
}
|
|
|
|
/* Get $GAUSSHOME, and check if any injection risk in it.
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* check kms url and region variables for TDE, kms_url and kms_region should not be null or '\0'.
|
|
*/
|
|
|
|
static inline void CheckKmsUrlRegin()
|
|
{
|
|
if (NULL == g_instance.attr.attr_common.transparent_encrypt_kms_url ||
|
|
'\0' == g_instance.attr.attr_common.transparent_encrypt_kms_url[0] ||
|
|
NULL == g_instance.attr.attr_security.transparent_encrypt_kms_region ||
|
|
'\0' == g_instance.attr.attr_security.transparent_encrypt_kms_region[0]) {
|
|
ereport(PANIC,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("transparent_encrypt_kms_url and transparent_encrypt_kms_region should not be empty when "
|
|
"transparent encryption enabled.\n")));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get DEK from KMS.
|
|
* Throw a PANIC if any error occurs.
|
|
* dek: the buffer to store DEK, which length should be with CIHPER_LEN + 1.
|
|
*/
|
|
void getTransEncryptDEK(GS_UCHAR* dek)
|
|
{
|
|
FILE* fp = NULL;
|
|
char *cmd = NULL;
|
|
char *cmd_log = NULL;
|
|
char *url = NULL;
|
|
char *tmp = NULL;
|
|
char *region = NULL;
|
|
char* get_result = NULL;
|
|
int rc = 0;
|
|
|
|
/* On POSIX Linux, max command line length is 4096. */
|
|
const int MAX_CMD_LEN = 4096;
|
|
char* gausshome = NULL;
|
|
int i = 0;
|
|
char ak[AK_LEN] = {'\0'};
|
|
char sk[SK_LEN] = {'\0'};
|
|
GS_UCHAR* decodedkey = NULL;
|
|
GS_UINT32 decodedlen = 0;
|
|
|
|
CheckKmsUrlRegin();
|
|
|
|
if (!getAkSkForTransEncrypt(ak, sizeof(ak), sk, sizeof(sk))) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
|
|
errmsg("Failed to get ak/sk for transparent encryption.\n")));
|
|
}
|
|
|
|
cmd = (char*)palloc0(MAX_CMD_LEN);
|
|
url = (char*)palloc0(MAX_CMD_LEN);
|
|
region = (char*)palloc0(MAX_CMD_LEN);
|
|
|
|
/* Replace $ with "\$" . */
|
|
for (i = 0, tmp = g_instance.attr.attr_common.transparent_encrypt_kms_url; i < MAX_CMD_LEN && *tmp; i++, tmp++) {
|
|
if (*tmp == '$') {
|
|
url[i++] = '\\';
|
|
}
|
|
url[i] = *tmp;
|
|
}
|
|
|
|
for (i = 0, tmp = g_instance.attr.attr_security.transparent_encrypt_kms_region; i < MAX_CMD_LEN && *tmp;
|
|
i++, tmp++) {
|
|
if (*tmp == '$') {
|
|
region[i++] = '\\';
|
|
}
|
|
region[i] = *tmp;
|
|
}
|
|
|
|
/* If we need to expand the transparent encryption to FI, use security_mode to
|
|
* identify whether it's running on DWS now.
|
|
*/
|
|
gausshome = getGaussHome();
|
|
rc = snprintf_s(cmd,
|
|
MAX_CMD_LEN,
|
|
MAX_CMD_LEN - 1,
|
|
"export JRE_HOME=$GAUSSHOME/jre;export "
|
|
"LD_LIBRARY_PATH=$JRE_HOME/lib/amd64/server:$LD_LIBRARY_PATH;export "
|
|
"PATH=$JRE_HOME/bin:$PATH;java -jar \"%s/bin/getDEK.jar\" \"%s\" \"%s\" \"%s\" \"%s\"",
|
|
gausshome,
|
|
url,
|
|
region,
|
|
ak,
|
|
sk);
|
|
securec_check_ss(rc, "", "");
|
|
|
|
/* This command is for logging, masked AK/SK to avoid password leak risk. */
|
|
cmd_log = (char*)palloc(MAX_CMD_LEN);
|
|
rc = snprintf_s(cmd_log,
|
|
MAX_CMD_LEN,
|
|
MAX_CMD_LEN - 1,
|
|
"export JRE_HOME=$GAUSSHOME/jre;export "
|
|
"LD_LIBRARY_PATH=$JRE_HOME/lib/amd64/server:$LD_LIBRARY_PATH;export "
|
|
"PATH=$JRE_HOME/bin:$PATH;java -jar \"%s/bin/getDEK.jar\" \"%s\" \"%s\" \"ak\" \"sk\"",
|
|
gausshome,
|
|
url,
|
|
region);
|
|
securec_check_ss(rc, "", "");
|
|
|
|
pfree_ext(region);
|
|
pfree_ext(url);
|
|
pfree_ext(gausshome);
|
|
fp = popen(cmd, "r");
|
|
pfree_ext(cmd);
|
|
|
|
get_result = (char*)palloc0(MAX_CMD_LEN + 1);
|
|
|
|
if (NULL == fp || fgets(get_result, MAX_CMD_LEN, fp) == NULL) {
|
|
if (fp != NULL) {
|
|
pclose(fp);
|
|
}
|
|
|
|
pfree_ext(get_result);
|
|
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
|
|
errmsg("Failed to fork subprocess to get DEK for transparent encryption. Failure command is: [%s]\n",
|
|
cmd_log)));
|
|
}
|
|
|
|
pclose(fp);
|
|
|
|
i = (int)strlen(get_result);
|
|
|
|
/* If any error occurs, getDEK.jar will give a message starts with "ERROR:". */
|
|
if (0 == i || strncmp(get_result, "ERROR:", 6) == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
|
|
errmsg("Failed to get DEK for transparent encryption. Failure command is [%s], error message is [%s]\n",
|
|
cmd_log,
|
|
((i == 0) ? "<NULL>" : get_result))));
|
|
}
|
|
|
|
if (get_result[i - 1] == '\n') {
|
|
get_result[i - 1] = '\0';
|
|
}
|
|
|
|
decodedkey = (GS_UCHAR*)SEC_decodeBase64(get_result, &decodedlen);
|
|
|
|
/* Here we failed to decode DEK, so get_result must be error message. */
|
|
if (NULL == decodedkey || decodedlen != DEK_LEN) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
|
|
errmsg("Failed to get decode DEK for transparent encryption. Failure content is [%s].\n", get_result)));
|
|
} else {
|
|
rc = memcpy_s(dek, DEK_LEN, decodedkey, decodedlen);
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
pfree_ext(cmd_log);
|
|
pfree_ext(get_result);
|
|
OPENSSL_free(decodedkey);
|
|
decodedkey = NULL;
|
|
}
|
|
}
|
|
|
|
/* Get iv from transparent encrypted string */
|
|
static void getIV(GS_UCHAR* encrypted_string, GS_UINT32 decodelen)
|
|
{
|
|
GS_UINT32 cipherpartlen = decodelen - RANDOM_LEN;
|
|
int ret = memcpy_s(trans_encrypt_iv, TDE_IV_LEN, encrypted_string + cipherpartlen, RANDOM_LEN);
|
|
securec_check(ret,"\0","\0");
|
|
ereport(LOG, (errmsg("Successfully get IV from transparent encrypted string.\n")));
|
|
}
|
|
|
|
/* check if it's the correct database encryption key. */
|
|
static void testDEK(GS_UCHAR* dek)
|
|
{
|
|
GS_UCHAR* encrypted_sample_string = NULL;
|
|
GS_UINT32 decodelen = 0;
|
|
GS_UINT32 plainlen = 0;
|
|
GS_UCHAR* plaintext = NULL;
|
|
|
|
/* The guc transparent_encrypted_string is a string in base64 mode. */
|
|
encrypted_sample_string =
|
|
(GS_UCHAR*)SEC_decodeBase64((char *)g_instance.attr.attr_security.transparent_encrypted_string, &decodelen);
|
|
|
|
if (encrypted_sample_string == NULL || decodelen < RANDOM_LEN) {
|
|
if (encrypted_sample_string != NULL) {
|
|
OPENSSL_free(encrypted_sample_string);
|
|
encrypted_sample_string = NULL;
|
|
}
|
|
ereport(PANIC,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Decode transparent_encrypted_string failed, please check it.\n")));
|
|
}
|
|
getIV(encrypted_sample_string, decodelen);
|
|
|
|
plaintext = (GS_UCHAR*)palloc0(decodelen);
|
|
|
|
if (!gs_decrypt_aes_128(encrypted_sample_string, decodelen, dek, DEK_LEN, plaintext, &plainlen)) {
|
|
OPENSSL_free(encrypted_sample_string);
|
|
encrypted_sample_string = NULL;
|
|
pfree_ext(plaintext);
|
|
ereport(PANIC,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Decrypt transparent_encrypted_string failed, please check it!\n")));
|
|
}
|
|
|
|
if (encrypted_sample_string != NULL) {
|
|
OPENSSL_free(encrypted_sample_string);
|
|
encrypted_sample_string = NULL;
|
|
}
|
|
if ((size_t)plainlen != strlen(DEK_SAMPLE_STRING) ||
|
|
strncmp((const char*)plaintext, DEK_SAMPLE_STRING, strlen(DEK_SAMPLE_STRING)) != 0) {
|
|
pfree_ext(plaintext);
|
|
ereport(PANIC,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Decrypt transparent_encrypted_string failed, please check it!\n")));
|
|
}
|
|
|
|
pfree_ext(plaintext);
|
|
}
|
|
|
|
bool getAndCheckTranEncryptDEK()
|
|
{
|
|
GS_UCHAR dek[DEK_LEN] = {'\0'};
|
|
int ret = 0;
|
|
|
|
/* Transparent encryption disabled. */
|
|
if (!isEncryptedCluster()) {
|
|
ereport(LOG, (errmsg("Transparent encryption disabled.\n")));
|
|
return true;
|
|
}
|
|
|
|
ereport(LOG, (errmsg("Transparent encryption enabled.\n")));
|
|
|
|
if (trans_encrypt_iv == NULL) {
|
|
trans_encrypt_iv = (GS_UCHAR*)palloc0(TDE_IV_LEN);
|
|
if (trans_encrypt_iv == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Out of memory while getting transparent encryption iv.\n")));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (isSecurityMode) {
|
|
getTransEncryptDEK(dek);
|
|
testDEK((GS_UCHAR*)dek);
|
|
|
|
} else {
|
|
init_the_kerberos_env();
|
|
char* gausshome = getGaussHome();
|
|
if (gausshome == NULL) {
|
|
ereport(WARNING, (errmsg("Transparent encryption Getting environment variable $GAUSSHOME error.\n")));
|
|
(void)fprintf(stderr, _("%s:Getting environment variable $GAUSSHOME error, please check.\n"), pgname);
|
|
return false;
|
|
}
|
|
|
|
KeyManager table_a_key("1", "1"); // next version for different database or table.
|
|
table_a_key.setfile(gausshome);
|
|
pfree_ext(gausshome);
|
|
|
|
if (!table_a_key.init_Key()) {
|
|
ereport(WARNING, (errmsg("Transparent encryption init key failed.\n")));
|
|
return false;
|
|
}
|
|
|
|
std::string dek_b64 = table_a_key.getDEK();
|
|
std::string tde_sample_str = table_a_key.getEncryptedSampleStr();
|
|
if (tde_sample_str.empty()) {
|
|
ereport(WARNING, (errmsg("Transparent encryption tde_sample_str error, please check.\n")));
|
|
(void)fprintf(stderr, _("%s:tde_sample_str error, please check.\n"), pgname);
|
|
return false;
|
|
}
|
|
|
|
if (dek_b64.length() < DEK_LEN) {
|
|
ereport(WARNING, (errmsg("Transparent encryption get key failed.\n")));
|
|
return false;
|
|
}
|
|
|
|
GS_UINT32 dek_origian_len = 0;
|
|
char* dek_origian = SEC_decodeBase64((char*)dek_b64.c_str(), &dek_origian_len);
|
|
if (dek_origian == NULL) {
|
|
ereport(WARNING, (errmsg("decode Base64 failed.\n")));
|
|
return false;
|
|
}
|
|
ret = memcpy_s(dek, DEK_LEN, dek_origian, dek_origian_len);
|
|
securec_check(ret, "\0", "\0");
|
|
OPENSSL_free(dek_origian);
|
|
dek_origian = NULL;
|
|
|
|
if (!table_a_key.check_key((unsigned char*)dek)) {
|
|
ereport(WARNING, (errmsg("Transparent encryption get key failed.\n")));
|
|
return false;
|
|
}
|
|
|
|
table_a_key.getIV(trans_encrypt_iv);
|
|
g_tde_algo = table_a_key.get_tde_algo();
|
|
}
|
|
|
|
/* init dek and iv global values */
|
|
trans_encrypt_dek = (GS_UCHAR*)palloc0(DEK_LEN);
|
|
ret = memcpy_s(trans_encrypt_dek, DEK_LEN, dek, DEK_LEN);
|
|
securec_check(ret, "\0", "\0");
|
|
|
|
ereport(LOG, (errmsg("Succeeded to check DEK for transparent encryption.\n")));
|
|
return true;
|
|
}
|
|
|
|
bool isEncryptedCluster()
|
|
{
|
|
if (isSecurityMode && (g_instance.attr.attr_security.transparent_encrypted_string == NULL ||
|
|
*g_instance.attr.attr_security.transparent_encrypted_string == '\0')) {
|
|
ereport(DEBUG5, (errmsg("Transparent encryption disabled in DWS.\n")));
|
|
return false;
|
|
|
|
} else if ((!isSecurityMode) && (g_instance.attr.attr_common.transparent_encrypt_kms_url == NULL ||
|
|
*g_instance.attr.attr_common.transparent_encrypt_kms_url == '\0')) {
|
|
ereport(DEBUG5, (errmsg("Transparent encryption disabled in GaussDB.\n")));
|
|
return false;
|
|
}
|
|
|
|
if (is_feature_disabled(TRANSPARENT_ENCRYPTION) == true) {
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("TDE is not supported.")));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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_KEY_LENGTH] = {0};
|
|
|
|
errno_t errorno = EOK;
|
|
GS_UINT32 ret = 0;
|
|
size_t cipherLength = 0;
|
|
|
|
plaintext = (char*)(text_to_cstring(PG_GETARG_TEXT_P(0)));
|
|
plaintextlen = strlen((const char*)plaintext);
|
|
|
|
key = (text_to_cstring(PG_GETARG_TEXT_P(1)));
|
|
keylen = strlen((const char*)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, SM4_KEY_LENGTH)));
|
|
}
|
|
errorno = memcpy_s(user_key, SM4_KEY_LENGTH, (GS_UCHAR*)key, keylen);
|
|
securec_check_c(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_c(errorno, "\0", "\0");
|
|
}
|
|
|
|
errorno = memset_s(key, keylen, '\0', keylen);
|
|
securec_check(errorno, "\0", "\0");
|
|
pfree_ext(key);
|
|
|
|
/*
|
|
* Calculate the max length of ciphertext:
|
|
*/
|
|
|
|
ciphertextlenmax = plaintextlen + SM4_BLOCK_SIZE - 1;
|
|
ciphertext = (char*)palloc(ciphertextlenmax);
|
|
errorno = memset_s(ciphertext, ciphertextlenmax, '\0', ciphertextlenmax);
|
|
securec_check(errorno, "\0", "\0");
|
|
|
|
ret = sm4_ctr_enc_partial_mode(
|
|
plaintext, plaintextlen, ciphertext, &cipherLength, user_key, useriv);
|
|
if (ret != 0) {
|
|
ereport(ERROR,
|
|
(errmsg("sm4 encrypt fail"),
|
|
errdetail("sm4_ctr_enc_partial_mode fail")));
|
|
}
|
|
|
|
errorno = memset_s(plaintext, plaintextlen, '\0', plaintextlen);
|
|
securec_check(errorno, "\0", "\0");
|
|
pfree_ext(plaintext);
|
|
|
|
/* encode the ciphertext for nice show and decrypt operation */
|
|
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;
|
|
}
|
|
|
|
bool gs_decrypt_sm4_function(FunctionCallInfo fcinfo, text** outtext)
|
|
{
|
|
char* key = NULL;
|
|
GS_UINT32 keylen = 0;
|
|
char* ciphertext = NULL;
|
|
GS_UINT32 ciphertextlen = 0;
|
|
char* encodetext = NULL;
|
|
GS_UCHAR* decodetext;
|
|
GS_UINT32 ret = 0;
|
|
|
|
GS_UINT32 decodetextlen = 0;
|
|
GS_UINT32 ciphertextlenmax = 0;
|
|
GS_UCHAR userkey[SM4_KEY_LENGTH];
|
|
GS_UCHAR useriv[SM4_KEY_LENGTH] = {0};
|
|
|
|
errno_t errorno = EOK;
|
|
size_t encodetextlen = 0;
|
|
|
|
decodetext = (GS_UCHAR*)(text_to_cstring(PG_GETARG_TEXT_P(0)));
|
|
|
|
decodetextlen = strlen((const char*)decodetext);
|
|
|
|
key = (char*)(text_to_cstring(PG_GETARG_TEXT_P(1)));
|
|
keylen = strlen((const char*)key);
|
|
|
|
if (!check_input_password(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)));
|
|
}
|
|
|
|
ciphertext = (char*)(SEC_decodeBase64((const char*)decodetext , &ciphertextlen));
|
|
if ((ciphertext == NULL)) {
|
|
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 wrong!")));
|
|
}
|
|
|
|
errorno = memset_s(decodetext, decodetextlen, '\0', decodetextlen);
|
|
securec_check(errorno, "\0", "\0");
|
|
pfree_ext(decodetext);
|
|
|
|
|
|
errorno = memcpy_s(userkey, SM4_KEY_LENGTH, (GS_UCHAR*)key, keylen);
|
|
securec_check_c(errorno, "\0", "\0");
|
|
|
|
|
|
if (keylen < SM4_KEY_LENGTH) {
|
|
errorno = memset_s(userkey + keylen, SM4_KEY_LENGTH - keylen, '\0', SM4_KEY_LENGTH - keylen);
|
|
securec_check_c(errorno, "\0", "\0");
|
|
}
|
|
|
|
errorno = memset_s(key, keylen, '\0', keylen);
|
|
securec_check(errorno, "\0", "\0");
|
|
pfree_ext(key);
|
|
|
|
ciphertextlenmax = ciphertextlen + SM4_BLOCK_SIZE - 1;
|
|
encodetext = (char*)palloc(ciphertextlenmax);
|
|
errorno = memset_s(encodetext, ciphertextlenmax, '\0', ciphertextlenmax);
|
|
securec_check(errorno, "\0", "\0");
|
|
|
|
ret = sm4_ctr_dec_partial_mode(
|
|
ciphertext, ciphertextlen, encodetext, &encodetextlen, userkey, useriv);
|
|
if (ret != 0) {
|
|
ereport(ERROR,
|
|
(errmsg("sm4 decrypt fail"),
|
|
errdetail("sm4_ctr_dec_partial_mode fail")));
|
|
}
|
|
|
|
errorno = memset_s(ciphertext, ciphertextlen, '\0', ciphertextlen);
|
|
securec_check(errorno, "\0", "\0");
|
|
OPENSSL_free(ciphertext);
|
|
ciphertext = NULL;
|
|
|
|
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;
|
|
text* outtext = NULL;
|
|
bool status = false;
|
|
|
|
/* check input paramaters */
|
|
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;
|
|
text* outtext = NULL;
|
|
bool status = false;
|
|
/* check input paramaters */
|
|
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;
|
|
/* check input paramaters */
|
|
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;
|
|
/* check input paramaters */
|
|
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);
|
|
}
|
|
|
|
|