int sqliterkCryptoDecode(sqliterk_codec *codec, int pgno, void *data) { int rc; int offset = 0; unsigned char *pdata = (unsigned char *) data; int page_sz = sqlcipher_codec_ctx_get_pagesize(codec); unsigned char *buffer = (unsigned char *) sqlcipher_codec_ctx_get_data(codec); rc = sqlcipher_codec_key_derive(codec); if (rc != SQLITE_OK) return rc; if (pgno == 1) { offset = 16; // FILE_HEADER_SZ memcpy(buffer, "SQLite format 3", 16); } rc = sqlcipher_page_cipher(codec, CIPHER_READ_CTX, pgno, CIPHER_DECRYPT, page_sz - offset, pdata + offset, buffer + offset); if (rc != SQLITE_OK) goto bail; memcpy(pdata, buffer, page_sz); return SQLITERK_OK; bail: sqliterkOSError(SQLITERK_DAMAGED, "Failed to decode page %d: %s", pgno, sqlite3_errstr(rc)); return rc; }
static int codec_set_btree_to_codec_pagesize(sqlite3 *db, Db *pDb, codec_ctx *ctx) { int rc, page_sz, reserve_sz; page_sz = sqlcipher_codec_ctx_get_pagesize(ctx); reserve_sz = sqlcipher_codec_ctx_get_reservesize(ctx); sqlite3_mutex_enter(db->mutex); db->nextPagesize = page_sz; pDb->pBt->pBt->pageSizeFixed = 0; CODEC_TRACE(("codec_set_btree_to_codec_pagesize: sqlite3BtreeSetPageSize() size=%d reserve=%d\n", page_sz, reserve_sz)); rc = sqlite3BtreeSetPageSize(pDb->pBt, page_sz, reserve_sz, 0); sqlite3_mutex_leave(db->mutex); return rc; }
static int codec_set_btree_to_codec_pagesize(sqlite3 *db, Db *pDb, codec_ctx *ctx) { int rc, page_sz, reserve_sz; page_sz = sqlcipher_codec_ctx_get_pagesize(ctx); reserve_sz = sqlcipher_codec_ctx_get_reservesize(ctx); sqlite3_mutex_enter(db->mutex); db->nextPagesize = page_sz; /* before forcing the page size we need to unset the BTS_PAGESIZE_FIXED flag, else sqliteBtreeSetPageSize will block the change */ pDb->pBt->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; CODEC_TRACE(("codec_set_btree_to_codec_pagesize: sqlite3BtreeSetPageSize() size=%d reserve=%d\n", page_sz, reserve_sz)); rc = sqlite3BtreeSetPageSize(pDb->pBt, page_sz, reserve_sz, 0); sqlite3_mutex_leave(db->mutex); return rc; }
/* * sqlite3Codec can be called in multiple modes. * encrypt mode - expected to return a pointer to the * encrypted data without altering pData. * decrypt mode - expected to return a pointer to pData, with * the data decrypted in the input buffer */ void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) { codec_ctx *ctx = (codec_ctx *) iCtx; int offset = 0, rc = 0; int page_sz = sqlcipher_codec_ctx_get_pagesize(ctx); unsigned char *pData = (unsigned char *) data; void *buffer = sqlcipher_codec_ctx_get_data(ctx); void *kdf_salt = sqlcipher_codec_ctx_get_kdf_salt(ctx); CODEC_TRACE(("sqlite3Codec: entered pgno=%d, mode=%d, page_sz=%d\n", pgno, mode, page_sz)); /* call to derive keys if not present yet */ if((rc = sqlcipher_codec_key_derive(ctx)) != SQLITE_OK) { sqlcipher_codec_ctx_set_error(ctx, rc); return NULL; } if(pgno == 1) offset = FILE_HEADER_SZ; /* adjust starting pointers in data page for header offset on first page*/ CODEC_TRACE(("sqlite3Codec: switch mode=%d offset=%d\n", mode, offset)); switch(mode) { case 0: /* decrypt */ case 2: case 3: if(pgno == 1) memcpy(buffer, SQLITE_FILE_HEADER, FILE_HEADER_SZ); /* copy file header to the first 16 bytes of the page */ rc = sqlcipher_page_cipher(ctx, CIPHER_READ_CTX, pgno, CIPHER_DECRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); memcpy(pData, buffer, page_sz); /* copy buffer data back to pData and return */ return pData; break; case 6: /* encrypt */ if(pgno == 1) memcpy(buffer, kdf_salt, FILE_HEADER_SZ); /* copy salt to output buffer */ rc = sqlcipher_page_cipher(ctx, CIPHER_WRITE_CTX, pgno, CIPHER_ENCRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); return buffer; /* return persistent buffer data, pData remains intact */ break; case 7: if(pgno == 1) memcpy(buffer, kdf_salt, FILE_HEADER_SZ); /* copy salt to output buffer */ rc = sqlcipher_page_cipher(ctx, CIPHER_READ_CTX, pgno, CIPHER_ENCRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); return buffer; /* return persistent buffer data, pData remains intact */ break; default: return pData; break; } }
int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const char *zRight) { struct Db *pDb = &db->aDb[iDb]; codec_ctx *ctx = NULL; int rc; if(pDb->pBt) { sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); } CODEC_TRACE(("sqlcipher_codec_pragma: entered db=%p iDb=%d pParse=%p zLeft=%s zRight=%s ctx=%p\n", db, iDb, pParse, zLeft, zRight, ctx)); if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && zRight ) { sqlcipher_codec_set_store_pass(ctx, sqlite3GetBoolean(zRight, 1)); } else if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && !zRight ) { char *store_pass_value = sqlite3_mprintf("%d", sqlcipher_codec_get_store_pass(ctx)); codec_vdbe_return_static_string(pParse, "cipher_store_pass", store_pass_value); sqlite3_free(store_pass_value); } if( sqlite3StrICmp(zLeft, "cipher_profile")== 0 && zRight ){ char *profile_status = sqlite3_mprintf("%d", sqlcipher_cipher_profile(db, zRight)); codec_vdbe_return_static_string(pParse, "cipher_profile", profile_status); sqlite3_free(profile_status); } else if( sqlite3StrICmp(zLeft, "cipher_add_random")==0 && zRight ){ if(ctx) { char *add_random_status = sqlite3_mprintf("%d", sqlcipher_codec_add_random(ctx, zRight, sqlite3Strlen30(zRight))); codec_vdbe_return_static_string(pParse, "cipher_add_random", add_random_status); sqlite3_free(add_random_status); } } else if( sqlite3StrICmp(zLeft, "cipher_migrate")==0 && !zRight ){ if(ctx){ char *migrate_status = sqlite3_mprintf("%d", sqlcipher_codec_ctx_migrate(ctx)); codec_vdbe_return_static_string(pParse, "cipher_migrate", migrate_status); sqlite3_free(migrate_status); } } else if( sqlite3StrICmp(zLeft, "cipher_provider")==0 && !zRight ){ if(ctx) { codec_vdbe_return_static_string(pParse, "cipher_provider", sqlcipher_codec_get_cipher_provider(ctx)); } } else if( sqlite3StrICmp(zLeft, "cipher_version")==0 && !zRight ){ codec_vdbe_return_static_string(pParse, "cipher_version", codec_get_cipher_version()); }else if( sqlite3StrICmp(zLeft, "cipher")==0 ){ if(ctx) { if( zRight ) { sqlcipher_codec_ctx_set_cipher(ctx, zRight, 2); // change cipher for both }else { codec_vdbe_return_static_string(pParse, "cipher", sqlcipher_codec_ctx_get_cipher(ctx, 2)); } } }else if( sqlite3StrICmp(zLeft, "rekey_cipher")==0 && zRight ){ if(ctx) sqlcipher_codec_ctx_set_cipher(ctx, zRight, 1); // change write cipher only }else if( sqlite3StrICmp(zLeft,"cipher_default_kdf_iter")==0 ){ if( zRight ) { sqlcipher_set_default_kdf_iter(atoi(zRight)); // change default KDF iterations } else { char *kdf_iter = sqlite3_mprintf("%d", sqlcipher_get_default_kdf_iter()); codec_vdbe_return_static_string(pParse, "cipher_default_kdf_iter", kdf_iter); sqlite3_free(kdf_iter); } }else if( sqlite3StrICmp(zLeft, "kdf_iter")==0 ){ if(ctx) { if( zRight ) { sqlcipher_codec_ctx_set_kdf_iter(ctx, atoi(zRight), 2); // change of RW PBKDF2 iteration } else { char *kdf_iter = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_kdf_iter(ctx, 2)); codec_vdbe_return_static_string(pParse, "kdf_iter", kdf_iter); sqlite3_free(kdf_iter); } } }else if( sqlite3StrICmp(zLeft, "fast_kdf_iter")==0){ if(ctx) { if( zRight ) { sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, atoi(zRight), 2); // change of RW PBKDF2 iteration } else { char *fast_kdf_iter = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_fast_kdf_iter(ctx, 2)); codec_vdbe_return_static_string(pParse, "fast_kdf_iter", fast_kdf_iter); sqlite3_free(fast_kdf_iter); } } }else if( sqlite3StrICmp(zLeft, "rekey_kdf_iter")==0 && zRight ){ if(ctx) sqlcipher_codec_ctx_set_kdf_iter(ctx, atoi(zRight), 1); // write iterations only }else if( sqlite3StrICmp(zLeft,"cipher_page_size")==0 ){ if(ctx) { if( zRight ) { int size = atoi(zRight); rc = sqlcipher_codec_ctx_set_pagesize(ctx, size); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); } else { char * page_size = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_pagesize(ctx)); codec_vdbe_return_static_string(pParse, "cipher_page_size", page_size); sqlite3_free(page_size); } } }else if( sqlite3StrICmp(zLeft,"cipher_default_use_hmac")==0 ){ if( zRight ) { sqlcipher_set_default_use_hmac(sqlite3GetBoolean(zRight,1)); } else { char *default_use_hmac = sqlite3_mprintf("%d", sqlcipher_get_default_use_hmac()); codec_vdbe_return_static_string(pParse, "cipher_default_use_hmac", default_use_hmac); sqlite3_free(default_use_hmac); } }else if( sqlite3StrICmp(zLeft,"cipher_use_hmac")==0 ){ if(ctx) { if( zRight ) { rc = sqlcipher_codec_ctx_set_use_hmac(ctx, sqlite3GetBoolean(zRight,1)); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); /* since the use of hmac has changed, the page size may also change */ rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); } else { char *hmac_flag = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_use_hmac(ctx, 2)); codec_vdbe_return_static_string(pParse, "cipher_use_hmac", hmac_flag); sqlite3_free(hmac_flag); } } }else if( sqlite3StrICmp(zLeft,"cipher_hmac_pgno")==0 ){ if(ctx) { if(zRight) { // clear both pgno endian flags if(sqlite3StrICmp(zRight, "le") == 0) { sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO); sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_LE_PGNO); } else if(sqlite3StrICmp(zRight, "be") == 0) { sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO); sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_BE_PGNO); } else if(sqlite3StrICmp(zRight, "native") == 0) { sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO); sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO); } } else { if(sqlcipher_codec_ctx_get_flag(ctx, CIPHER_FLAG_LE_PGNO, 2)) { codec_vdbe_return_static_string(pParse, "cipher_hmac_pgno", "le"); } else if(sqlcipher_codec_ctx_get_flag(ctx, CIPHER_FLAG_BE_PGNO, 2)) { codec_vdbe_return_static_string(pParse, "cipher_hmac_pgno", "be"); } else { codec_vdbe_return_static_string(pParse, "cipher_hmac_pgno", "native"); } } } }else if( sqlite3StrICmp(zLeft,"cipher_hmac_salt_mask")==0 ){ if(ctx) { if(zRight) { if (sqlite3StrNICmp(zRight ,"x'", 2) == 0 && sqlite3Strlen30(zRight) == 5) { unsigned char mask = 0; const unsigned char *hex = (const unsigned char *)zRight+2; cipher_hex2bin(hex,2,&mask); sqlcipher_set_hmac_salt_mask(mask); } } else { char *hmac_salt_mask = sqlite3_mprintf("%02x", sqlcipher_get_hmac_salt_mask()); codec_vdbe_return_static_string(pParse, "cipher_hmac_salt_mask", hmac_salt_mask); sqlite3_free(hmac_salt_mask); } } }else { return 0; } return 1; }
int sqliterkCryptoSetCipher(sqliterk_pager *pager, sqliterk_file *fd, const sqliterk_cipher_conf *conf) { codec_ctx *codec = NULL; int rc; if (conf) { // Check arguments. if (!conf->key || conf->key_len <= 0) return SQLITERK_MISUSE; // SQLite library must be initialized before calling sqlcipher_activate(), // or it will cause a deadlock. sqlite3_initialize(); sqlcipher_activate(); // XXX: fake BTree structure passed to sqlcipher_codec_ctx_init. // Member of such structure is assigned but never used by repair kit. int fake_db[8]; sqlite3_file_rkredir file; struct sqlite3_io_methods methods = {0}; methods.xRead = sqliterkRead; file.pMethods = &methods; file.fd = fd; file.kdf_salt = conf->kdf_salt; // Initialize codec context. rc = sqlcipher_codec_ctx_init(&codec, fake_db, NULL, &file, conf->key, conf->key_len); if (rc != SQLITE_OK) goto bail_sqlite_errstr; // Set cipher. if (conf->cipher_name) { rc = sqlcipher_codec_ctx_set_cipher(codec, conf->cipher_name, CIPHER_READWRITE_CTX); if (rc != SQLITE_OK) goto bail_sqlite_errstr; } // Set page size. if (conf->page_size > 0) { rc = sqlcipher_codec_ctx_set_pagesize(codec, conf->page_size); if (rc != SQLITE_OK) goto bail_sqlite_errstr; } // Set HMAC usage. if (conf->use_hmac >= 0) { rc = sqlcipher_codec_ctx_set_use_hmac(codec, conf->use_hmac); if (rc != SQLITE_OK) goto bail_sqlite_errstr; } // Set KDF Iteration. if (conf->kdf_iter > 0) { rc = sqlcipher_codec_ctx_set_kdf_iter(codec, conf->kdf_iter, CIPHER_READWRITE_CTX); if (rc != SQLITE_OK) goto bail; } // Update pager page size. int page_sz = sqlcipher_codec_ctx_get_pagesize(codec); int reserve_sz = sqlcipher_codec_ctx_get_reservesize(codec); pager->pagesize = page_sz; pager->reservedBytes = reserve_sz; } if (pager->codec) { sqlcipher_codec_ctx_free(&pager->codec); sqlcipher_deactivate(); } pager->codec = codec; return SQLITERK_OK; bail_sqlite_errstr: sqliterkOSError(SQLITERK_CANTOPEN, "Failed to initialize cipher context: %s", sqlite3_errstr(rc)); rc = SQLITERK_CANTOPEN; bail: if (codec) sqlcipher_codec_ctx_free(&codec); sqlcipher_deactivate(); return rc; }