int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, sqlite3_file *fd, const void *zKey, int nKey) { int rc; codec_ctx *ctx; *iCtx = sqlcipher_malloc(sizeof(codec_ctx)); ctx = *iCtx; if(ctx == NULL) return SQLITE_NOMEM; ctx->pBt = pDb->pBt; /* assign pointer to database btree structure */ /* allocate space for salt data. Then read the first 16 bytes directly off the database file. This is the salt for the key derivation function. If we get a short read allocate a new random salt value */ ctx->kdf_salt_sz = FILE_HEADER_SZ; ctx->kdf_salt = sqlcipher_malloc(ctx->kdf_salt_sz); if(ctx->kdf_salt == NULL) return SQLITE_NOMEM; /* allocate space for separate hmac salt data. We want the HMAC derivation salt to be different than the encryption key derivation salt */ ctx->hmac_kdf_salt = sqlcipher_malloc(ctx->kdf_salt_sz); if(ctx->hmac_kdf_salt == NULL) return SQLITE_NOMEM; /* Always overwrite page size and set to the default because the first page of the database in encrypted and thus sqlite can't effectively determine the pagesize. this causes an issue in cases where bytes 16 & 17 of the page header are a power of 2 as reported by John Lehman */ if((rc = sqlcipher_codec_ctx_set_pagesize(ctx, SQLITE_DEFAULT_PAGE_SIZE)) != SQLITE_OK) return rc; if((rc = sqlcipher_cipher_ctx_init(&ctx->read_ctx)) != SQLITE_OK) return rc; if((rc = sqlcipher_cipher_ctx_init(&ctx->write_ctx)) != SQLITE_OK) return rc; if(fd == NULL || sqlite3OsRead(fd, ctx->kdf_salt, FILE_HEADER_SZ, 0) != SQLITE_OK) { ctx->need_kdf_salt = 1; } if((rc = sqlcipher_codec_ctx_set_cipher(ctx, CIPHER, 0)) != SQLITE_OK) return rc; if((rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, default_kdf_iter, 0)) != SQLITE_OK) return rc; if((rc = sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, FAST_PBKDF2_ITER, 0)) != SQLITE_OK) return rc; if((rc = sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, 0)) != SQLITE_OK) return rc; /* Note that use_hmac is a special case that requires recalculation of page size so we call set_use_hmac to perform setup */ if((rc = sqlcipher_codec_ctx_set_use_hmac(ctx, default_flags & CIPHER_FLAG_HMAC)) != SQLITE_OK) return rc; if((rc = sqlcipher_cipher_ctx_copy(ctx->write_ctx, ctx->read_ctx)) != SQLITE_OK) return rc; return SQLITE_OK; }
int codec_set_use_hmac(sqlite3* db, int nDb, int use) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("codec_set_use_hmac: entered db=%d nDb=%d use=%d\n", db, nDb, use)); if(pDb->pBt) { int rc; codec_ctx *ctx; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); rc = sqlcipher_codec_ctx_set_use_hmac(ctx, use); if(rc != SQLITE_OK) return rc; /* since the use of hmac has changed, the page size may also change */ /* Note: before forcing the page size we need to force pageSizeFixed to 0, else sqliteBtreeSetPageSize will block the change */ return codec_set_btree_to_codec_pagesize(db, pDb, ctx); } return SQLITE_ERROR; }
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; }