Exemple #1
0
/* sqlite3_rekey 
** Given a database, this will reencrypt the database using a new key.
** There are two possible modes of operation. The first is rekeying
** an existing database that was not previously encrypted. The second
** is to change the key on an existing database.
** 
** The proposed logic for this function follows:
** 1. Determine if there is already a key present
** 2. If there is NOT already a key present, create one and attach a codec (key would be null)
** 3. Initialize a ctx->rekey parameter of the codec
** 
** Note: this will require modifications to the sqlite3Codec to support rekey
**
*/
int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
  CODEC_TRACE(("sqlite3_rekey: entered db=%d pKey=%s, nKey=%d\n", db, pKey, nKey));
  activate_openssl();
  if(db && pKey && nKey) {
    struct Db *pDb = &db->aDb[0];
    CODEC_TRACE(("sqlite3_rekey: database pDb=%d\n", pDb));
    if(pDb->pBt) {
      codec_ctx *ctx;
      int rc, page_count;
      Pgno pgno;
      PgHdr *page;
      Pager *pPager = pDb->pBt->pBt->pPager;

      sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
     
      if(ctx == NULL) { 
        CODEC_TRACE(("sqlite3_rekey: no codec attached to db, attaching now\n"));
        /* there was no codec attached to this database,so attach one now with a null password */
        sqlite3CodecAttach(db, 0, pKey, nKey);
        sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
        
        /* prepare this setup as if it had already been initialized */
        RAND_pseudo_bytes(ctx->kdf_salt, ctx->kdf_salt_sz);
        ctx->read_ctx->key_sz = ctx->read_ctx->iv_sz =  ctx->read_ctx->pass_sz = 0;
      }

      sqlite3_mutex_enter(db->mutex);

      if(ctx->read_ctx->iv_sz != ctx->write_ctx->iv_sz) {
        char *error;
        CODEC_TRACE(("sqlite3_rekey: updating page size for iv_sz change from %d to %d\n", ctx->read_ctx->iv_sz, ctx->write_ctx->iv_sz));
        db->nextPagesize = SQLITE_DEFAULT_PAGE_SIZE;
        pDb->pBt->pBt->pageSizeFixed = 0; /* required for sqlite3BtreeSetPageSize to modify pagesize setting */
        sqlite3BtreeSetPageSize(pDb->pBt, db->nextPagesize, EVP_MAX_IV_LENGTH, 0);
        sqlite3RunVacuum(&error, db);
      }

      codec_set_pass_key(db, 0, pKey, nKey, 1);
      ctx->mode_rekey = 1; 
    
      /* do stuff here to rewrite the database 
      ** 1. Create a transaction on the database
      ** 2. Iterate through each page, reading it and then writing it.
      ** 3. If that goes ok then commit and put ctx->rekey into ctx->key
      **    note: don't deallocate rekey since it may be used in a subsequent iteration 
      */
      rc = sqlite3BtreeBeginTrans(pDb->pBt, 1); /* begin write transaction */
      sqlite3PagerPagecount(pPager, &page_count);
      for(pgno = 1; rc == SQLITE_OK && pgno <= page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */
        if(!sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */
          rc = sqlite3PagerGet(pPager, pgno, &page);
          if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */
            rc = sqlite3PagerWrite(page);
            //printf("sqlite3PagerWrite(%d)\n", pgno);
            if(rc == SQLITE_OK) {
              sqlite3PagerUnref(page);
            } 
          } 
        } 
      }

      /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */
      if(rc == SQLITE_OK) { 
        CODEC_TRACE(("sqlite3_rekey: committing\n"));
        db->nextPagesize = SQLITE_DEFAULT_PAGE_SIZE;
        rc = sqlite3BtreeCommit(pDb->pBt); 
        cipher_ctx_copy(ctx->read_ctx, ctx->write_ctx);
      } else {
        CODEC_TRACE(("sqlite3_rekey: rollback\n"));
        sqlite3BtreeRollback(pDb->pBt);
      }

      ctx->mode_rekey = 0;
      sqlite3_mutex_leave(db->mutex);
    }
    return SQLITE_OK;
  }
  return SQLITE_ERROR;
}
Exemple #2
0
/* sqlite3_rekey 
** Given a database, this will reencrypt the database using a new key.
** There are two possible modes of operation. The first is rekeying
** an existing database that was not previously encrypted. The second
** is to change the key on an existing database.
** 
** The proposed logic for this function follows:
** 1. Determine if there is already a key present
** 2. If there is NOT already a key present, create one and attach a codec (key would be null)
** 3. Initialize a ctx->rekey parameter of the codec
** 
** Note: this will require modifications to the sqlite3Codec to support rekey
**
*/
int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
  if(db && pKey && nKey) {
    int i, prepared_key_sz;
    int key_sz =  EVP_CIPHER_key_length(CIPHER);
    void *key = sqlite3Malloc(key_sz);
    if(key == NULL) return SQLITE_NOMEM;
    
    for(i=0; i<db->nDb; i++){
      struct Db *pDb = &db->aDb[i];
      if(pDb->pBt) {
        codec_ctx *ctx;
        int rc, page_count;
        Pgno pgno;
        PgHdr *page;
        Pager *pPager = pDb->pBt->pBt->pPager;
 
        sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
        
        if(ctx == NULL) { 
          /* there was no codec attached to this database,so attach one now with a null password */
          char *error;
          db->nextPagesize =  sqlite3BtreeGetPageSize(pDb->pBt);
          pDb->pBt->pBt->pageSizeFixed = 0; /* required for sqlite3BtreeSetPageSize to modify pagesize setting */
          sqlite3BtreeSetPageSize(pDb->pBt, db->nextPagesize, EVP_CIPHER_iv_length(CIPHER), 0);
          sqlite3RunVacuum(&error, db);
          sqlite3CodecAttach(db, i, pKey, nKey);
          sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
          /* prepare this setup as if it had already been initialized */
          RAND_pseudo_bytes(ctx->salt, FILE_HEADER_SZ);
          ctx->rekey_plaintext = 1;
        }
        
        codec_prepare_key(db, pKey, nKey, ctx->salt, FILE_HEADER_SZ, key, &prepared_key_sz);  
        assert(prepared_key_sz == key_sz);
        
        ctx->rekey = key; /* set rekey to new key data - note that ctx->key is original encryption key */
      
        /* do stuff here to rewrite the database 
        ** 1. Create a transaction on the database
        ** 2. Iterate through each page, reading it and then writing it.
        ** 3. If that goes ok then commit and put ctx->rekey into ctx->key
        **    note: don't deallocate rekey since it may be used in a subsequent iteration 
        */
        rc = sqlite3BtreeBeginTrans(pDb->pBt, 1); /* begin write transaction */
        rc = sqlite3PagerPagecount(pPager, &page_count);
        for(pgno = 1; rc == SQLITE_OK && pgno <= page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */
          if(!sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */
            rc = sqlite3PagerGet(pPager, pgno, &page);
            if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */
              rc = sqlite3PagerWrite(page);
              //printf("sqlite3PagerWrite(%d)\n", pgno);
              if(rc == SQLITE_OK) {
                sqlite3PagerUnref(page);
              } 
            } 
          } 
        }

        /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */
        if(rc == SQLITE_OK) { 
          rc = sqlite3BtreeCommit(pDb->pBt); 
          memcpy(ctx->key, ctx->rekey, key_sz); 
          if(ctx->pass) {
            memset(ctx->pass, 0, ctx->pass_sz);
            sqlite3_free(ctx->pass);
          }
          ctx->pass = sqlite3Malloc(nKey);
          if(ctx->pass == NULL) return SQLITE_NOMEM;
          memcpy(ctx->pass, pKey, nKey);
          ctx->pass_sz = nKey;

        } else {
          printf("error\n");
          sqlite3BtreeRollback(pDb->pBt);
        }

        /* cleanup rekey data, make sure to overwrite rekey_plaintext or read errors will ensue */
        ctx->rekey = NULL; 
        ctx->rekey_plaintext = 0;
      }
    }
    
    /* clear and free temporary key data */
    memset(key, 0, key_sz); 
    sqlite3_free(key);
    return SQLITE_OK;
  }
  return SQLITE_ERROR;
}