File crypt_opfunc.c¶
File List > cubrid > src > query > crypt_opfunc.c
Go to the documentation of this file
/*
* Copyright 2008 Search Solution Corporation
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* Crypt_opfunc.c
*/
#ident "$Id$"
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory>
#include <memory.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <errno.h>
#if defined (WINDOWS)
#include <winsock2.h>
#else
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#endif
#include "CRC.h"
#include "thread_compat.hpp"
#include "porting.h"
#include "error_code.h"
#include "error_manager.h"
#include "memory_alloc.h"
#include "crypt_opfunc.h"
#if defined (SERVER_MODE)
#include "thread_manager.hpp" // for thread_get_thread_entry_info
#endif // SERVER_MODE
#include "base64.h"
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
#define AES128_BLOCK_LEN (128/8)
#define AES128_KEY_LEN (128/8)
#define DES_BLOCK_LEN (8)
#define MD5_CHECKSUM_LEN 16
#define MD5_CHECKSUM_HEX_LEN (32 + 1)
typedef enum
{
CRYPT_LIB_INIT_ERR = 0,
CRYPT_LIB_OPEN_CIPHER_ERR,
CRYPT_LIB_SET_KEY_ERR,
CRYPT_LIB_CRYPT_ERR,
CRYPT_LIB_UNKNOWN_ERR
} CRYPT_LIB_ERROR;
typedef enum
{
SHA_ONE,
SHA_TWO_224,
SHA_TWO_256,
SHA_TWO_384,
SHA_TWO_512,
} SHA_FUNCTION;
// *INDENT-OFF*
template<typename T>
using deleted_unique_ptr = std::unique_ptr<T, std::function<void (T *)>>;
// *INDENT-ON*
static const char *const crypt_lib_fail_info[] = {
"Initialization failure!",
"Open cipher failure!",
"Set secret key failure!",
"Encrypt/decrypt failure!",
"Unknown error!"
};
static const char lower_hextable[] = "0123456789abcdef";
static const char upper_hextable[] = "0123456789ABCDEF";
static int crypt_sha_functions (THREAD_ENTRY * thread_p, const char *src, int src_len, SHA_FUNCTION sha_func,
char **dest_p, int *dest_len_p);
static int crypt_md5_buffer_binary (const char *buffer, size_t len, char *resblock);
static void aes_default_gen_key (const char *key, int key_len, char *dest_key, int dest_key_len);
void
str_to_hex_prealloced (const char *src, int src_len, char *dest, int dest_len, HEX_LETTERCASE lettercase)
{
int i = src_len;
unsigned char item_num = 0;
const char *hextable;
assert (src != NULL && dest != NULL);
assert (dest_len >= (src_len * 2 + 1));
if (lettercase == HEX_UPPERCASE)
{
hextable = upper_hextable;
}
else
{
hextable = lower_hextable;
}
while (i > 0)
{
--i;
item_num = (unsigned char) src[i];
dest[2 * i] = hextable[item_num / 16];
dest[2 * i + 1] = hextable[item_num % 16];
}
dest[src_len * 2] = '\0';
}
/*
* str_to_hex() - convert a string to its hexadecimal expreesion string
* return:
* thread_p(in):
* src(in):
* src_len(in):
* dest_p(out):
* dest_len_p(out):
* Note:
*/
char *
str_to_hex (THREAD_ENTRY * thread_p, const char *src, int src_len, char **dest_p, int *dest_len_p,
HEX_LETTERCASE lettercase)
{
int dest_len = 2 * src_len + 1;
int i = 0;
unsigned char item_num = 0;
char *dest;
assert (src != NULL);
#if defined (SERVER_MODE)
if (thread_p == NULL)
{
thread_p = thread_get_thread_entry_info ();
}
#endif // SERVER_MODE
dest = (char *) db_private_alloc (thread_p, dest_len * sizeof (char));
if (dest == NULL)
{
return NULL;
}
str_to_hex_prealloced (src, src_len, dest, dest_len, lettercase);
*dest_p = dest;
*dest_len_p = dest_len - 1;
return dest;
}
/*
* aes_default_gen_key() - if aes's key is not equal to 128, the function generate the 128 length key. like mysql.
* return:
* key(in):
* key_len(in):
* dest_key(out):
* dest_key_len(in):
* Note:
*/
static void
aes_default_gen_key (const char *key, int key_len, char *dest_key, int dest_key_len)
{
int i, j;
assert (key != NULL);
assert (dest_key != NULL);
memset (dest_key, 0, dest_key_len);
for (i = 0, j = 0; j < key_len; ++i, ++j)
{
if (i == dest_key_len)
{
i = 0;
}
dest_key[i] = ((unsigned char) dest_key[i]) ^ ((unsigned char) key[j]);
}
}
/*
* crypt_default_encrypt() - like mysql's aes_encrypt. Use (AES-128/DES)/ECB/PKCS7 method.
* return:
* thread_p(in):
* src(in): source string
* src_len(in): the length of source string
* key(in): the encrypt key
* key_len(in): the length of the key
* dest_p(out): the encrypted data. The pointer has to be free by db_private_free
* dest_len_p(out):
* Note:
*/
int
crypt_default_encrypt (THREAD_ENTRY * thread_p, const char *src, int src_len, const char *key, int key_len,
char **dest_p, int *dest_len_p, CIPHER_ENCRYPTION_TYPE enc_type)
{
int pad;
int padding_src_len;
int ciphertext_len = 0;
char *padding_src = NULL;
char *dest = NULL;
int error_status = NO_ERROR;
assert (src != NULL);
assert (key != NULL);
const EVP_CIPHER *cipher;
int block_len;
char new_key[AES128_KEY_LEN + 1];
const char *key_arg = NULL;
switch (enc_type)
{
case AES_128_ECB:
cipher = EVP_aes_128_ecb ();
block_len = AES128_BLOCK_LEN;
aes_default_gen_key (key, key_len, new_key, AES128_KEY_LEN);
new_key[AES128_KEY_LEN] = '\0';
key_arg = new_key;
break;
case DES_ECB:
cipher = EVP_des_ecb ();
block_len = DES_BLOCK_LEN;
key_arg = key;
break;
default:
return ER_FAILED;
}
#if defined (SERVER_MODE)
if (thread_p == NULL)
{
thread_p = thread_get_thread_entry_info ();
}
#endif // SERVER_MODE
*dest_p = NULL;
*dest_len_p = 0;
// *INDENT-OFF*
deleted_unique_ptr<EVP_CIPHER_CTX> context (EVP_CIPHER_CTX_new (), [] (EVP_CIPHER_CTX *ctxt_ptr)
{
if (ctxt_ptr != NULL)
{
EVP_CIPHER_CTX_free (ctxt_ptr);
}
});
// *INDENT-ON*
if (context == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_INIT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
if (EVP_EncryptInit (context.get (), cipher, (const unsigned char *) key_arg, NULL) != 1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_INIT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
/* PKCS7 */
if ((src_len % block_len) == 0)
{
pad = block_len;
padding_src_len = src_len + pad;
}
else
{
padding_src_len = (int) ceil ((double) src_len / block_len) * block_len;
pad = padding_src_len - src_len;
}
padding_src = (char *) db_private_alloc (thread_p, padding_src_len);
if (padding_src == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
memcpy (padding_src, src, src_len);
memset (padding_src + src_len, pad, pad);
dest = (char *) db_private_alloc (thread_p, padding_src_len);
if (dest == NULL)
{
db_private_free_and_init (thread_p, padding_src);
return ER_OUT_OF_VIRTUAL_MEMORY;
}
if (EVP_EncryptUpdate (context.get (), (unsigned char *) dest, &ciphertext_len, (const unsigned char *) padding_src,
padding_src_len) != 1)
{
db_private_free_and_init (thread_p, padding_src);
db_private_free_and_init (thread_p, dest);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_CRYPT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
*dest_len_p = ciphertext_len;
*dest_p = dest;
db_private_free_and_init (thread_p, padding_src);
return NO_ERROR;
}
/*
* crypt_default_decrypt() - like mysql's aes_decrypt. Use AES-128/ECB/PKCS7 method.
* return:
* thread_p(in):
* src(in): source string
* src_len(in): the length of source string
* key(in): the encrypt key
* key_len(in): the length of the key
* dest_p(out): the encrypted data. The pointer has to be free by db_private_free
* dest_len_p(out):
* Note:
*/
int
crypt_default_decrypt (THREAD_ENTRY * thread_p, const char *src, int src_len, const char *key, int key_len,
char **dest_p, int *dest_len_p, CIPHER_ENCRYPTION_TYPE enc_type)
{
char *dest = NULL;
int dest_len = 0;
int error_status = NO_ERROR;
int pad, pad_len;
int i;
const EVP_CIPHER *cipher;
int block_len;
char new_key[AES128_KEY_LEN + 1];
const char *key_arg = NULL;
switch (enc_type)
{
case AES_128_ECB:
cipher = EVP_aes_128_ecb ();
block_len = AES128_BLOCK_LEN;
aes_default_gen_key (key, key_len, new_key, AES128_KEY_LEN);
new_key[AES128_KEY_LEN] = '\0';
key_arg = new_key;
break;
case DES_ECB:
cipher = EVP_des_ecb ();
block_len = DES_BLOCK_LEN;
key_arg = key;
break;
default:
return ER_FAILED;
}
#if defined (SERVER_MODE)
if (thread_p == NULL)
{
thread_p = thread_get_thread_entry_info ();
}
#endif // SERVER_MODE
assert (src != NULL);
assert (key != NULL);
*dest_p = NULL;
*dest_len_p = 0;
/* src is not a string encrypted by aes_default_encrypt, return NULL */
if (src_len % block_len)
{
return NO_ERROR;
}
// *INDENT-OFF*
deleted_unique_ptr<EVP_CIPHER_CTX> context (EVP_CIPHER_CTX_new (), [] (EVP_CIPHER_CTX *ctxt_ptr)
{
if (ctxt_ptr != NULL)
{
EVP_CIPHER_CTX_free (ctxt_ptr);
}
});
// *INDENT-ON*
if (context == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_INIT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
if (EVP_DecryptInit (context.get (), cipher, (const unsigned char *) key_arg, NULL) != 1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_INIT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
dest = (char *) db_private_alloc (thread_p, src_len * sizeof (char));
if (dest == NULL)
{
error_status = ER_OUT_OF_VIRTUAL_MEMORY;
return error_status;
}
int len;
if (EVP_DecryptUpdate (context.get (), (unsigned char *) dest, &len, (const unsigned char *) src, src_len) != 1)
{
db_private_free_and_init (thread_p, dest);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_CRYPT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
if (EVP_DecryptFinal (context.get (), (unsigned char *) dest + len, &len) != 1)
{
db_private_free_and_init (thread_p, dest);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_CRYPT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
/* PKCS7 */
if (src_len != 0)
{
pad = dest[src_len - 1];
if (pad > AES128_BLOCK_LEN)
{
/* src is not a string encrypted by aes_default_encrypt, return NULL */
db_private_free_and_init (thread_p, dest);
return ER_FAILED;
}
i = src_len - 2;
pad_len = 1;
while ((i >= 0) && (dest[i] == pad))
{
pad_len++;
i--;
}
if ((pad_len >= pad))
{
pad_len = pad;
dest_len = src_len - pad_len;
}
else
{
/* src is not a string encrypted by aes_default_encrypt, return NULL */
db_private_free_and_init (thread_p, dest);
return ER_FAILED;
}
}
*dest_p = dest;
*dest_len_p = dest_len;
return NO_ERROR;
}
/*
* crypt_sha_one() -
* return:
* thread_p(in):
* src(in):
* src_len(in):
* dest_len_p(out):
* Note:
*/
int
crypt_sha_one (THREAD_ENTRY * thread_p, const char *src, int src_len, char **dest_p, int *dest_len_p)
{
return crypt_sha_functions (thread_p, src, src_len, SHA_ONE, dest_p, dest_len_p);
}
/*
* crypt_sha_two() -
* return:
* thread_p(in):
* src(in):
* src_len(in):
* need_hash_len(in):
* dest_p(out)
* dest_len_p(out):
* Note:
*/
int
crypt_sha_two (THREAD_ENTRY * thread_p, const char *src, int src_len, int need_hash_len, char **dest_p, int *dest_len_p)
{
SHA_FUNCTION sha_func;
switch (need_hash_len)
{
case 0:
case 256:
sha_func = SHA_TWO_256;
break;
case 224:
sha_func = SHA_TWO_224;
break;
case 384:
sha_func = SHA_TWO_384;
break;
case 512:
sha_func = SHA_TWO_512;
break;
default:
return NO_ERROR;
}
return crypt_sha_functions (thread_p, src, src_len, sha_func, dest_p, dest_len_p);
}
static int
crypt_sha_functions (THREAD_ENTRY * thread_p, const char *src, int src_len, SHA_FUNCTION sha_func, char **dest_p,
int *dest_len_p)
{
char *dest_hex = NULL;
int dest_hex_len;
assert (src != NULL);
#if defined (SERVER_MODE)
if (thread_p == NULL)
{
thread_p = thread_get_thread_entry_info ();
}
#endif // SERVER_MODE
*dest_p = NULL;
// *INDENT-OFF*
deleted_unique_ptr<EVP_MD_CTX> context (EVP_MD_CTX_new (), [] (EVP_MD_CTX * ctxt_ptr)
{
if (ctxt_ptr != NULL)
{
EVP_MD_CTX_free (ctxt_ptr);
}
});
// *INDENT-ON*
if (context == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_INIT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
int rc;
switch (sha_func)
{
case SHA_ONE:
rc = EVP_DigestInit (context.get (), EVP_sha1 ());
break;
case SHA_TWO_256:
rc = EVP_DigestInit (context.get (), EVP_sha256 ());
break;
case SHA_TWO_224:
rc = EVP_DigestInit (context.get (), EVP_sha224 ());
break;
case SHA_TWO_384:
rc = EVP_DigestInit (context.get (), EVP_sha384 ());
break;
case SHA_TWO_512:
rc = EVP_DigestInit (context.get (), EVP_sha512 ());
break;
default:
assert (false);
return ER_FAILED;
}
if (rc == 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_INIT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
if (EVP_DigestUpdate (context.get (), src, src_len) == 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_CRYPT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int lengthOfHash = 0;
if (EVP_DigestFinal (context.get (), hash, &lengthOfHash) == 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_CRYPT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
dest_hex = str_to_hex (thread_p, (char *) hash, lengthOfHash, &dest_hex, &dest_hex_len, HEX_UPPERCASE);
if (dest_hex == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
*dest_p = dest_hex;
*dest_len_p = dest_hex_len;
return NO_ERROR;
}
static int
crypt_md5_buffer_binary (const char *buffer, size_t len, char *resblock)
{
if (buffer == NULL || resblock == NULL)
{
assert (false);
return ER_FAILED;
}
// *INDENT-OFF*
deleted_unique_ptr<EVP_MD_CTX> context (EVP_MD_CTX_new (), [] (EVP_MD_CTX *ctxt_ptr)
{
if (ctxt_ptr != NULL)
{
EVP_MD_CTX_free (ctxt_ptr);
}
});
// *INDENT-ON*
if (context == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_INIT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
if (EVP_DigestInit (context.get (), EVP_md5 ()) == 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_INIT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
if (EVP_DigestUpdate (context.get (), buffer, len) == 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_CRYPT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
if (EVP_DigestFinal (context.get (), (unsigned char *) resblock, NULL) == 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_CRYPT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
return NO_ERROR;
}
int
crypt_md5_buffer_hex (const char *buffer, size_t len, char *resblock)
{
int ec = NO_ERROR;
if (buffer == NULL || resblock == NULL)
{
assert (false);
return ER_FAILED;
}
ec = crypt_md5_buffer_binary (buffer, len, resblock);
if (ec != NO_ERROR)
{
return ec;
}
str_to_hex_prealloced (resblock, MD5_CHECKSUM_LEN, resblock, MD5_CHECKSUM_HEX_LEN, HEX_LOWERCASE);
return NO_ERROR;
}
/*
* crypt_crc32() -
* return:
* src(in): original message
* src_len(in): length of original message
* dest(out): crc32 result
* Note:
*/
void
crypt_crc32 (const char *src, int src_len, int *dest)
{
assert (src != NULL && dest != NULL);
// *INDENT-OFF*
*dest = CRC::Calculate (src, src_len, CRC::CRC_32 ());
// *INDENT-ON*
}
/*
* crypt_generate_random_bytes() - Generate random number bytes
* return: error code or NO_ERROR
* dest(out): the generated bytes
* length(in): the length of bytes to generate
* Note:
*/
int
crypt_generate_random_bytes (char *dest, int length)
{
assert (dest != NULL && length > 0);
if (RAND_bytes ((unsigned char *) dest, length) != 1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_CRYPT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
return NO_ERROR;
}
static const unsigned char dblink_cipher_nonce[16] = {
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07
};
static int
init_dblink_cipher (EVP_CIPHER_CTX ** ctx, const EVP_CIPHER ** cipher_type, bool is_aes_algorithm)
{
if ((*ctx = EVP_CIPHER_CTX_new ()) == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_INIT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
if (is_aes_algorithm)
{
*cipher_type = EVP_aes_256_ctr ();
}
else
{
*cipher_type = EVP_aria_256_ctr ();
}
if (*cipher_type == NULL)
{
EVP_CIPHER_CTX_free (*ctx);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_INIT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
return NO_ERROR;
}
/*
* crypt_dblink_encrypt () : Converts binary encrypted by AES/ARIA to readable string data.
*
* return : NO_ERROR or error code.
* str(in) : password string data(In fact, the binary data).
* str_len(in) : length of "src"
* cipher_buffer(out) : Encrypted binary data (by AES/ARIA)
* pk(in) : The private key(master key) used to create the encrypted binary
*
* Remark:
* If pk is an empty string, the internal master key is used and the AES algorithm is used.
* Otherwise, it is selected between AES and ARIA algorithms according to the value of a specific location.
*/
int
crypt_dblink_encrypt (const unsigned char *str, int str_len, unsigned char *cipher_buffer, unsigned char *key)
{
int len, cipher_len, err;
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher_type;
assert (key && *key);
assert ((int) strlen ((char *) key) < DBLINK_CRYPT_KEY_LENGTH);
err = init_dblink_cipher (&ctx, &cipher_type, (key[13] & 0x01) == 0x00);
if (err != NO_ERROR)
{
return err;
}
if (EVP_EncryptInit_ex (ctx, cipher_type, NULL, key, dblink_cipher_nonce) != 1)
{
goto cleanup;
}
if (EVP_EncryptUpdate (ctx, cipher_buffer, &len, str, str_len) != 1)
{
goto cleanup;
}
cipher_len = len;
// Further ciphertext bytes may be written at finalizing (Partial block).
if (EVP_EncryptFinal_ex (ctx, cipher_buffer + len, &len) != 1)
{
goto cleanup;
}
cipher_len += len;
// CTR_MODE is stream mode so that there is no need to check,
// but check it for safe.
assert (cipher_len == str_len);
cleanup:
EVP_CIPHER_CTX_free (ctx);
if (err != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_CRYPT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
return err;
}
/*
* crypt_dblink_decrypt () : Decrypt the encrypted binary to extract the password string
*
* return : NO_ERROR or error code.
* cipher(in) : Encrypted password data(binary).
* cipher_len(in) : length of "cipher"
* str_buffer(out) : Decrypted string data(In fact, the binary data).
* pk(in) : Private key (master key) used for decryption
*
* Remark:
*
*/
int
crypt_dblink_decrypt (const unsigned char *cipher, int cipher_len, unsigned char *str_buffer, unsigned char *key)
{
int len, str_len, err;
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher_type;
assert (key && *key);
assert ((int) strlen ((char *) key) < DBLINK_CRYPT_KEY_LENGTH);
err = init_dblink_cipher (&ctx, &cipher_type, (key[13] & 0x01) == 0x00);
if (err != NO_ERROR)
{
return err;
}
if (EVP_DecryptInit_ex (ctx, cipher_type, NULL, key, dblink_cipher_nonce) != 1)
{
goto cleanup;
}
if (EVP_DecryptUpdate (ctx, str_buffer, &len, cipher, cipher_len) != 1)
{
goto cleanup;
}
str_len = len;
// Further plaintext bytes may be written at finalizing (Partial block).
if (EVP_DecryptFinal_ex (ctx, str_buffer + len, &len) != 1)
{
goto cleanup;
}
str_len += len;
// CTR_MODE is stream mode so that there is no need to check,
// but check it for safe.
assert (str_len == cipher_len);
cleanup:
EVP_CIPHER_CTX_free (ctx);
if (err != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ENCRYPTION_LIB_FAILED, 1, crypt_lib_fail_info[CRYPT_LIB_CRYPT_ERR]);
return ER_ENCRYPTION_LIB_FAILED;
}
return err;
}
// *INDENT-OFF*
#define BYTE_2_HEX(u, b) ( \
(b)[0] = upper_hextable[((unsigned char)(u)) >> 4], \
(b)[1] = upper_hextable[((unsigned char)(u)) & 0x0F] \
)
#define HEX_2_BYTE(h, u) ( \
(u) = (((h)[0] <= '9') ? ((h)[0] - '0') << 4 : ((h)[0] - 'A' + 10) << 4), \
(u) |= (((h)[1] <= '9') ? ((h)[1] - '0') : ((h)[1] - 'A' + 10)) \
)
// *INDENT-ON*
/*
* crypt_dblink_bin_to_str () : Converts binary encrypted by AES/ARIA to readable string data.
*
* return : NO_ERROR or error code.
* src(in) : Encrypted binary data.
* src_len(in) : length of "src"
* dest(out) : Buffer to contain the final processed encrypted string
* dest_len(in) : Size of "dest" buffer.
* pk(in) : The private key(master key) used to create the encrypted binary
* tm(in) : The value to be used to reorder the encrypted data
*
* Remark:
* The length of binary data encrypted with AES/ARIA is variable.
* It includes additional information to make it a specific path encrypted string.
*
*/
int
crypt_dblink_bin_to_str (const char *src, int src_len, char *dest, int dest_len, unsigned char *pk, long tm)
{
int err, i, pk_len, idx, even, odd;
const char *hextable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
int hextable_mod = 62; /* strlen (hextable) */
unsigned char *enc_ptr = NULL;
int enc_len = 0;
unsigned char empty_str[4] = { 0x00, };
assert (src != NULL && dest != NULL);
err = base64_encode ((unsigned char *) src, src_len, &enc_ptr, &enc_len);
if (err != NO_ERROR)
{
db_private_free (NULL, enc_ptr);
return err;
}
// remove '\n' character
char *p, *t;
p = (char *) enc_ptr;
while (*p && (*p != '\n'))
{
p++;
}
for (t = p; *p; p++)
{
if (*p == '\n')
{
enc_len--;
continue;
}
*t = *p;
t++;
}
*t = 0x00;
if (!pk)
{
pk = empty_str;
}
assert (dest_len >= (enc_len + 4));
odd = (tm >> 3) % 15;
even = tm % 15;
for (i = 0; pk[i]; i++)
{
pk[i] += ('a' - '0' + odd);
if (pk[i + 1] == '\0')
{
break;
}
i++;
pk[i] += ('a' - '0' + even);
}
idx = 0;
BYTE_2_HEX (enc_len, dest + idx);
idx += 2;
BYTE_2_HEX ((odd << 4) | even, dest + idx);
idx += 2;
pk_len = (int) strlen ((char *) pk);
BYTE_2_HEX (pk_len, dest + idx);
idx += 2;
memcpy (dest + idx, pk, pk_len);
pk_len += idx;
memcpy (dest + pk_len, enc_ptr, enc_len);
db_private_free (NULL, enc_ptr);
/* Adjust the length so that it is a multiple of 4. */
dest_len >>= 2;
dest_len <<= 2;
dest_len -= 2;
even = 0;
for (i = enc_len + pk_len; i < dest_len; i++)
{
dest[i] = hextable[rand () % hextable_mod];
even += (dest[i] + i);
}
dest[i++] = ((even >> 8) % 26) + 'A';
dest[i++] = (even % 26) + 'a';
dest[i] = '\0';
return NO_ERROR;
}
/*
* crypt_dblink_str_to_bin () : Extract the original encrypted binary
* from the string created via crypt_dblink_bin_to_str().
*
* return : NO_ERROR or error code.
* src(in) : String created via crypt_dblink_bin_to_str().
* src_len(in) : length of "src"
* dest(out) : Buffer to contain the original encrypted binary.
* dest_len(in) : Size of "dest" buffer.
* pk(out) : The private key(master key) used to create the encrypted binary
*
* Remark:
*
*
*/
int
crypt_dblink_str_to_bin (const char *src, int src_len, char *dest, int *dest_len, unsigned char *pk)
{
int i, err, pk_len, idx, odd, even;
unsigned char *dec_ptr = NULL;
char *src_bk = (char *) src;
assert (src && dest && pk && dest_len);
assert (src_len >= 6);
idx = 0;
HEX_2_BYTE (src + idx, pk_len);
idx += 2;
if (pk_len >= src_len)
{
return ER_FAILED;
}
src_len = pk_len;
HEX_2_BYTE (src + idx, even);
idx += 2;
odd = even >> 4;
even &= 0x0F;
HEX_2_BYTE (src + idx, pk_len);
idx += 2;
src += idx;
memcpy (pk, src, pk_len);
pk[pk_len] = '\0';
for (i = 0; pk[i]; i++)
{
pk[i] -= ('a' - '0' + odd);
if (pk[i + 1] == '\0')
{
break;
}
i++;
pk[i] -= ('a' - '0' + even);
}
src += pk_len;
err = base64_decode ((unsigned char *) src, src_len, &dec_ptr, dest_len);
if (err == NO_ERROR)
{
memcpy (dest, dec_ptr, *dest_len);
err = NO_ERROR;
}
db_private_free (NULL, dec_ptr);
if (err == NO_ERROR)
{
// check validation
even = 0;
src += src_len;
for (i = src - src_bk; src_bk[i + 2]; i++)
{
even += (src_bk[i] + i);
}
if ((src_bk[i] != (((even >> 8) % 26) + 'A')) || (src_bk[i + 1] != ((even % 26) + 'a')))
{
err = ER_FAILED;
}
}
return err;
}
/*
* shake_dblink_password () : Transforms the input password to create a new one.
*
* return : length of newly created password.
* passwd(in) : Raw password.
* confused(out) : Newly created password.
* confused_size(in) : size of confused buffer.
* chk_time(out) : Time of creation of new password
*
* Remark:
* Even if the same raw password is entered, a different password is always generated.
* This is to make it difficult to guess the password from the outside.
* Newly created password is binary.
* <header part><garbage part><data part><checksum part>
*/
int
shake_dblink_password (const char *passwd, char *confused, int confused_size, struct timeval *chk_time)
{
const char *tmpx = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%%^&*()_-+=;:[]{}(),./?<>";
int i, pwdlen, start;
int shift, safe;
unsigned char *p;
unsigned char checksum;
int tmpx_len = 90; /* strlen(tmpx) */
p = (unsigned char *) confused;
pwdlen = (int) strlen (passwd);
gettimeofday (chk_time, NULL);
shift = (chk_time->tv_usec & 0x7FFF);
memcpy (p, &shift, sizeof (int));
p += sizeof (int);
*p = (unsigned char) pwdlen; // pwdlen is less than or equal to DBLINK_PASSWORD_MAX_LENGTH.
p++;
safe = pwdlen + 11; /* 11? int + char + char + some safe */
start = 0;
if (confused_size > safe)
{
if ((start = (confused_size - safe) / 2) > 2)
{
start = (chk_time->tv_usec >> 3) % start;
}
}
*p = (unsigned char) start;
p++;
for (i = 0; i < start; i++)
{
p[i] = tmpx[rand () % tmpx_len];
}
p += start;
if (pwdlen > 0)
{
shift %= pwdlen;
if (shift == 0)
{
shift++;
}
// conceptual example) password --> ordpassw
for (i = 0; i < pwdlen; i++)
{
p[((i + shift) < pwdlen) ? (i + shift) : ((i + shift) - pwdlen)] = passwd[i];
}
// conceptual example) ordpassw --> pseqbttx
shift = ((chk_time->tv_usec & 0x7A5A) % 127) + 1;
for (i = 0; i < pwdlen; i++)
{
p[i] = ((p[i] + shift) < 128) ? (p[i] + shift) : (p[i] + shift - 128);
}
p += pwdlen;
}
pwdlen = p - ((unsigned char *) confused);
checksum = 0x7C;
p = (unsigned char *) confused;
for (i = 0; i < pwdlen; i++)
{
checksum += p[i];
}
p[pwdlen++] = checksum;
/* Adjust the length so that it is a multiple of 3.
* This is to prevent the '=' character from appearing in the base64 conversion result. */
while (pwdlen % 3)
{
p[pwdlen++] = checksum;
}
p[pwdlen] = '\0';
return pwdlen;
}
/*
* reverse_shake_dblink_password () : Extract the raw password from the re-created password.
*
* return : NO_ERROR or error code.
* confused(in) : Password created via shake_dblink_password(). This is binary.
* length(in) : lelgth of "confused"
* passwd(out) : Raw password.
*
* Remark:
*
*/
int
reverse_shake_dblink_password (char *confused, int length, char *passwd)
{
int i, pwdlen;
int shift;
unsigned char *p;
unsigned char checksum;
p = (unsigned char *) confused;
memcpy (&shift, p, sizeof (int));
p += sizeof (int);
pwdlen = *p;
p++;
p += (*p + 1);
if (length < pwdlen)
{
return ER_FAILED;
}
if (pwdlen > 0)
{
shift %= pwdlen;
if (shift == 0)
{
shift++;
}
// conceptual example) pseqbttx --> ordpassw
for (i = 0; i < pwdlen; i++)
{
passwd[i] = p[((i + shift) < pwdlen) ? (i + shift) : ((i + shift) - pwdlen)];
}
memcpy (&shift, confused, sizeof (int));
shift = ((shift & 0x7A5A) % 127) + 1;
// conceptual example) ordpassw --> password
for (i = 0; i < pwdlen; i++)
{
passwd[i] = ((passwd[i] - shift) >= 0) ? (passwd[i] - shift) : (passwd[i] - shift + 128);
}
}
passwd[pwdlen] = '\0';
p += pwdlen;
pwdlen = p - ((unsigned char *) confused);
checksum = 0x7C;
p = (unsigned char *) confused;
for (i = 0; i < pwdlen; i++)
{
checksum += p[i];
}
do
{
if (p[pwdlen] != checksum)
{
return ER_FAILED;
}
}
while (++pwdlen % 3);
return NO_ERROR;
}