Use of unresolved identifier 'sqlite3_key'

App is coding with Swift, SQLCipher installed and had tried to compile the app without using database, it worked well, but when I tried to use sqlite3_key, it gives an error: Use of unresolved identifier ‘sqlite3_key’.

I have defined -DSQLITE_HAS_CODEC in CFLAGS and added #import in bridgeHeader.h

Can anyone help to solve this error?

Thanks.

Hi @jdleung

How did you install SQLCipher into your application? Are you certain you are including -DSQLITE_HAS_CODEC for the target you are building (i.e., Debug vs. Release)?

Hi @developernotes

My installation, step by step, followed the instrution on this site https://www.zetetic.net/sqlcipher/ios-tutorial/

The codes have included some sqlite3 operation before installing SQLCipher, and the App compile without any sqlite error after the SQLCipher’s installation. But when I tried to add “sqlite3_key”, it gave me “unresolved identifier” error.

Here is what I do in CFLAGS for the TARGET:

BTW, I got 22 warnings something like the following:
sqlite3.c:30923:6: “gethostuuid() is disabled.”
sqlite3.c:33984:42: Implicit conversion loses integer precision: ‘unsigned long long’ to ‘int’
sqlite3.c:76743:42: Implicit conversion loses integer precision: ‘sqlite3_uint64’ (aka ‘unsigned long long’) to ‘int’

Thanks.

Hi @developernotes

My installation, step by step, followed the instrution on this site https://www.zetetic.net/sqlcipher/ios-tutorial/

The codes have included some sqlite3 operation before installing SQLCipher, and the App compile without any sqlite error after the SQLCipher’s installation. But when I tried to add “sqlite3_key”, it gave me “unresolved identifier” error.

Here is what I do in CFLAGS for the TARGET:

BTW, I got 22 warnings something like the following:
sqlite3.c:30923:6: “gethostuuid() is disabled.”
sqlite3.c:33984:42: Implicit conversion loses integer precision: ‘unsigned long long’ to ‘int’
sqlite3.c:76743:42: Implicit conversion loses integer precision: ‘sqlite3_uint64’ (aka ‘unsigned long long’) to ‘int’

Thanks.

Hello @jdleung

Have you removed any other SQLite references such as the default SQLite dylib? If any other references to SQLite are present outside of SQLCipher you may see issues like this.

Hi, @developernotes

According to the configuration instruction:
:fire: Hot Tip: If libsqlite3.dylib or another SQLite framework is listed in your Link Binary With Libraries list be sure to remove it, or you’ll see duplicate symbol errors.

I had removed libsqlite3.tbd when adding SQLCipher to my projects , and I now add it in the project again, but the problem still exists.

Is there any issue will cause such problem? If isn’t, I may need to try a whole new configuration again.

Thanks.

@developernotes

Other SQLite3 operations are working fine, such as sqlite3_open, sqlite3_close…

You will need to remove all other references to SQLite aside from any references you source for SQLCipher. If your application is picking up on any other version of SQLite while trying to include SQLCipher it will cause issues.

Hello, @developernotes

I tried to build two TOTALLY NEW projects, one is Swift, anther is Objective-c. Adding SQLCipher in the two projects with the same steps.

The results are: Objective-c works, but Swift doesn’t. Still "use of unresolved identifier ‘sqlite3_key’. sqlite3_open and others works fine.

Hi @jdleung

I was able to replicate the behavior you are seeing, a solution that works for me was to include the following within my bridging header:

#define SQLITE_HAS_CODEC 1

Would you give that a try and let us know your results? Thanks!

Hello, @developernotes

Thanks a lot! Your suggestion works great! The database can be encrypted now. Writing code like this:

let sql2 = "ATTACH DATABASE '\(encryptedFile)' AS encrypted KEY 'testkey'"
if sqlite3_exec(db, sql2, nil, nil, nil) != SQLITE_OK{
    let errmsg = String.fromCString(sqlite3_errmsg(db))
    print("error has : \(errmsg)")
}

let sql3 = "SELECT sqlcipher_export('encrypted')"
sqlite3_exec(db, sql3, nil, nil, nil)

let sql4 = "DETACH DATABASE encrypted"
sqlite3_exec(db, sql4, nil, nil, nil)

I must enter correct key when open the database with DB browser for SQLite on Mac, but I get 2 problems when using it on iOS:

1. The encrypted database can be opened without correct key once number of key characters matches, the following operation can open the database.

    if sqlite3_open(encryptedFile, &db) == SQLITE_OK {
        let key = "testkey333" // wrong key
        sqlite3_key(db, key, 7); // correct number of characters "7"

If I place the key before opening database, both the key and the number of characters must be correct:

    let key = "testkey" // correct key
    sqlite3_key(db, key, 7); // correct number of characters "7"
    if sqlite3_open(encryptedFile, &db) == SQLITE_OK {

2. I got 22 warnings below:

Thanks.

Hi @jdleung

I am glad to hear that worked for you. With regard to your secondary problem, you must call sqlite3_open before you attempt to invoke sqlite3_key. You may wish to run something like the following against the output associated with your encryptedFile to verify it is being encrypted:

hexdump -C sqlcipher.db

Also, I would recommend checking the result code of your call to sqlite3_exec when you are performing your call to sqlcipher_export(…).

Hi, @developernotes

I think the file is encrypted, here is the screenshot after using command hexdump -C encrypted.db

But BIG PROBLEM is the encrypted database can be opened with the exact string length of the original key, and the key doesn’t matter.

For example:
I used key “testkey” when encrypted the database, it has 7 characters.

ATTACH DATABASE '\(newFile)' AS encrypted KEY 'testkey'

After the encrypted database is opened, I use the WRONG KEY and CORRECT STRING LENGTH, THE CONTENT OF THE DATABASE CAN BE READ!

let key = "testkey_WRONG"
sqlite3_key(db, key, 7);

Did I make anything wrong when encrypting the database? Or is it a bug?

Thanks.

Hello @jdleung

Can you post a full example of what you are doing? Can you adjust your process to not perform your attach, but then open the encrypted database directly, key it, finally query the database?

In this case the key is ‘testkey’. When using attach, all characters in the string are used for key material.

In this second case, the key is also ‘testkey’. Even though you have set the string to ‘testkey_WRONG’, by passing the third argument to sqlite3_key as 7, you are instructing the library to use only the first 7 characters as key material. Thus, the keys used in the first case and the second case are actually the same.

However, if you were to change your code to read as follows, where the first 7 characters are different, then the operation would fail because the first 7 characters of the string would be different, i.e. 'testkey' != 'yektset'.

let key = "yektset_WRONG"

Likewise, if you were to use the full string length, instead of truncating it, it would also fail:

let key = "testkey_WRONG"
sqlite3_key(db, key, 13);

In this case, the full string would be used for key material, and 'testkey' != 'testkey_WRONG'

Hi, @sjlombardo

Yes, I get it!

I only added characters after the original key, and I thought the key must be fully matched even if the third argument was not changed.

It works perfect now except 22 warnings :slight_smile:

@developernotes @sjlombardo
Thanks.

@jdleung I’m glad to hear everything is working for you now.

The warnings you referred to are likely from the upstream SQLite source. As a policy, we work to avoid changing core SQLite code in SQLCipher unless strictly required, as it makes auditing and periodic merges more reliable. We’ll look into the warnings, but it is possible that they may not be fixed.

Hello, @sjlombardo

I choose to ignore the warnings, because they by now will not impact the APP’s compiling. Just don’t know if they will decrease the security of SQLCipher? :laughing:

Thanks again.