Integrating SQLCipher into a Core Data project, but not encrypting Core Data store

My app is now using Core Data (SQLite store) AND a separate SQLite database. My Core Data part is where my static schema is stored and the SQLite store is where my dynamic schema is stored. Tables in the database and columns in the tables can be added and deleted. That’s the dynamic part. I’m doing it this way for flexibility and performance reasons. So everything has been working great up until the next thing I need to do. Encryption.

I want to encrypt the SQLite database using SQLCipher. I’m using cocoapods to integrate SQLCipher with the following parameters in my Podfile:

target 'TFCoreMac' do
        platform :osx, '12.0'
        pod 'FMDB'
        pod 'FMDB/SQLCipher'
end

post_install do |installer_representation|
  installer_representation.pods_project.targets.each do |target|
    if target.name == 'TFCoreMac'
      target.build_configurations.each do |config|
        config.build_settings['OTHER_CFLAGS'] ||= ['$(inherited)']
        config.build_settings['OTHER_CFLAGS'] << '-DSQLITE_HAS_CODEC -DSQLITE_POWERSAFE_OVERWRITE=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_TEMP_STORE=2 -DSQLITE_THREADSAFE=2 -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS4_UNICODE61'
      end
    end
  end
end

The problem I’m having is the version of SQLite that my app seems to be using is the built-in system SQLite version that Core Data is using, which does not support encryption.

Is there a way to allow Core Data to do its own thing with SQLite while other parts of my app use the SQCipher version of SQLite? Or can I get Core Data to use the same SQLite build that SQLCipher is using?

Some ideas of what I can try would be very helpful and appreciated. Thanks.

Hi @BrendanD

Thank you for your interest in SQLCipher and for posting to the discussion forum.

One thing I noticed is that you’re using both the standard FMDB pod and the FMDB/SQLCipher pod, you should only need to use the FMDB/SQLCipher pod (which includes all the standard FMDB stuff + SQLCipher).

Is there a way to allow Core Data to do its own thing with SQLite while other parts of my app use the SQCipher version of SQLite? Or can I get Core Data to use the same SQLite build that SQLCipher is using?

We DO NOT SUPPORT using SQLCipher with a project that includes a separate sqlite3 dependency, including via CocoaPods. That sort of configuration carries multiple inherent risks, including undefined behavior, deadlocks, loss of data, loss of encryption functionality, etc.

That being said, you may want to review this document: Important Advisory: SQLCipher with Xcode 8 and new SDKs – specifically the section under Project Setup and Linking which has instructions to ensure SQLCipher is linked into your Application first.

Hi @mmoore

Thanks for the help! That was the key! Adding the -framework SQLCipher entry to the Other Linker Flags setting at the project level.

Also thanks for the warning about using SQLite with another dependency. I’ll have to keep an eye on that. I know some people are using SQLCipher with Core Data in the Encrypted Core Data project. But that project hasn’t been updated in many years and I’m reluctant to rely on something old like that. That’s why I’m leaving my Core Data store unencrypted and SQLCipher will be used just to encrypt records data in a separate SQLite database file.

There is a minor version number difference between the Apple supplied SQLite (on Ventura). Ventura has 3.39.5 and SQLCipher has 3.39.4. Hopefully that’s just a minor difference and nothing consequential.

Actually it turns out just having that extra FMDB pod in my Podfile is what caused the built-in SQLite version to be used. The Other Linker Flags option didn’t make a difference as I took it out to test and with just having FMDB/SQLCipher in my Podfile allowed the SQLCipher version 3.39.4 to be loaded instead of the built-in 3.39.5.

But your warning does have me worried a bit. There are quite a few differences in the way the built-in SQLite is compiled vs. the SQLCipher cocoa pods version.

Apple’s SQLite 3.39.5 version:
ATOMIC_INTRINSICS=1
BUG_COMPATIBLE_20160819
CCCRYPT256
COMPILER=clang-14.0.0
DEFAULT_AUTOVACUUM
DEFAULT_CACHE_SIZE=2000
DEFAULT_CKPTFULLFSYNC
DEFAULT_FILE_FORMAT=4
DEFAULT_JOURNAL_SIZE_LIMIT=32768
DEFAULT_LOOKASIDE=1200,102
DEFAULT_MEMSTATUS=0
DEFAULT_MMAP_SIZE=0
DEFAULT_PAGE_SIZE=4096
DEFAULT_PCACHE_INITSZ=20
DEFAULT_RECURSIVE_TRIGGERS
DEFAULT_SECTOR_SIZE=4096
DEFAULT_SYNCHRONOUS=2
DEFAULT_WAL_AUTOCHECKPOINT=1000
DEFAULT_WAL_SYNCHRONOUS=1
DEFAULT_WORKER_THREADS=0
ENABLE_API_ARMOR
ENABLE_BYTECODE_VTAB
ENABLE_COLUMN_METADATA
ENABLE_DBSTAT_VTAB
ENABLE_FTS3
ENABLE_FTS3_PARENTHESIS
ENABLE_FTS3_TOKENIZER
ENABLE_FTS4
ENABLE_FTS5
ENABLE_LOCKING_STYLE=1
ENABLE_NORMALIZE
ENABLE_PREUPDATE_HOOK
ENABLE_RTREE
ENABLE_SESSION
ENABLE_SNAPSHOT
ENABLE_SQLLOG
ENABLE_STMT_SCANSTATUS
ENABLE_UNKNOWN_SQL_FUNCTION
ENABLE_UPDATE_DELETE_LIMIT
HAS_CODEC_RESTRICTED
HAVE_ISNAN
MALLOC_SOFT_LIMIT=1024
MAX_ATTACHED=10
MAX_COLUMN=2000
MAX_COMPOUND_SELECT=500
MAX_DEFAULT_PAGE_SIZE=8192
MAX_EXPR_DEPTH=1000
MAX_FUNCTION_ARG=127
MAX_LENGTH=2147483645
MAX_LIKE_PATTERN_LENGTH=50000
MAX_MMAP_SIZE=1073741824
MAX_PAGE_COUNT=1073741823
MAX_PAGE_SIZE=65536
MAX_SQL_LENGTH=1000000000
MAX_TRIGGER_DEPTH=1000
MAX_VARIABLE_NUMBER=500000
MAX_VDBE_OP=250000000
MAX_WORKER_THREADS=8
MUTEX_UNFAIR
OMIT_AUTORESET
OMIT_LOAD_EXTENSION
STMTJRNL_SPILL=131072
SYSTEM_MALLOC
TEMP_STORE=1
THREADSAFE=2
USE_URI

SQLCipher’s 3.39.4 version:

ATOMIC_INTRINSICS=1
COMPILER=clang-14.0.0
DEFAULT_AUTOVACUUM
DEFAULT_CACHE_SIZE=-2000
DEFAULT_FILE_FORMAT=4
DEFAULT_JOURNAL_SIZE_LIMIT=-1
DEFAULT_MMAP_SIZE=0
DEFAULT_PAGE_SIZE=4096
DEFAULT_PCACHE_INITSZ=20
DEFAULT_RECURSIVE_TRIGGER
DEFAULT_SECTOR_SIZE=4096
DEFAULT_SYNCHRONOUS=2
DEFAULT_WAL_AUTOCHECKPOINT=1000
DEFAULT_WAL_SYNCHRONOUS=2
DEFAULT_WORKER_THREADS=0
ENABLE_COLUMN_METADATA
ENABLE_FTS3
ENABLE_FTS3_PARENTHESIS
ENABLE_FTS4
ENABLE_FTS5
ENABLE_LOAD_EXTENSION
ENABLE_MEMORY_MANAGEMENT
ENABLE_RTREE
ENABLE_STAT4
ENABLE_UNLOCK_NOTIFY
HAS_CODEC
MALLOC_SOFT_LIMIT=1024
MAX_ATTACHED=10
MAX_COLUMN=2000
MAX_COMPOUND_SELECT=500
MAX_DEFAULT_PAGE_SIZE=8192
MAX_EXPR_DEPTH=1000
MAX_FUNCTION_ARG=127
MAX_LENGTH=1000000000
MAX_LIKE_PATTERN_LENGTH=50000
MAX_MMAP_SIZE=0x7fff0000
MAX_PAGE_COUNT=1073741823
MAX_PAGE_SIZE=65536
MAX_SQL_LENGTH=1000000000
MAX_TRIGGER_DEPTH=1000
MAX_VARIABLE_NUMBER=99999
MAX_VDBE_OP=250000000
MAX_WORKER_THREADS=8
MUTEX_PTHREADS
SOUNDEX
SYSTEM_MALLOC
TEMP_STORE=2
THREADSAFE=1

I don’t know why THREADSAFE=1. I explicitly set it to 2 in my Podfile.

Do you see any of these differences to be troublesome?

@BrendanD

What was occurring since you were including both (FMDB + FMDB/SQLCipher pods) is that -l"sqlite3" was being automatically put in your Other Linker flags by the FMDB pod and -framework SQLCipher was after it (from the FMDB/SQLCipher pod) so standard sqlite was being linked first into your Application. After removing the standard FMDB, SQLCipher is now properly being linked first.

The C Flags used for the SQLCipher Pod are in the Podspec here: https://github.com/sqlcipher/sqlcipher/blob/master/SQLCipher.podspec.json

We don’t develop or maintain FMDB, but I believe there are a few more added in their Podspec as well.