Skip to content

File tde.c

File List > cubrid > src > storage > tde.c

Go to the documentation of this file

/*
 * 
 * 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.
 *
 */

/*
 * tde.c - Transparent Data Encryption (TDE) Module
 */

#ident "$Id$"

#include <stdlib.h>
#include <assert.h>

#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#if defined(WINDOWS)
#include <io.h>
#endif /* WINDOWS */

#if !defined(CS_MODE)
#include "heap_file.h"
#include "btree.h"
#include "system_parameter.h"
#include "boot_sr.h"
#include "file_io.h"
#endif /* !CS_MODE */

#include "error_manager.h"
#include "error_code.h"
#include "log_storage.hpp"
#include "log_volids.hpp"
#include "tde.h"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

#define off_signals(new_mask, old_mask) \
  do {  \
  sigfillset (&(new_mask)); \
  sigdelset (&(new_mask), SIGINT);  \
  sigdelset (&(new_mask), SIGQUIT); \
  sigdelset (&(new_mask), SIGTERM); \
  sigdelset (&(new_mask), SIGHUP);  \
  sigdelset (&(new_mask), SIGABRT);   \
  sigprocmask (SIG_SETMASK, &(new_mask), &(old_mask));  \
  } while (0)
#define restore_signals(old_mask) sigprocmask(SIG_SETMASK, &(old_mask), NULL)

#if !defined(CS_MODE)
TDE_CIPHER tde_Cipher;      /* global var for TDE Module */

static OID tde_Keyinfo_oid = OID_INITIALIZER;   /* Location of keys */
static HFID tde_Keyinfo_hfid = HFID_INITIALIZER;

static int tde_generate_keyinfo (TDE_KEYINFO * keyinfo, int mk_index, const unsigned char *master_key,
                 const time_t created_time, const TDE_DATA_KEY_SET * dks);
static int tde_update_keyinfo (THREAD_ENTRY * thread_p, const TDE_KEYINFO * keyinfo);

static int tde_create_keys_file (const char *keyfile_fullname);
static bool tde_validate_mk (const unsigned char *master_key, const unsigned char *mk_hash);
static void tde_make_mk_hash (const unsigned char *master_key, unsigned char *mk_hash);
static int tde_load_dks (const unsigned char *master_key, const TDE_KEYINFO * keyinfo);
static int tde_create_dk (unsigned char *data_key);
static int tde_encrypt_dk (const unsigned char *dk_plain, TDE_DATA_KEY_TYPE dk_type, const unsigned char *master_key,
               unsigned char *dk_cipher);
static int tde_decrypt_dk (const unsigned char *dk_cipher, TDE_DATA_KEY_TYPE dk_type, const unsigned char *master_key,
               unsigned char *dk_plain);

static void tde_dk_nonce (TDE_DATA_KEY_TYPE dk_type, unsigned char *dk_nonce);

/*
 * TDE internal functions for encrpytion and decryption. All the en/decryption go through it.
 */
static int tde_encrypt_internal (const unsigned char *plain_buffer, int length, TDE_ALGORITHM tde_algo,
                 const unsigned char *key, const unsigned char *nonce, unsigned char *cipher_buffer);
static int tde_decrypt_internal (const unsigned char *cipher_buffer, int length, TDE_ALGORITHM tde_algo,
                 const unsigned char *key, const unsigned char *nonce, unsigned char *plain_buffer);

/*
 * tde_initialize () - Initialize the tde module, which is called during initializing server.
 *
 * return             : Error code
 * thread_p (in)      : Thread entry
 * keyinfo_hfid (in)  : HFID of the special heap file which stores TDE_KEY_INFO.
 */
int
tde_initialize (THREAD_ENTRY * thread_p, HFID * keyinfo_hfid)
{
  char mk_path[PATH_MAX] = { 0, };
  unsigned char default_mk[TDE_MASTER_KEY_LENGTH] = { 0, };
  int mk_index;
  int err = NO_ERROR;
  RECDES recdes;
  HEAP_OPERATION_CONTEXT heapop_context;
  TDE_DATA_KEY_SET dks;
  time_t created_time;
  int vdes = -1;
  TDE_KEYINFO keyinfo;
  char recdes_buffer[sizeof (int) + sizeof (TDE_KEYINFO)];
  int repid_and_flag_bits = 0;

  tde_make_keys_file_fullname (mk_path, boot_db_full_name (), false);
  err = tde_create_keys_file (mk_path);
  if (err == NO_ERROR)
    {
      vdes = fileio_mount (thread_p, boot_db_full_name (), mk_path, LOG_DBTDE_KEYS_VOLID, false, false);
      if (vdes == NULL_VOLDES)
    {
      ASSERT_ERROR ();
      err = er_errid ();
      goto exit;
    }

      err = tde_create_mk (default_mk, &created_time);
      if (err != NO_ERROR)
    {
      goto exit;
    }

      err = tde_add_mk (vdes, default_mk, created_time, &mk_index);
      if (err != NO_ERROR)
    {
      goto exit;
    }
    }
  else if (err == ER_BO_VOLUME_EXISTS)
    {
      vdes = fileio_mount (thread_p, boot_db_full_name (), mk_path, LOG_DBTDE_KEYS_VOLID, false, false);
      if (vdes == NULL_VOLDES)
    {
      ASSERT_ERROR ();
      err = er_errid ();
      goto exit;
    }

      if (tde_validate_keys_file (vdes) == false)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_INVALID_KEYS_FILE, 1, mk_path);
      err = ER_TDE_INVALID_KEYS_FILE;
      goto exit;
    }

      err = tde_find_first_mk (vdes, &mk_index, default_mk, &created_time);
      if (err != NO_ERROR)
    {
      goto exit;
    }
    }
  else
    {
      goto exit;
    }

  err = tde_create_dk (dks.perm_key);
  if (err != NO_ERROR)
    {
      goto exit;
    }
  err = tde_create_dk (dks.temp_key);
  if (err != NO_ERROR)
    {
      goto exit;
    }
  err = tde_create_dk (dks.log_key);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  err = tde_generate_keyinfo (&keyinfo, mk_index, default_mk, created_time, &dks);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  /* HACK: to prevent the record from adjuestment in vacuum_rv_check_at_undo() while UNDOing */
  memcpy (recdes_buffer, &repid_and_flag_bits, sizeof (int));
  memcpy (recdes_buffer + sizeof (int), &keyinfo, sizeof (TDE_KEYINFO));

  recdes.length = recdes.area_size = sizeof (recdes_buffer);
  recdes.type = REC_HOME;
  recdes.data = (char *) recdes_buffer;

  /* Prepare context */
  heap_create_insert_context (&heapop_context, keyinfo_hfid, NULL, &recdes, NULL);

  /* Insert and fetch location */
  err = heap_insert_logical (thread_p, &heapop_context, NULL);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  HFID_COPY (&tde_Keyinfo_hfid, keyinfo_hfid);
  COPY_OID (&tde_Keyinfo_oid, &heapop_context.res_oid);

exit:
  fileio_dismount (thread_p, vdes);

  return err;
}

/*
 * tde_cipher_initialize () - Load the tde module, which is called during restarting server.
 *                            It prepares the tde_Cipher to encrpyt and decrypt.
 *
 * return             : Error code
 * thread_p (in)      : Thread entry
 * keyinfo_hfid (in)  : HFID of the special heap file which stores TDE_KEY_INFO.
 * mk_path_given (in) : The path of the master key file. It has higher priority than the default path or that from the system parameter.
 */
int
tde_cipher_initialize (THREAD_ENTRY * thread_p, const HFID * keyinfo_hfid, const char *mk_path_given)
{
  char mk_path_buffer[PATH_MAX] = { 0, };
  const char *mk_path = NULL;
  unsigned char master_key[TDE_MASTER_KEY_LENGTH];
  TDE_KEYINFO keyinfo;
  int err = NO_ERROR;
  int vdes = NULL_VOLDES;

  if (mk_path_given != NULL)
    {
      mk_path = mk_path_given;
      vdes = fileio_mount (thread_p, boot_db_full_name (), mk_path, LOG_DBTDE_KEYS_VOLID, 1, false);
      /* 
       * When restoredb, backup _keys file is given.
       * if it is fail to mount, try to use normal key file ([db]_keys) below
       */
    }

  if (mk_path == NULL || vdes == NULL_VOLDES)
    {
      tde_make_keys_file_fullname (mk_path_buffer, boot_db_full_name (), false);
      mk_path = mk_path_buffer;
      vdes = fileio_mount (thread_p, boot_db_full_name (), mk_path, LOG_DBTDE_KEYS_VOLID, 1, false);
      if (vdes == NULL_VOLDES)
    {
      ASSERT_ERROR ();
      return er_errid ();
    }
    }

  if (tde_validate_keys_file (vdes) == false)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_INVALID_KEYS_FILE, 1, mk_path);
      err = ER_TDE_INVALID_KEYS_FILE;
      goto exit;
    }

  HFID_COPY (&tde_Keyinfo_hfid, keyinfo_hfid);

  err = tde_get_keyinfo (thread_p, &keyinfo);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  assert (keyinfo.mk_index >= 0);

  err = tde_load_mk (vdes, &keyinfo, master_key);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  err = tde_load_dks (master_key, &keyinfo);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  tde_Cipher.temp_write_counter = 0;

  tde_Cipher.is_loaded = true;

exit:
  fileio_dismount (thread_p, vdes);

  return err;
}

/*
 * tde_is_loaded () - Is the TDE module initialized correctly?
 *
 * return                 : true or false
 */
bool
tde_is_loaded ()
{
  return tde_Cipher.is_loaded;
}

/*
 * tde_create_keys_file () - Create TDE master key file
 *
 * return                 : Error code
 * keyfile_fullname (in)  : Full name of the key file.
 */
static int
tde_create_keys_file (const char *keyfile_fullname)
{
  int vdes = NULL_VOLDES;
  int err = NO_ERROR;
  char magic[CUBRID_MAGIC_MAX_LENGTH] = { 0, };
#if !defined(WINDOWS)
  sigset_t new_mask, old_mask;
  off_signals (new_mask, old_mask);
#endif /* !WINDOWS */

  if (fileio_is_volume_exist (keyfile_fullname))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_VOLUME_EXISTS, 1, keyfile_fullname);
      err = ER_BO_VOLUME_EXISTS;
      goto exit;
    }

  if ((vdes = fileio_open (keyfile_fullname, O_CREAT | O_RDWR, 0600)) == NULL_VOLDES)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_CANNOT_CREATE_VOL, 3, keyfile_fullname, boot_db_full_name (),
          er_get_msglog_filename ());
      err = ER_BO_CANNOT_CREATE_VOL;
      goto exit;
    }

  memcpy (magic, CUBRID_MAGIC_KEYS, sizeof (CUBRID_MAGIC_KEYS));
  write (vdes, magic, CUBRID_MAGIC_MAX_LENGTH);

  fsync (vdes);

exit:
  if (vdes != NULL_VOLDES)
    {
      fileio_close (vdes);
    }
#if !defined(WINDOWS)
  restore_signals (old_mask);
#endif /* !WINDOWS */

  return err;
}

/*
 * tde_validate_keys_file () - Validate the master key file
 *
 * return       : Whether or not it is valid
 * vdes (in)    : Key file descriptor
 */
bool
tde_validate_keys_file (int vdes)
{
  char magic[CUBRID_MAGIC_MAX_LENGTH] = { 0, };
  bool valid = true;
#if !defined(WINDOWS)
  sigset_t new_mask, old_mask;
  off_signals (new_mask, old_mask);
#endif /* !WINDOWS */

  if (lseek (vdes, 0L, SEEK_SET) != 0L)
    {
      valid = false;
      goto exit;
    }

  read (vdes, magic, CUBRID_MAGIC_MAX_LENGTH);
  if (memcmp (magic, CUBRID_MAGIC_KEYS, sizeof (CUBRID_MAGIC_MAX_LENGTH)) != 0)
    {
      valid = false;
      goto exit;
    }

exit:
#if !defined(WINDOWS)
  restore_signals (old_mask);
#endif /* !WINDOWS */
  return valid;
}

/*
 * tde_copy_keys_file () - Copy the master key file
 *
 * return                 : Error code
 * thread_p (in)          : Thread entry
 * dest_fullname (in)     : Copy to
 * src_fullname (in)      : Copy from
 * keep_dest_mount (in)   : Whether to keep the destination file mount after this function finishes
 * keep_src_mount (in)    : Whether to keep the source file mount after this function finishes
 */
int
tde_copy_keys_file (THREAD_ENTRY * thread_p, const char *dest_fullname, const char *src_fullname, bool keep_dest_mount,
            bool keep_src_mount)
{
  char buffer[4096];
  int from_vdes = -1;
  int to_vdes = -1;
  int err = NO_ERROR;
  int nread = -1;

  from_vdes = fileio_mount (thread_p, boot_db_full_name (), src_fullname, LOG_DBTDE_KEYS_VOLID, 1, false);
  if (from_vdes == NULL_VOLDES)
    {
      ASSERT_ERROR ();
      return er_errid ();
    }

  if (tde_validate_keys_file (from_vdes) == false)
    {
      fileio_dismount (thread_p, from_vdes);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_INVALID_KEYS_FILE, 1, src_fullname);
      return ER_TDE_INVALID_KEYS_FILE;
    }

  if (fileio_is_volume_exist (dest_fullname))
    {
      fileio_unformat_and_rename (thread_p, dest_fullname, NULL);
    }

  err = tde_create_keys_file (dest_fullname);
  if (err != NO_ERROR)
    {
      fileio_dismount (thread_p, from_vdes);
      return err;
    }

  to_vdes = fileio_mount (thread_p, boot_db_full_name (), dest_fullname, LOG_DBCOPY_VOLID, 1, false);
  if (to_vdes == NULL_VOLDES)
    {
      ASSERT_ERROR_AND_SET (err);
      fileio_dismount (thread_p, from_vdes);
      return err;
    }

  if (lseek (from_vdes, 0L, SEEK_SET) != 0L)
    {
      fileio_dismount (thread_p, from_vdes);
      fileio_unformat_and_rename (thread_p, dest_fullname, NULL);
      return ER_FAILED;
    }

  while ((nread = read (from_vdes, buffer, 4096)) > 0)
    {
      char *out_ptr = buffer;
      ssize_t nwritten = -1;

      do
    {
      nwritten = write (to_vdes, out_ptr, nread);

      if (nwritten >= 0)
        {
          nread -= nwritten;
          out_ptr += nwritten;
        }
      else if (errno != EINTR)
        {
          fileio_dismount (thread_p, from_vdes);
          fileio_unformat_and_rename (thread_p, dest_fullname, NULL);
          er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 0);
          return ER_IO_WRITE;
        }
    }
      while (nread > 0);
    }

  if (!keep_src_mount)
    {
      fileio_dismount (thread_p, from_vdes);
    }
  if (!keep_dest_mount)
    {
      fileio_dismount (thread_p, to_vdes);
    }
  return err;
}

/*
 * tde_make_keys_file_fullname () - Make the full name of TDE master key file. I can be set by a system parameter
 *
 * keys_vol_fullname (out)  : Full path of the key file  
 * db_full_name (in)        : Full paht of the data volume 
 * ignore_parm (in)         : Wwhether to ignore the system parameter or not
 */
void
tde_make_keys_file_fullname (char *keys_vol_fullname, const char *db_full_name, bool ignore_parm)
{
  char *mk_path = NULL;
  const char *base_name = NULL;

  mk_path = (char *) prm_get_string_value (PRM_ID_TDE_KEYS_FILE_PATH);
  if (ignore_parm || mk_path == NULL || mk_path[0] == '\0')
    {
      fileio_make_keys_name (keys_vol_fullname, db_full_name);
    }
  else
    {
      base_name = fileio_get_base_file_name (db_full_name);
      fileio_make_keys_name_given_path (keys_vol_fullname, mk_path, base_name);
    }
}

/*
 * tde_generate_keyinfo () - Generate a keyinfo with a master key and data keys information
 *
 * return             : Error code
 * keyinfo (out)      : keyinfo 
 * mk_index (in)      : Index in the master key file
 * master_key (in)    : Master key
 * created_time (in)  : Creation time of the master key
 * dks (in)           : Data key set
 */
static int
tde_generate_keyinfo (TDE_KEYINFO * keyinfo, int mk_index, const unsigned char *master_key,
              const time_t created_time, const TDE_DATA_KEY_SET * dks)
{
  int err = NO_ERROR;

  keyinfo->mk_index = mk_index;
  tde_make_mk_hash (master_key, keyinfo->mk_hash);

  err = tde_encrypt_dk (dks->perm_key, TDE_DATA_KEY_TYPE_PERM, master_key, keyinfo->dk_perm);
  if (err != NO_ERROR)
    {
      return err;
    }
  err = tde_encrypt_dk (dks->temp_key, TDE_DATA_KEY_TYPE_TEMP, master_key, keyinfo->dk_temp);
  if (err != NO_ERROR)
    {
      return err;
    }
  err = tde_encrypt_dk (dks->log_key, TDE_DATA_KEY_TYPE_LOG, master_key, keyinfo->dk_log);
  if (err != NO_ERROR)
    {
      return err;
    }

  keyinfo->created_time = created_time;
  keyinfo->set_time = time (NULL);
  return err;
}

/*
 * tde_get_keyinfo () - Get keyinfo from key info heap file 
 *
 * return             : Error code
 * thread_p (in)          : Thread entry
 * keyinfo (out)      : keyinfo from the keyinfo heap
 */
int
tde_get_keyinfo (THREAD_ENTRY * thread_p, TDE_KEYINFO * keyinfo)
{
  RECDES recdes;
  HEAP_SCANCACHE scan_cache;
  SCAN_CODE scan = S_SUCCESS;
  char recdes_buffer[sizeof (int) + sizeof (TDE_KEYINFO)];
  int error_code = NO_ERROR;

  assert (!HFID_IS_NULL (&tde_Keyinfo_hfid));

  recdes.length = recdes.area_size = sizeof (recdes_buffer);
  recdes.data = (char *) recdes_buffer;

  heap_scancache_quick_start_with_class_hfid (thread_p, &scan_cache, &tde_Keyinfo_hfid);
  scan = heap_first (thread_p, &tde_Keyinfo_hfid, NULL, &tde_Keyinfo_oid, &recdes, &scan_cache, COPY);
  heap_scancache_end (thread_p, &scan_cache);

  if (scan != S_SUCCESS)
    {
      assert (false);
      return ER_FAILED;
    }

  /* HACK: the front of the record if dummy int to prevent the record from adjuestment
   *  in vacuum_rv_check_at_undo() while UNDOing, refer to tde_insert_keyinfo() */
  memcpy (keyinfo, recdes_buffer + sizeof (int), sizeof (TDE_KEYINFO));

  return NO_ERROR;
}

/*
 * tde_get_keyinfo () - Get keyinfo from key info heap file 
 *
 * return           : Error code
 * thread_p (in)    : Thread entry
 * keyinfo (in)     : keyinfo to update that in the keyinfo heap
 */
static int
tde_update_keyinfo (THREAD_ENTRY * thread_p, const TDE_KEYINFO * keyinfo)
{
  HEAP_SCANCACHE scan_cache;
  HEAP_OPERATION_CONTEXT update_context;
  RECDES recdes;
  char recdes_buffer[sizeof (int) + sizeof (TDE_KEYINFO)];
  int repid_and_flag_bits = 0;
  int error_code = NO_ERROR;

  assert (!HFID_IS_NULL (&tde_Keyinfo_hfid));
  assert (!OID_ISNULL (&tde_Keyinfo_oid));

  /* HACK: to prevent the record from adjuestment in vacuum_rv_check_at_undo() while UNDOing */
  memcpy (recdes_buffer, &repid_and_flag_bits, sizeof (int));
  memcpy (recdes_buffer + sizeof (int), keyinfo, sizeof (TDE_KEYINFO));

  recdes.length = recdes.area_size = sizeof (recdes_buffer);
  recdes.data = (char *) recdes_buffer;

  /* note that we start a scan cache with NULL class_oid. That's because ketinfo_oid doesn't really have a class!
   * we have to start the scan cache this way so it can cache also cache file type for heap_update_logical.
   * otherwise it will try to read it from cache using root class OID. which actually has its own heap file and its own
   * heap file type.
   */
  error_code = heap_scancache_start_modify (thread_p, &scan_cache, &tde_Keyinfo_hfid, NULL, SINGLE_ROW_UPDATE, NULL);
  if (error_code != NO_ERROR)
    {
      ASSERT_ERROR ();
      return error_code;
    }

  /* hack the class to avoid heap_scancache_check_with_hfid. */
  scan_cache.node.class_oid = *oid_Root_class_oid;
  heap_create_update_context (&update_context, &tde_Keyinfo_hfid, &tde_Keyinfo_oid, oid_Root_class_oid, &recdes,
                  &scan_cache, UPDATE_INPLACE_CURRENT_MVCCID);
  error_code = heap_update_logical (thread_p, &update_context);
  if (error_code != NO_ERROR)
    {
      ASSERT_ERROR ();
    }
  heap_scancache_end (thread_p, &scan_cache);
  return error_code;
}

/*
 * tde_chnage_mk () - Change the master key set on the database
 *
 * return             : Error code
 * thread_p (in)      : Thread entry
 * mk_index (in)      : The index in the master key file
 * master_key (in)    : The master key
 * created_time (in)  : The creation time of the master key
 */
int
tde_change_mk (THREAD_ENTRY * thread_p, const int mk_index, const unsigned char *master_key, const time_t created_time)
{
  TDE_KEYINFO keyinfo;
  TDE_DATA_KEY_SET dks;
  int err = NO_ERROR;

  if (!tde_is_loaded ())
    {
      err = ER_TDE_CIPHER_IS_NOT_LOADED;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_CIPHER_IS_NOT_LOADED, 0);
      return err;
    }

  /* generate keyinfo from tde_Cipher and update heap (on Disk) */
  err = tde_generate_keyinfo (&keyinfo, mk_index, master_key, created_time, &tde_Cipher.data_keys);
  if (err != NO_ERROR)
    {
      return err;
    }

  err = tde_update_keyinfo (thread_p, &keyinfo);
  if (err != NO_ERROR)
    {
      return err;
    }

  /* heap_flush() is mandatory. Without this, it cannot be guaranteed
   * that the master key corresponding key info in heap exists in _keys file.
   * By calling heap_flush at end of changing key, DBA can remove the previous key after it.
   */
  heap_flush (thread_p, &tde_Keyinfo_oid);

  return err;
}

/*
 * tde_load_mk () - Read a master key specified by keyinfo from the master key file and validate it
 *
 * return             : Error code
 * vdes (in)          : Key file descriptor
 * keyinfo (in)       : Key info which contains the master key information to load
 * master_key (out)   : Master key
 */
int
tde_load_mk (int vdes, const TDE_KEYINFO * keyinfo, unsigned char *master_key)
{
  int err = NO_ERROR;
  unsigned char mk[TDE_MASTER_KEY_LENGTH];
  time_t created_time;

  assert (keyinfo->mk_index >= 0);

  err = tde_find_mk (vdes, keyinfo->mk_index, mk, &created_time);
  if (err != NO_ERROR)
    {
      return err;
    }

  /* MK has found */

  if (!(tde_validate_mk (mk, keyinfo->mk_hash) && created_time == keyinfo->created_time))
    {
      err = ER_TDE_INVALID_MASTER_KEY;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_INVALID_MASTER_KEY, 1, keyinfo->mk_index);
      return err;
    }
  /* MK has validated */

  memcpy (master_key, mk, TDE_MASTER_KEY_LENGTH);

  return err;
}

/*
 * tde_load_dks () - Load data keys from keyinfo onto tde_Cipher
 *
 * return             : Error code
 * master_key (in)    : Master key
 * keyinfo (in)       : Keyinfo which contains encrypted data keys
 */
static int
tde_load_dks (const unsigned char *master_key, const TDE_KEYINFO * keyinfo)
{
  int err = NO_ERROR;

  err = tde_decrypt_dk (keyinfo->dk_perm, TDE_DATA_KEY_TYPE_PERM, master_key, tde_Cipher.data_keys.perm_key);
  if (err != NO_ERROR)
    {
      return err;
    }
  err = tde_decrypt_dk (keyinfo->dk_temp, TDE_DATA_KEY_TYPE_TEMP, master_key, tde_Cipher.data_keys.temp_key);
  if (err != NO_ERROR)
    {
      return err;
    }
  err = tde_decrypt_dk (keyinfo->dk_log, TDE_DATA_KEY_TYPE_LOG, master_key, tde_Cipher.data_keys.log_key);
  if (err != NO_ERROR)
    {
      return err;
    }

  return err;
}

/*
 * tde_validate_mk () - Validate a master key by comparing with the hash value, 
 *                      usually with the hash value stored in tde key info heap
 *
 * return             : Valid or not
 * master_key (in)    : Master key
 * mk_hash (in)       : Hash to be compared with the master key
 */
static bool
tde_validate_mk (const unsigned char *master_key, const unsigned char *mk_hash)
{
  unsigned char hash[SHA256_DIGEST_LENGTH];

  tde_make_mk_hash (master_key, hash);

  if (memcmp (mk_hash, hash, TDE_MASTER_KEY_LENGTH) != 0)
    {
      return false;
    }
  return true;
}

/*
 * tde_make_mk_hash () - Make a hash value to validate master key later
 *
 * master_key (in)    : Master key
 * mk_hash (out)      : Hash value created with the master key
 */
static void
tde_make_mk_hash (const unsigned char *master_key, unsigned char *mk_hash)
{
  SHA256_CTX sha_ctx;

  assert (SHA256_DIGEST_LENGTH == TDE_MASTER_KEY_LENGTH);
  assert (master_key != NULL);
  assert (mk_hash != NULL);

  SHA256_Init (&sha_ctx);
  SHA256_Update (&sha_ctx, master_key, TDE_MASTER_KEY_LENGTH);
  SHA256_Final (mk_hash, &sha_ctx);
}

/*
 * tde_create_dk () - Create a data key
 *
 * return             : Error code
 * data_key (out)      : Data key created
 */
static int
tde_create_dk (unsigned char *data_key)
{
  assert (data_key != NULL);

  if (RAND_bytes (data_key, TDE_DATA_KEY_LENGTH) != 1)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_KEY_CREATION_FAIL, 0);
      return ER_TDE_KEY_CREATION_FAIL;
    }

  return NO_ERROR;
}

/*
 * tde_encrypt_dk () - encrypt a data key to store a data key in stable storage, 
 *                     it has to be encrypted.
 *
 * return             : error code
 * dk_plain (in)      : data key to encrypt
 * dk_type (in)       : data key type
 * master_key (in)    : a master key to encrypt the data key
 * dk_cipher (out)    : encrypted data key
 */
static int
tde_encrypt_dk (const unsigned char *dk_plain, TDE_DATA_KEY_TYPE dk_type, const unsigned char *master_key,
        unsigned char *dk_cipher)
{
  unsigned char dk_nonce[TDE_DK_NONCE_LENGTH] = { 0, };

  tde_dk_nonce (dk_type, dk_nonce);

  return tde_encrypt_internal (dk_plain, TDE_DATA_KEY_LENGTH, TDE_DK_ALGORITHM, master_key, dk_nonce, dk_cipher);
}

/*
 * tde_decrypt_dk () - Decrypt a data key
 *
 * return             : Error code
 * dk_cipher (in)     : Data key to decrypt
 * dk_type (in)       : Data key type
 * master_key (in)    : A master key to encrypt the data key
 * dk_plain (out)     : Decrypted data key
 */
static int
tde_decrypt_dk (const unsigned char *dk_cipher, TDE_DATA_KEY_TYPE dk_type, const unsigned char *master_key,
        unsigned char *dk_plain)
{
  unsigned char dk_nonce[TDE_DK_NONCE_LENGTH] = { 0, };

  tde_dk_nonce (dk_type, dk_nonce);

  return tde_decrypt_internal (dk_cipher, TDE_DATA_KEY_LENGTH, TDE_DK_ALGORITHM, master_key, dk_nonce, dk_plain);
}

/*
 * tde_dk_nonce () - Get a data key nonce according to dk_type
 *
 * dk_type (in)       : Data key type
 * dk_nonce (out)     : A nonce for dk_type
 */
static inline void
tde_dk_nonce (TDE_DATA_KEY_TYPE dk_type, unsigned char *dk_nonce)
{
  assert (dk_nonce != NULL);

  switch (dk_type)
    {
    case TDE_DATA_KEY_TYPE_PERM:
      memset (dk_nonce, 0, TDE_DK_NONCE_LENGTH);
      break;
    case TDE_DATA_KEY_TYPE_TEMP:
      memset (dk_nonce, 1, TDE_DK_NONCE_LENGTH);
      break;
    case TDE_DATA_KEY_TYPE_LOG:
      memset (dk_nonce, 2, TDE_DK_NONCE_LENGTH);
      break;
    default:
      assert (false);
      break;
    }
}

/*
 * tde_encrypt_data_page () - Encrypt a data page. 
 *
 * return               : Error code
 * iopage_plain (in)    : Data page to encrypt
 * tde_algo (in)        : Encryption algorithm
 * is_temp (in)         : Whether the page is for temp file
 * iopage_cipher (out)  : Encrpyted data page
 *
 * Nonce value for temporary file and permanent file are different.
 */
int
tde_encrypt_data_page (const FILEIO_PAGE * iopage_plain, TDE_ALGORITHM tde_algo, bool is_temp,
               FILEIO_PAGE * iopage_cipher)
{
  int err = NO_ERROR;
  unsigned char nonce[TDE_DATA_PAGE_NONCE_LENGTH] = { 0, };
  const unsigned char *data_key;
  int64_t tmp_nonce;

  if (tde_is_loaded () == false)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_CIPHER_IS_NOT_LOADED, 0);
      return ER_TDE_CIPHER_IS_NOT_LOADED;
    }

  if (is_temp)
    {
      // temporary file: atomic counter for nonce
      data_key = tde_Cipher.data_keys.temp_key;
      tmp_nonce = ATOMIC_INC_64 (&tde_Cipher.temp_write_counter, 1);
      memcpy (nonce, &tmp_nonce, sizeof (tmp_nonce));
    }
  else
    {
      // permanent file: page lsa as nonce
      data_key = tde_Cipher.data_keys.perm_key;
      memcpy (nonce, &iopage_plain->prv.lsa, sizeof (iopage_plain->prv.lsa));
    }

  /* copy FILEIO_PAGE_RESERVED */
  memcpy (iopage_cipher, iopage_plain, TDE_DATA_PAGE_ENC_OFFSET);
  /* copy FILEIO_PAGE_WATERMARK */
  memcpy ((char *) iopage_cipher + TDE_DATA_PAGE_ENC_OFFSET + TDE_DATA_PAGE_ENC_LENGTH,
      (char *) iopage_plain + TDE_DATA_PAGE_ENC_OFFSET + TDE_DATA_PAGE_ENC_LENGTH, sizeof (FILEIO_PAGE_WATERMARK));

  memcpy (&iopage_cipher->prv.tde_nonce, nonce, sizeof (iopage_cipher->prv.tde_nonce));

  err = tde_encrypt_internal (((const unsigned char *) iopage_plain) + TDE_DATA_PAGE_ENC_OFFSET,
                  TDE_DATA_PAGE_ENC_LENGTH, tde_algo, data_key, nonce,
                  ((unsigned char *) iopage_cipher) + TDE_DATA_PAGE_ENC_OFFSET);

  return err;
}

/*
 * tde_decrypt_data_page () - Decrypt a data page. 
 *
 * return               : Error code
 * iopage_cipher (in)   : Data page to decrypt
 * tde_algo (in)        : Encryption algorithm
 * is_temp (in)         : Whether the page is for temp file
 * iopage_plain (out)   : Decrypted data page
 */
int
tde_decrypt_data_page (const FILEIO_PAGE * iopage_cipher, TDE_ALGORITHM tde_algo, bool is_temp,
               FILEIO_PAGE * iopage_plain)
{
  int err = NO_ERROR;
  unsigned char nonce[TDE_DATA_PAGE_NONCE_LENGTH] = { 0, };
  const unsigned char *data_key;

  if (tde_is_loaded () == false)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_CIPHER_IS_NOT_LOADED, 0);
      return ER_TDE_CIPHER_IS_NOT_LOADED;
    }

  if (is_temp)
    {
      // temporary file: atomic counter for nonce
      data_key = tde_Cipher.data_keys.temp_key;
    }
  else
    {
      // permanent file: page lsa for nonce
      data_key = tde_Cipher.data_keys.perm_key;
    }

  /* copy FILEIO_PAGE_RESERVED */
  memcpy (iopage_plain, iopage_cipher, TDE_DATA_PAGE_ENC_OFFSET);
  /* copy FILEIO_PAGE_WATERMARK */
  memcpy ((char *) iopage_plain + TDE_DATA_PAGE_ENC_OFFSET + TDE_DATA_PAGE_ENC_LENGTH,
      (char *) iopage_cipher + TDE_DATA_PAGE_ENC_OFFSET + TDE_DATA_PAGE_ENC_LENGTH, sizeof (FILEIO_PAGE_WATERMARK));

  memcpy (nonce, &iopage_cipher->prv.tde_nonce, sizeof (iopage_cipher->prv.tde_nonce));

  err = tde_decrypt_internal (((const unsigned char *) iopage_cipher) + TDE_DATA_PAGE_ENC_OFFSET,
                  TDE_DATA_PAGE_ENC_LENGTH, tde_algo, data_key, nonce,
                  ((unsigned char *) iopage_plain) + TDE_DATA_PAGE_ENC_OFFSET);

  return err;
}

/*
 * tde_encrypt_log_page () - Encrypt a log page. 
 *
 * return               : Error code
 * iopage_plain (in)    : Log page to encrypt
 * tde_algo (in)        : Encryption algorithm
 * iopage_cipher (out)  : Encrpyted log page
 */
int
tde_encrypt_log_page (const LOG_PAGE * logpage_plain, TDE_ALGORITHM tde_algo, LOG_PAGE * logpage_cipher)
{
  unsigned char nonce[TDE_LOG_PAGE_NONCE_LENGTH] = { 0, };
  const unsigned char *data_key;

  if (tde_is_loaded () == false)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_CIPHER_IS_NOT_LOADED, 0);
      return ER_TDE_CIPHER_IS_NOT_LOADED;
    }

  data_key = tde_Cipher.data_keys.log_key;

  memcpy (nonce, &logpage_plain->hdr.logical_pageid, sizeof (logpage_plain->hdr.logical_pageid));
  memcpy (logpage_cipher, logpage_plain, TDE_LOG_PAGE_ENC_OFFSET);

  return tde_encrypt_internal (((const unsigned char *) logpage_plain) + TDE_LOG_PAGE_ENC_OFFSET,
                   TDE_LOG_PAGE_ENC_LENGTH, tde_algo, data_key, nonce,
                   ((unsigned char *) logpage_cipher) + TDE_LOG_PAGE_ENC_OFFSET);
}

/*
 * tde_decrypt_log_page () - Decrypt a log page. 
 *
 * return               : Error code
 * iopage_cipher (in)   : Log page to decrypt
 * tde_algo (in)        : Encryption algorithm
 * iopage_plain (out)   : Decrpyted log page
 */
int
tde_decrypt_log_page (const LOG_PAGE * logpage_cipher, TDE_ALGORITHM tde_algo, LOG_PAGE * logpage_plain)
{
  unsigned char nonce[TDE_LOG_PAGE_NONCE_LENGTH] = { 0, };
  const unsigned char *data_key;

  if (tde_is_loaded () == false)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_CIPHER_IS_NOT_LOADED, 0);
      return ER_TDE_CIPHER_IS_NOT_LOADED;
    }

  data_key = tde_Cipher.data_keys.log_key;

  memcpy (nonce, &logpage_cipher->hdr.logical_pageid, sizeof (logpage_cipher->hdr.logical_pageid));
  memcpy (logpage_plain, logpage_cipher, TDE_LOG_PAGE_ENC_OFFSET);

  return tde_decrypt_internal (((const unsigned char *) logpage_cipher) + TDE_LOG_PAGE_ENC_OFFSET,
                   TDE_LOG_PAGE_ENC_LENGTH, tde_algo, data_key, nonce,
                   ((unsigned char *) logpage_plain) + TDE_LOG_PAGE_ENC_OFFSET);
}

/*
 * tde_encrypt_internal () - Gerneral encryption function
 *
 * return               : Error code
 * plain_buffer (in)    : Data to encrypt
 * length (in)          : The length of data
 * tde_algo (in)        : Encryption algorithm
 * key (in)             : key
 * nonce (in)           : nonce, which has to be unique in time and space
 * cipher_buffer (out)  : Encrypted data
 *
 * plain_buffer and cipher_buffer has more space than length
 */
static int
tde_encrypt_internal (const unsigned char *plain_buffer, int length, TDE_ALGORITHM tde_algo, const unsigned char *key,
              const unsigned char *nonce, unsigned char *cipher_buffer)
{
  EVP_CIPHER_CTX *ctx;
  const EVP_CIPHER *cipher_type;
  int len;
  int cipher_len;
  int err = ER_TDE_ENCRYPTION_ERROR;

  if ((ctx = EVP_CIPHER_CTX_new ()) == NULL)
    {
      goto exit;
    }

  switch (tde_algo)
    {
    case TDE_ALGORITHM_AES:
      cipher_type = EVP_aes_256_ctr ();
      break;
    case TDE_ALGORITHM_ARIA:
      cipher_type = EVP_aria_256_ctr ();
      break;
    case TDE_ALGORITHM_NONE:
    default:
      assert (false);
      goto cleanup;
    }

  if (EVP_EncryptInit_ex (ctx, cipher_type, NULL, key, nonce) != 1)
    {
      goto cleanup;
    }

  if (EVP_EncryptUpdate (ctx, cipher_buffer, &len, plain_buffer, length) != 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 == length);

  err = NO_ERROR;

cleanup:
  EVP_CIPHER_CTX_free (ctx);

exit:
  if (err != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_ENCRYPTION_ERROR, 0);
    }
  return err;
}

/*
 * tde_decrypt_internal () - Gerneral decryption function
 *
 * return               : Error code
 * cipher_buffer (in)   : Data to decrypt
 * length (in)          : The length of data
 * tde_algo (in)        : Encryption algorithm
 * key (in)             : key
 * nonce (in)           : nonce used during encryption
 * plain_buffer (out)   : Decrypted data
 *
 * plain_buffer and cipher_buffer has more space than length
 */
static int
tde_decrypt_internal (const unsigned char *cipher_buffer, int length, TDE_ALGORITHM tde_algo, const unsigned char *key,
              const unsigned char *nonce, unsigned char *plain_buffer)
{
  EVP_CIPHER_CTX *ctx;
  const EVP_CIPHER *cipher_type;
  int len;
  int plain_len;
  int err = ER_TDE_DECRYPTION_ERROR;

  if ((ctx = EVP_CIPHER_CTX_new ()) == NULL)
    {
      goto exit;
    }

  switch (tde_algo)
    {
    case TDE_ALGORITHM_AES:
      cipher_type = EVP_aes_256_ctr ();
      break;
    case TDE_ALGORITHM_ARIA:
      cipher_type = EVP_aria_256_ctr ();
      break;
    case TDE_ALGORITHM_NONE:
    default:
      assert (false);
      goto cleanup;
    }

  if (EVP_DecryptInit_ex (ctx, cipher_type, NULL, key, nonce) != 1)
    {
      goto cleanup;
    }

  if (EVP_DecryptUpdate (ctx, plain_buffer, &len, cipher_buffer, length) != 1)
    {
      goto cleanup;
    }
  plain_len = len;

  // Further plaintext bytes may be written at finalizing (Partial block).
  if (EVP_DecryptFinal_ex (ctx, plain_buffer + len, &len) != 1)
    {
      goto cleanup;
    }
  plain_len += len;

  // CTR_MODE is stream mode so that there is no need to check,
  // but check it for safe.
  assert (plain_len == length);

  err = NO_ERROR;

cleanup:
  EVP_CIPHER_CTX_free (ctx);

exit:
  if (err != NO_ERROR)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_DECRYPTION_ERROR, 0);
    }
  return err;
}

/*
 * xtde_get_mk_info () - Get some information of the master key set on the database
 *
 * return             : Error code
 * thread_p (in)      : Thread entry
 * mk_index (out)     : Index of the master key in the master key file
 * created_time (out) : Creation time of the master key
 * set_time (out)     : Set time of the master key
 */
int
xtde_get_mk_info (THREAD_ENTRY * thread_p, int *mk_index, time_t * created_time, time_t * set_time)
{
  TDE_KEYINFO keyinfo;
  int err = NO_ERROR;

  if (!tde_is_loaded ())
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_CIPHER_IS_NOT_LOADED, 0);
      return ER_TDE_CIPHER_IS_NOT_LOADED;
    }

  err = tde_get_keyinfo (thread_p, &keyinfo);
  if (err != NO_ERROR)
    {
      return err;
    }
  *mk_index = keyinfo.mk_index;
  *created_time = keyinfo.created_time;
  *set_time = keyinfo.set_time;

  return err;
}

/*
 * xtde_change_mk_without_flock () - Change the master key on the database.
 *                                   While changing, it mounts the key file without file lock. 
 *
 * return             : Error code
 * thread_p (in)      : Thread entry
 * mk_index (in)      : Index of the master key to set
 */
int
xtde_change_mk_without_flock (THREAD_ENTRY * thread_p, const int mk_index)
{
  char mk_path[PATH_MAX] = { 0, };
  TDE_KEYINFO keyinfo;
  unsigned char master_key[TDE_MASTER_KEY_LENGTH] = { 0, };
  time_t created_time;
  int vdes;
  int err = NO_ERROR;

  tde_make_keys_file_fullname (mk_path, boot_db_full_name (), false);

  /* Without file lock: It is because it must've already been locked in client-side: tde() */
  vdes = fileio_open (mk_path, O_RDONLY, 0);
  if (vdes == NULL_VOLDES)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, mk_path);
      return ER_IO_MOUNT_FAIL;
    }

  err = tde_find_mk (vdes, mk_index, master_key, &created_time);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  err = tde_get_keyinfo (thread_p, &keyinfo);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  /* if the same key with the key set on the database */
  if (mk_index == keyinfo.mk_index && tde_validate_mk (master_key, keyinfo.mk_hash))
    {
      goto exit;
    }

  /* The previous key has to exist */
  err = tde_find_mk (vdes, keyinfo.mk_index, NULL, NULL);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  err = tde_change_mk (thread_p, mk_index, master_key, created_time);
  if (err != NO_ERROR)
    {
      goto exit;
    }

exit:
  fileio_close (vdes);
  return err;
}

#endif /* !CS_MODE */

/*
 * tde_create_mk () - Create a master key
 *
 * return               : Error code
 * master_key (out)     : Created master key
 * created_time (out)   : The time the key created
 */
int
tde_create_mk (unsigned char *master_key, time_t * created_time)
{
  assert (master_key != NULL);

  if (RAND_bytes (master_key, TDE_MASTER_KEY_LENGTH) != 1)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_KEY_CREATION_FAIL, 0);
      return ER_TDE_KEY_CREATION_FAIL;
    }

  *created_time = time (NULL);

  return NO_ERROR;
}

/*
 * tde_print_mk () - Print a master key value
 *
 * master_key (out)   : Master key to print
 */
void
tde_print_mk (const unsigned char *master_key)
{
  int i;
  for (i = 0; i < TDE_MASTER_KEY_LENGTH; i++)
    {
      printf ("%02x", master_key[i]);
    }
}

/*
 * tde_add_mk () - Add a master key to the master key file
 *
 * return             : Error code
 * vdes (in)          : Key file descriptor
 * master_key (in)    : A master key to add
 * created_time (in)  : When the master key was created
 * mk_index (out)     : The assigend index for the master key to add
 */
int
tde_add_mk (int vdes, const unsigned char *master_key, time_t created_time, int *mk_index)
{
  TDE_MK_FILE_ITEM adding_item;
  TDE_MK_FILE_ITEM reading_item;
  int location = 0;
  int err = NO_ERROR;
  int ret;
#if !defined(WINDOWS)
  sigset_t new_mask, old_mask;
  off_signals (new_mask, old_mask);
#endif /* !WINDOWS */

  if (lseek (vdes, TDE_MK_FILE_CONTENTS_START, SEEK_SET) != TDE_MK_FILE_CONTENTS_START)
    {
      err = ER_FAILED;
      goto exit;
    }

  adding_item.created_time = created_time;
  memcpy (adding_item.master_key, master_key, TDE_MASTER_KEY_LENGTH);

  while (true)
    {
      ret = read (vdes, &reading_item, TDE_MK_FILE_ITEM_SIZE);
      if (ret < 0)
    {
      err = ER_IO_READ;
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2,
                   fileio_get_volume_label_by_fd (vdes, PEEK), -1);
      goto exit;
    }
      else if (ret == 0)
    {
      break;        /* EOF */
    }
      else if (ret != TDE_MK_FILE_ITEM_SIZE)
    {
      err = ER_TDE_INVALID_KEYS_FILE;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_INVALID_KEYS_FILE, 1,
          fileio_get_volume_label_by_fd (vdes, PEEK));
      goto exit;
    }

      if (reading_item.created_time == -1)
    {
      /* invalid item, which means available space */
      lseek (vdes, -TDE_MK_FILE_ITEM_SIZE, SEEK_CUR);
      break;
    }
    }

  location = lseek (vdes, 0, SEEK_CUR);

  if (TDE_MK_FILE_ITEM_INDEX (location) >= TDE_MK_FILE_ITEM_COUNT_MAX)
    {
      err = ER_TDE_MAX_KEY_FILE;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_MAX_KEY_FILE, 0);
      goto exit;
    }
  else
    {
      *mk_index = TDE_MK_FILE_ITEM_INDEX (location);
    }

  /* add key */
  write (vdes, &adding_item, TDE_MK_FILE_ITEM_SIZE);

  fsync (vdes);
exit:
#if !defined(WINDOWS)
  restore_signals (old_mask);
#endif /* !WINDOWS */

  return err;
}

/*
 * tde_find_mk () - Find a master key in the master key file
 *
 * return               : Error code
 * vdes (in)            : Key file descriptor
 * mk_index (in)        : Index of the master key to find
 * master_key (out)     : The master key to add
 * created_time (out)   : When the master key was created
 */
int
tde_find_mk (int vdes, int mk_index, unsigned char *master_key, time_t * created_time)
{
  bool found;
  int location;
  TDE_MK_FILE_ITEM item;

#if !defined(WINDOWS)
  sigset_t new_mask, old_mask;
  off_signals (new_mask, old_mask);
#endif /* !WINDOWS */

  location = TDE_MK_FILE_ITEM_OFFSET (mk_index);

  if (lseek (vdes, location, SEEK_SET) != location)
    {
      found = false;
      goto exit;        /* not found */
    }

  if (read (vdes, &item, TDE_MK_FILE_ITEM_SIZE) != TDE_MK_FILE_ITEM_SIZE)
    {
      found = false;
      goto exit;
    }

  if (item.created_time == -1)
    {
      found = false;
      goto exit;
    }

  if (master_key != NULL)
    {
      memcpy (master_key, item.master_key, TDE_MASTER_KEY_LENGTH);
    }

  if (created_time != NULL)
    {
      *created_time = item.created_time;
    }

  found = true;

exit:
#if !defined(WINDOWS)
  restore_signals (old_mask);
#endif /* !WINDOWS */

  if (!found)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_MASTER_KEY_NOT_FOUND, 1, mk_index);
      return ER_TDE_MASTER_KEY_NOT_FOUND;
    }

  return NO_ERROR;
}

/*
 * tde_find_first_mk () - Find the first master key in the master key file
 *
 * return               : Error code
 * vdes (in)            : Key file descriptor
 * mk_index (out)       : Index of the master key first met
 * master_key (out)     : The master key to be found
 * created_time (out)   : When the master key was created
 */
int
tde_find_first_mk (int vdes, int *mk_index, unsigned char *master_key, time_t * created_time)
{
  TDE_MK_FILE_ITEM item;
  int location;
  int index = 0;
  int err = NO_ERROR;
  int ret;

#if !defined(WINDOWS)
  sigset_t new_mask, old_mask;
  off_signals (new_mask, old_mask);
#endif /* !WINDOWS */

  location = TDE_MK_FILE_ITEM_OFFSET (0);

  if (lseek (vdes, location, SEEK_SET) != location)
    {
      err = ER_FAILED;
      goto exit;        /* not found */
    }

  while (true)
    {
      ret = read (vdes, &item, TDE_MK_FILE_ITEM_SIZE);
      if (ret < 0)
    {
      err = ER_IO_READ;
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2,
                   fileio_get_volume_label_by_fd (vdes, PEEK), -1);
      goto exit;
    }
      else if (ret != TDE_MK_FILE_ITEM_SIZE)
    {
      /* The file has been crashed, or the key is not found and EOF. */
      err = ER_TDE_INVALID_KEYS_FILE;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_INVALID_KEYS_FILE, 1,
          fileio_get_volume_label_by_fd (vdes, PEEK));
      goto exit;
    }

      if (item.created_time != -1)
    {
      *mk_index = index;
      *created_time = item.created_time;
      memcpy (master_key, item.master_key, TDE_MASTER_KEY_LENGTH);
      break;
    }
      index++;
    }

exit:
#if !defined(WINDOWS)
  restore_signals (old_mask);
#endif /* !WINDOWS */
  return err;
}

/*
 * tde_delete_mk () - Delete a master key in the master key file
 *
 * return               : Error code
 * vdes (in)            : Key file descriptor
 * mk_index (in)        : Index of the master key to delete
 */
int
tde_delete_mk (int vdes, int mk_index)
{
  bool found;
  int location;
  TDE_MK_FILE_ITEM item;

#if !defined(WINDOWS)
  sigset_t new_mask, old_mask;
  off_signals (new_mask, old_mask);
#endif /* !WINDOWS */

  location = TDE_MK_FILE_ITEM_OFFSET (mk_index);

  if (lseek (vdes, location, SEEK_SET) != location)
    {
      found = false;
      goto exit;        /* not found */
    }

  if (read (vdes, &item, TDE_MK_FILE_ITEM_SIZE) != TDE_MK_FILE_ITEM_SIZE)
    {
      found = false;
      goto exit;
    }

  if (item.created_time == -1)
    {
      found = false;
      goto exit;
    }

  item.created_time = -1;   /* mark it invalid */

  lseek (vdes, -TDE_MK_FILE_ITEM_SIZE, SEEK_CUR);
  write (vdes, &item, TDE_MK_FILE_ITEM_SIZE);
  fsync (vdes);
  found = true;

exit:
#if !defined(WINDOWS)
  restore_signals (old_mask);
#endif /* !WINDOWS */

  if (!found)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_MASTER_KEY_NOT_FOUND, 1, mk_index);
      return ER_TDE_MASTER_KEY_NOT_FOUND;
    }

  return NO_ERROR;
}

/*
 * tde_find_mk () - Dump master keys in a master key file
 *
 * return               : Error code
 * vdes (in)            : Key file descriptor
 * print_value (in)     : Wheter to print key value
 */
int
tde_dump_mks (int vdes, bool print_value)
{
  TDE_MK_FILE_ITEM item;
  int cnt_valid = 0;
  int cnt_invalid = 0;
  int location;
  int i;
  char ctime_buf[CTIME_MAX] = { 0, };
#if !defined(WINDOWS)
  sigset_t new_mask, old_mask;
  off_signals (new_mask, old_mask);
#endif /* !WINDOWS */

  if ((location = lseek (vdes, TDE_MK_FILE_CONTENTS_START, SEEK_SET)) != TDE_MK_FILE_CONTENTS_START)
    {
      return ER_FAILED;
    }

  printf ("Keys Information: \n");

  while (true)
    {
      if (read (vdes, &item, TDE_MK_FILE_ITEM_SIZE) != TDE_MK_FILE_ITEM_SIZE)
    {
      break;        /* EOF */
    }

      if (item.created_time == -1)
    {
      cnt_invalid++;
    }
      else
    {
      cnt_valid++;
      ctime_r (&item.created_time, ctime_buf);
      printf ("Key Index: %lu ", TDE_MK_FILE_ITEM_INDEX (location));
      printf ("created on %s", ctime_buf);
      if (print_value)
        {
          printf ("Key: ");
          tde_print_mk (item.master_key);
          printf ("\n");
        }
    }
      location += TDE_MK_FILE_ITEM_SIZE;
    }
  printf ("\n");

exit:
  printf ("The number of keys: %d\n", cnt_valid);

#if !defined(WINDOWS)
  restore_signals (old_mask);
#endif /* !WINDOWS */
  return NO_ERROR;
}

/*
 * tde_get_algorithm_name () - Convert TDE_ALGORITHM to corresponding string
 *
 * return               : String version of tde_algo. NULL means it is not valid
 * tde_algo (in)        : Encryption algorithm
 *
 */
const char *
tde_get_algorithm_name (TDE_ALGORITHM tde_algo)
{
  switch (tde_algo)
    {
    case TDE_ALGORITHM_NONE:
      return "NONE";
    case TDE_ALGORITHM_AES:
      return "AES";
    case TDE_ALGORITHM_ARIA:
      return "ARIA";
    default:
      return NULL;
    }
}