/*
** Name:        cipher_common.c
** Purpose:     Implementation of SQLite codecs
** Author:      Ulrich Telle
** Created:     2020-02-02
** Copyright:   (c) 2006-2020 Ulrich Telle
** License:     MIT
*/

#include "cipher_common.h"

static unsigned char padding[] =
"\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";

/* --- Codec Descriptor Table --- */

/*
** Common configuration parameters
**
** - cipher     : default cipher type
** - hmac_check : flag whether page hmac should be verified on read
*/

static CipherParams commonParams[] =
{
  { "cipher",     CODEC_TYPE, CODEC_TYPE, 1, CODEC_TYPE_MAX },
  { "hmac_check",          1,          1, 0,              1 },
  CIPHER_PARAMS_SENTINEL
};

SQLITE_PRIVATE int
sqlite3mcGetCipherParameter(CipherParams* cipherParams, const char* paramName)
{
  int value = -1;
  for (; strlen(cipherParams->m_name) > 0; ++cipherParams)
  {
    if (sqlite3_stricmp(paramName, cipherParams->m_name) == 0) break;
  }
  if (strlen(cipherParams->m_name) > 0)
  {
    value = cipherParams->m_value;
    cipherParams->m_value = cipherParams->m_default;
  }
  return value;
}

static CodecParameter globalCodecParameterTable[] =
{
  { "global",    CODEC_TYPE_UNKNOWN,   commonParams },
#if HAVE_CIPHER_AES_128_CBC
  { "aes128cbc", CODEC_TYPE_AES128,    mcAES128Params },
#endif
#if HAVE_CIPHER_AES_256_CBC
  { "aes256cbc", CODEC_TYPE_AES256,    mcAES256Params },
#endif
#if HAVE_CIPHER_CHACHA20
  { "chacha20",  CODEC_TYPE_CHACHA20,  mcChaCha20Params },
#endif
#if HAVE_CIPHER_SQLCIPHER
  { "sqlcipher", CODEC_TYPE_SQLCIPHER, mcSQLCipherParams },
#endif
#if HAVE_CIPHER_RC4
  { "rc4",       CODEC_TYPE_RC4,       mcRC4Params },
#endif
  { "",          CODEC_TYPE_UNKNOWN,   NULL }
};

#define CODEC_TYPE_UNKNOWN   0
#define CODEC_TYPE_AES128    1
#define CODEC_TYPE_AES256    2
#define CODEC_TYPE_CHACHA20  3
#define CODEC_TYPE_SQLCIPHER 4
#define CODEC_TYPE_RC4       5

SQLITE_PRIVATE CodecParameter*
sqlite3mcCloneCodecParameterTable()
{
  /* Count number of codecs and cipher parameters */
  int nTables = 0;
  int nParams = 0;
  int j, k, n;
  CipherParams* cloneCipherParams;
  CodecParameter* cloneCodecParams;

  for (j = 0; strlen(globalCodecParameterTable[j].m_name) > 0; ++j)
  {
    CipherParams* params = globalCodecParameterTable[j].m_params;
    for (k = 0; strlen(params[k].m_name) > 0; ++k);
    nParams += k;
  }
  nTables = j;

  /* Allocate memory for cloned codec parameter tables (including sentinel for each table) */
  cloneCipherParams = (CipherParams*) sqlite3_malloc((nParams + nTables) * sizeof(CipherParams));
  cloneCodecParams = (CodecParameter*) sqlite3_malloc((nTables + 1) * sizeof(CodecParameter));

  /* Create copy of tables */
  if (cloneCodecParams != NULL)
  {
    int offset = 0;
    for (j = 0; j < nTables; ++j)
    {
      CipherParams* params = globalCodecParameterTable[j].m_params;
      cloneCodecParams[j].m_name = globalCodecParameterTable[j].m_name;
      cloneCodecParams[j].m_params = &cloneCipherParams[offset];
      for (n = 0; strlen(params[n].m_name) > 0; ++n);
      /* Copy all parameters of the current table (including sentinel) */
      for (k = 0; k <= n; ++k)
      {
        cloneCipherParams[offset + k].m_name     = params[k].m_name;
        cloneCipherParams[offset + k].m_value    = params[k].m_value;
        cloneCipherParams[offset + k].m_default  = params[k].m_default;
        cloneCipherParams[offset + k].m_minValue = params[k].m_minValue;
        cloneCipherParams[offset + k].m_maxValue = params[k].m_maxValue;
      }
      offset += (n + 1);
    }
    cloneCodecParams[nTables].m_name = globalCodecParameterTable[nTables].m_name;
    cloneCodecParams[nTables].m_params = NULL;
  }
  else
  {
    sqlite3_free(cloneCipherParams);
  }
  return cloneCodecParams;
}

SQLITE_PRIVATE void
sqlite3mcFreeCodecParameterTable(CodecParameter* codecParams)
{
  sqlite3_free(codecParams[0].m_params);
  sqlite3_free(codecParams);
}

static const CipherDescriptor mcSentinelDescriptor =
{
  "", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

static const CipherDescriptor mcDummyDescriptor =
{
  "@dummy@", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

static const CipherDescriptor* codecDescriptorTable[] =
{
#if HAVE_CIPHER_AES_128_CBC
  &mcAES128Descriptor,
#else
  &mcDummyDescriptor,
#endif
#if HAVE_CIPHER_AES_256_CBC
  &mcAES256Descriptor,
#else
  &mcDummyDescriptor,
#endif
#if HAVE_CIPHER_CHACHA20
  &mcChaCha20Descriptor,
#else
  &mcDummyDescriptor,
#endif
#if HAVE_CIPHER_SQLCIPHER
  &mcSQLCipherDescriptor,
#else
  &mcDummyDescriptor,
#endif
#if HAVE_CIPHER_RC4
  &mcRC4Descriptor,
#else
  &mcDummyDescriptor,
#endif
  &mcSentinelDescriptor
};

/* --- Codec --- */

SQLITE_PRIVATE CodecParameter*
sqlite3mcGetCodecParams(sqlite3* db);

SQLITE_PRIVATE int
sqlite3mcGetCipherType(sqlite3* db)
{
  CodecParameter* codecParams = (db != NULL) ? sqlite3mcGetCodecParams(db) : globalCodecParameterTable;
  CipherParams* cipherParamTable = (codecParams != NULL) ? codecParams[0].m_params : commonParams;
  int cipherType = CODEC_TYPE;
  CipherParams* cipher = cipherParamTable;
  for (; strlen(cipher->m_name) > 0; ++cipher)
  {
    if (sqlite3_stricmp("cipher", cipher->m_name) == 0) break;
  }
  if (strlen(cipher->m_name) > 0)
  {
    cipherType = cipher->m_value;
    cipher->m_value = cipher->m_default;
  }
  return cipherType;
}

SQLITE_PRIVATE CipherParams*
sqlite3mcGetCipherParams(sqlite3* db, int cypherType)
{
  CodecParameter* codecParams = (db != NULL) ? sqlite3mcGetCodecParams(db) : globalCodecParameterTable;
  CipherParams* cipherParamTable = (codecParams != NULL) ? codecParams[cypherType].m_params : globalCodecParameterTable[cypherType].m_params;
  return cipherParamTable;
}

SQLITE_PRIVATE int
sqlite3mcCodecInit(Codec* codec)
{
  int rc = SQLITE_OK;
  if (codec != NULL)
  {
    codec->m_isEncrypted = 0;
    codec->m_hmacCheck = 1;

    codec->m_hasReadCipher = 0;
    codec->m_readCipherType = CODEC_TYPE_UNKNOWN;
    codec->m_readCipher = NULL;
    codec->m_readReserved = -1;

    codec->m_hasWriteCipher = 0;
    codec->m_writeCipherType = CODEC_TYPE_UNKNOWN;
    codec->m_writeCipher = NULL;
    codec->m_writeReserved = -1;

    codec->m_db = NULL;
    codec->m_bt = NULL;
    codec->m_btShared = NULL;
    memset(codec->m_page, 0, sizeof(codec->m_page));
    codec->m_pageSize = 0;
    codec->m_reserved = 0;
    codec->m_hasKeySalt = 0;
    memset(codec->m_keySalt, 0, sizeof(codec->m_keySalt));
  }
  else
  {
    rc = SQLITE_NOMEM;
  }
  return rc;
}

SQLITE_PRIVATE void
sqlite3mcCodecTerm(Codec* codec)
{
  if (codec->m_readCipher != NULL)
  {
    codecDescriptorTable[codec->m_readCipherType - 1]->m_freeCipher(codec->m_readCipher);
    codec->m_readCipher = NULL;
  }
  if (codec->m_writeCipher != NULL)
  {
    codecDescriptorTable[codec->m_writeCipherType - 1]->m_freeCipher(codec->m_writeCipher);
    codec->m_writeCipher = NULL;
  }
  memset(codec, 0, sizeof(Codec));
}

SQLITE_PRIVATE void
sqlite3mcClearKeySalt(Codec* codec)
{
  codec->m_hasKeySalt = 0;
  memset(codec->m_keySalt, 0, sizeof(codec->m_keySalt));
}

SQLITE_PRIVATE int
sqlite3mcCodecSetup(Codec* codec, int cipherType, char* userPassword, int passwordLength)
{
  int rc = SQLITE_OK;
  CipherParams* globalParams = sqlite3mcGetCipherParams(codec->m_db, 0);
  codec->m_isEncrypted = 1;
  codec->m_hmacCheck = sqlite3mcGetCipherParameter(globalParams, "hmac_check");
  codec->m_hasReadCipher = 1;
  codec->m_hasWriteCipher = 1;
  codec->m_readCipherType = cipherType;
  codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType-1]->m_allocateCipher(codec->m_db);
  if (codec->m_readCipher != NULL)
  {
    unsigned char* keySalt = (codec->m_hasKeySalt != 0) ? codec->m_keySalt : NULL;
    sqlite3mcGenerateReadKey(codec, userPassword, passwordLength, keySalt);
    rc = sqlite3mcCopyCipher(codec, 1);
  }
  else
  {
    rc = SQLITE_NOMEM;
  }
  return rc;
}

SQLITE_PRIVATE int
sqlite3mcSetupWriteCipher(Codec* codec, int cipherType, char* userPassword, int passwordLength)
{
  int rc = SQLITE_OK;
  CipherParams* globalParams = sqlite3mcGetCipherParams(codec->m_db, 0);
  if (codec->m_writeCipher != NULL)
  {
    codecDescriptorTable[codec->m_writeCipherType-1]->m_freeCipher(codec->m_writeCipher);
  }
  codec->m_isEncrypted = 1;
  codec->m_hmacCheck = sqlite3mcGetCipherParameter(globalParams, "hmac_check");
  codec->m_hasWriteCipher = 1;
  codec->m_writeCipherType = cipherType;
  codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType-1]->m_allocateCipher(codec->m_db);
  if (codec->m_writeCipher != NULL)
  {
    unsigned char* keySalt = (codec->m_hasKeySalt != 0) ? codec->m_keySalt : NULL;
    sqlite3mcGenerateWriteKey(codec, userPassword, passwordLength, keySalt);
  }
  else
  {
    rc = SQLITE_NOMEM;
  }
  return rc;
}

SQLITE_PRIVATE void
sqlite3mcSetIsEncrypted(Codec* codec, int isEncrypted)
{
  codec->m_isEncrypted = isEncrypted;
}

SQLITE_PRIVATE void
sqlite3mcSetReadCipherType(Codec* codec, int cipherType)
{
  codec->m_readCipherType = cipherType;
}

SQLITE_PRIVATE void
sqlite3mcSetWriteCipherType(Codec* codec, int cipherType)
{
  codec->m_writeCipherType = cipherType;
}

SQLITE_PRIVATE void
sqlite3mcSetHasReadCipher(Codec* codec, int hasReadCipher)
{
  codec->m_hasReadCipher = hasReadCipher;
}

SQLITE_PRIVATE void
sqlite3mcSetHasWriteCipher(Codec* codec, int hasWriteCipher)
{
  codec->m_hasWriteCipher = hasWriteCipher;
}

SQLITE_PRIVATE void
sqlite3mcSetDb(Codec* codec, sqlite3* db)
{
  codec->m_db = db;
}

SQLITE_PRIVATE void
sqlite3mcSetBtree(Codec* codec, Btree* bt)
{
  codec->m_bt = bt;
  codec->m_btShared = bt->pBt;
}

SQLITE_PRIVATE void
sqlite3mcSetReadReserved(Codec* codec, int reserved)
{
  codec->m_readReserved = reserved;
}

SQLITE_PRIVATE void
sqlite3mcSetWriteReserved(Codec* codec, int reserved)
{
  codec->m_writeReserved = reserved;
}

SQLITE_PRIVATE int
sqlite3mcIsEncrypted(Codec* codec)
{
  return codec->m_isEncrypted;
}

SQLITE_PRIVATE int
sqlite3mcHasReadCipher(Codec* codec)
{
  return codec->m_hasReadCipher;
}

SQLITE_PRIVATE int
sqlite3mcHasWriteCipher(Codec* codec)
{
  return codec->m_hasWriteCipher;
}

SQLITE_PRIVATE Btree*
sqlite3mcGetBtree(Codec* codec)
{
  return codec->m_bt;
}

SQLITE_PRIVATE BtShared*
sqlite3mcGetBtShared(Codec* codec)
{
  return codec->m_btShared;
}

SQLITE_PRIVATE int
sqlite3mcGetPageSize(Codec* codec)
{
  return codec->m_btShared->pageSize;
}

SQLITE_PRIVATE int
sqlite3mcGetReadReserved(Codec* codec)
{
  return codec->m_readReserved;
}

SQLITE_PRIVATE int
sqlite3mcGetWriteReserved(Codec* codec)
{
  return codec->m_writeReserved;
}

SQLITE_PRIVATE unsigned char*
sqlite3mcGetPageBuffer(Codec* codec)
{
  return &codec->m_page[4];
}

SQLITE_PRIVATE int
sqlite3mcGetLegacyReadCipher(Codec* codec)
{
  int legacy = (codec->m_hasReadCipher  && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType - 1]->m_getLegacy(codec->m_readCipher) : 0;
  return legacy;
}

SQLITE_PRIVATE int
sqlite3mcGetLegacyWriteCipher(Codec* codec)
{
  int legacy = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1]->m_getLegacy(codec->m_writeCipher) : -1;
  return legacy;
}

SQLITE_PRIVATE int
sqlite3mcGetPageSizeReadCipher(Codec* codec)
{
  int pageSize = (codec->m_hasReadCipher  && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType - 1]->m_getPageSize(codec->m_readCipher) : 0;
  return pageSize;
}

SQLITE_PRIVATE int
sqlite3mcGetPageSizeWriteCipher(Codec* codec)
{
  int pageSize = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1]->m_getPageSize(codec->m_writeCipher) : -1;
  return pageSize;
}

SQLITE_PRIVATE int
sqlite3mcGetReservedReadCipher(Codec* codec)
{
  int reserved = (codec->m_hasReadCipher  && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType-1]->m_getReserved(codec->m_readCipher) : -1;
  return reserved;
}

SQLITE_PRIVATE int
sqlite3mcGetReservedWriteCipher(Codec* codec)
{
  int reserved = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType-1]->m_getReserved(codec->m_writeCipher) : -1;
  return reserved;
}

SQLITE_PRIVATE int
sqlite3mcReservedEqual(Codec* codec)
{
  int readReserved  = (codec->m_hasReadCipher  && codec->m_readCipher  != NULL) ? codecDescriptorTable[codec->m_readCipherType-1]->m_getReserved(codec->m_readCipher)   : -1;
  int writeReserved = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType-1]->m_getReserved(codec->m_writeCipher) : -1;
  return (readReserved == writeReserved);
}

SQLITE_PRIVATE unsigned char*
sqlite3mcGetSaltWriteCipher(Codec* codec)
{
  unsigned char* salt = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1]->m_getSalt(codec->m_writeCipher) : NULL;
  return salt;
}

SQLITE_PRIVATE int
sqlite3mcCodecCopy(Codec* codec, Codec* other)
{
  int rc = SQLITE_OK;
  codec->m_isEncrypted = other->m_isEncrypted;
  codec->m_hmacCheck = other->m_hmacCheck;
  codec->m_hasReadCipher = other->m_hasReadCipher;
  codec->m_hasWriteCipher = other->m_hasWriteCipher;
  codec->m_readCipherType = other->m_readCipherType;
  codec->m_writeCipherType = other->m_writeCipherType;
  codec->m_readCipher = NULL;
  codec->m_writeCipher = NULL;
  codec->m_readReserved = other->m_readReserved;
  codec->m_writeReserved = other->m_writeReserved;

  if (codec->m_hasReadCipher)
  {
    codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType - 1]->m_allocateCipher(codec->m_db);
    if (codec->m_readCipher != NULL)
    {
      codecDescriptorTable[codec->m_readCipherType - 1]->m_cloneCipher(codec->m_readCipher, other->m_readCipher);
    }
    else
    {
      rc = SQLITE_NOMEM;
    }
  }

  if (codec->m_hasWriteCipher)
  {
    codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType - 1]->m_allocateCipher(codec->m_db);
    if (codec->m_writeCipher != NULL)
    {
      codecDescriptorTable[codec->m_writeCipherType - 1]->m_cloneCipher(codec->m_writeCipher, other->m_writeCipher);
    }
    else
    {
      rc = SQLITE_NOMEM;
    }
  }
  codec->m_db = other->m_db;
  codec->m_bt = other->m_bt;
  codec->m_btShared = other->m_btShared;
  return rc;
}

SQLITE_PRIVATE int
sqlite3mcCodecCompare(Codec* codec1, Codec* codec2)
{
  int equal = 0;
  if (codec1->m_hasReadCipher == codec2->m_hasReadCipher &&
      codec1->m_hasWriteCipher == codec2->m_hasWriteCipher)
  {
    int eqRead = (codec1->m_hasReadCipher) ? codec1->m_readCipherType == codec2->m_readCipherType : 1;
    int eqWrite = (codec1->m_hasWriteCipher) ? codec1->m_writeCipherType == codec2->m_writeCipherType : 1;
    if (eqRead && eqWrite)
    {
      eqRead = (codec1->m_hasReadCipher) ? codecDescriptorTable[codec1->m_readCipherType - 1]->m_compareCipher(codec1->m_readCipher, codec2->m_readCipher) : 1;
      eqWrite = (codec1->m_hasWriteCipher) ? codecDescriptorTable[codec1->m_writeCipherType - 1]->m_compareCipher(codec1->m_writeCipher, codec2->m_writeCipher) : 1;
      equal = eqRead && eqWrite;
    }
  }
  return equal;
}

SQLITE_PRIVATE int
sqlite3mcCopyCipher(Codec* codec, int read2write)
{
  int rc = SQLITE_OK;
  if (read2write)
  {
    if (codec->m_writeCipher != NULL && codec->m_writeCipherType != codec->m_readCipherType)
    {
      codecDescriptorTable[codec->m_writeCipherType-1]->m_freeCipher(codec->m_writeCipher);
      codec->m_writeCipher = NULL;
    }
    if (codec->m_writeCipher == NULL)
    {
      codec->m_writeCipherType = codec->m_readCipherType;
      codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType-1]->m_allocateCipher(codec->m_db);
    }
    if (codec->m_writeCipher != NULL)
    {
      codecDescriptorTable[codec->m_writeCipherType-1]->m_cloneCipher(codec->m_writeCipher, codec->m_readCipher);
    }
    else
    {
      rc = SQLITE_NOMEM;
    }
  }
  else
  {
    if (codec->m_readCipher != NULL && codec->m_readCipherType != codec->m_writeCipherType)
    {
      codecDescriptorTable[codec->m_readCipherType-1]->m_freeCipher(codec->m_readCipher);
      codec->m_readCipher = NULL;
    }
    if (codec->m_readCipher == NULL)
    {
      codec->m_readCipherType = codec->m_writeCipherType;
      codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType-1]->m_allocateCipher(codec->m_db);
    }
    if (codec->m_readCipher != NULL)
    {
      codecDescriptorTable[codec->m_readCipherType-1]->m_cloneCipher(codec->m_readCipher, codec->m_writeCipher);
    }
    else
    {
      rc = SQLITE_NOMEM;
    }
  }
  return rc;
}

SQLITE_PRIVATE void
sqlite3mcPadPassword(char* password, int pswdlen, unsigned char pswd[32])
{
  int j;
  int p = 0;
  int m = pswdlen;
  if (m > 32) m = 32;

  for (j = 0; j < m; j++)
  {
    pswd[p++] = (unsigned char) password[j];
  }
  for (j = 0; p < 32 && j < 32; j++)
  {
    pswd[p++] = padding[j];
  }
}

SQLITE_PRIVATE void
sqlite3mcGenerateReadKey(Codec* codec, char* userPassword, int passwordLength, unsigned char* cipherSalt)
{
  codecDescriptorTable[codec->m_readCipherType-1]->m_generateKey(codec->m_readCipher, codec->m_btShared, userPassword, passwordLength, 0, cipherSalt);
}

SQLITE_PRIVATE void
sqlite3mcGenerateWriteKey(Codec* codec, char* userPassword, int passwordLength, unsigned char* cipherSalt)
{
  codecDescriptorTable[codec->m_writeCipherType-1]->m_generateKey(codec->m_writeCipher, codec->m_btShared, userPassword, passwordLength, 1, cipherSalt);
}

SQLITE_PRIVATE int
sqlite3mcEncrypt(Codec* codec, int page, unsigned char* data, int len, int useWriteKey)
{
  int cipherType = (useWriteKey) ? codec->m_writeCipherType : codec->m_readCipherType;
  void* cipher = (useWriteKey) ? codec->m_writeCipher : codec->m_readCipher;
  int reserved = (useWriteKey) ? (codec->m_writeReserved >= 0) ? codec->m_writeReserved : codec->m_reserved
                               : (codec->m_readReserved >= 0) ? codec->m_readReserved : codec->m_reserved;
  return codecDescriptorTable[cipherType-1]->m_encryptPage(cipher, page, data, len, reserved);
}

SQLITE_PRIVATE int
sqlite3mcDecrypt(Codec* codec, int page, unsigned char* data, int len)
{
  int cipherType = codec->m_readCipherType;
  void* cipher = codec->m_readCipher;
  int reserved = (codec->m_readReserved >= 0) ? codec->m_readReserved : codec->m_reserved;
  return codecDescriptorTable[cipherType-1]->m_decryptPage(cipher, page, data, len, reserved, codec->m_hmacCheck);
}

SQLITE_PRIVATE void
sqlite3mcConfigureSQLCipherVersion(sqlite3* db, int configDefault, int legacyVersion)
{
  static char* stdNames[] = { "legacy_page_size",         "kdf_iter",         "hmac_use",         "kdf_algorithm",         "hmac_algorithm",         NULL };
  static char* defNames[] = { "default:legacy_page_size", "default:kdf_iter", "default:hmac_use", "default:kdf_algorithm", "default:hmac_algorithm", NULL };
  static int versionParams[SQLCIPHER_VERSION_MAX][5] =
  {
    { 1024,   4000, 0, SQLCIPHER_KDF_ALGORITHM_SHA1,   SQLCIPHER_HMAC_ALGORITHM_SHA1   }, 
    { 1024,   4000, 1, SQLCIPHER_KDF_ALGORITHM_SHA1,   SQLCIPHER_HMAC_ALGORITHM_SHA1   },
    { 1024,  64000, 1, SQLCIPHER_KDF_ALGORITHM_SHA1,   SQLCIPHER_HMAC_ALGORITHM_SHA1   },
    { 4096, 256000, 1, SQLCIPHER_KDF_ALGORITHM_SHA512, SQLCIPHER_HMAC_ALGORITHM_SHA512 }
  };
  if (legacyVersion > 0 && legacyVersion <= SQLCIPHER_VERSION_MAX)
  {
    char** names = (configDefault != 0) ? defNames : stdNames;
    int* values = &versionParams[legacyVersion - 1][0];
    int j;
    for (j = 0; names[j] != NULL; ++j)
    {
      sqlite3mc_config_cipher(db, "sqlcipher", names[j], values[j]);
    }
  }
}
