GithubHelp home page GithubHelp logo

sqleet's Introduction

Warning: This project is currently unmaintained due to the deprecation of codec API in SQLite3 upstream. Therefore, sqleet is stuck at SQLite version 3.31.1 until a major rewrite happens. Meanwhile, I recommend using wxSQLite3 which offers sqleet-compatible encryption for the latest version(s) of SQLite3.


sqleet is an encryption extension for SQLite3. The encryption is transparent (on-the-fly) and based on modern cryptographic algorithms designed for high performance in software and robust side-channel resistance. The compilation of sqleet is easy because there are no external dependencies, which simplifies cross-compiling and cross-platform development.

In the spirit of SQLite3, the sqleet source code is in the public domain.

Compiling

SQLite3 shell with sqleet encryption support can be compiled as follows:

% # UNIX
% gcc sqleet.c shell.c -o sqleet -lpthread -ldl

% # Windows
% gcc sqleet.c shell.c -o sqleet

Example demonstrates the use of the sqleet encryption extension with the compiled shell. For application programmers, sqleet API offers C programming interface and language-agnostic URI-based configuration interface for run-time management of the encryption settings.

To use sqleet as a library, the recommended way is to download a preconfigured release package instead of cloning the git repository. Release package contains sqleet.c and sqleet.h amalgamations that are drop-in replacements for the official sqlite3.c and sqlite3.h amalgamations. Non-amalgamated sqleet.c and sqleet.h files from master branch can be used as drop-in replacements similarly, assuming all necessary sqleet source files are available during compilation. However, sqleet development mainly happens in the master branch, so release are considered to be more stable and a better choice for the average user.

Building a custom release version of sqleet is a straightforward task.

  • Clone or fork sqleet and patch it as you wish
  • Create source and header amalgamations for release
    • ./script/amalgamate.sh <sqleet.c >sqlame.c
    • ./script/amalgamate.sh <sqleet.h >sqlame.h
  • Package the amalgamations with other release files

script/release.sh shows the exact release procedure of sqleet.

Example

Encrypting an existing database hello.db with a key (i.e., password) "swordfish".

[sqleet]% hexdump -C hello.db
00000000  53 51 4c 69 74 65 20 66  6f 72 6d 61 74 20 33 00  |SQLite format 3.|
00000010  10 00 01 01 00 40 20 20  00 00 00 01 00 00 00 02  |.....@  ........|
*
00000fd0  00 00 00 2b 01 06 17 17  17 01 37 74 61 62 6c 65  |...+......7table|
00000fe0  68 65 6c 6c 6f 68 65 6c  6c 6f 02 43 52 45 41 54  |hellohello.CREAT|
00000ff0  45 20 54 41 42 4c 45 20  68 65 6c 6c 6f 28 78 29  |E TABLE hello(x)|
*
00001fe0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 0f  |................|
00001ff0  01 02 27 48 65 6c 6c 6f  2c 20 77 6f 72 6c 64 21  |..'Hello, world!|
[sqleet]% ./sqleet hello.db
SQLite version 3.28.0 2019-04-16 19:49:53
Enter ".help" for usage hints.
sqlite> PRAGMA rekey='swordfish';
sqlite> .quit
[sqleet]% hexdump -C hello.db
00000000  4e 61 0c 1a 25 3f 77 1e  20 50 f4 56 61 c6 b3 37  |Na..%?w. P.Va..7|
00000010  eb aa d5 59 37 0d e6 41  1d d1 69 c8 8e 9a f5 eb  |...Y7..A..i.....|
*
00001fe0  07 79 a0 3b f1 cc 9f 7b  b2 72 11 21 28 15 71 ce  |.y.;...{.r.!(.q.|
00001ff0  e5 ad 4a cd 75 af 8e 8a  e2 79 f3 d9 2e 21 e8 4b  |..J.u....y...!.K|

Notice that the data of the encrypted database is indistinguishable from random. After encryption, the unencrypted data is accessible only with the correct key.

[sqleet]% ./sqleet hello.db
SQLite version 3.28.0 2019-04-16 19:49:53
Enter ".help" for usage hints.
sqlite> .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
/**** ERROR: (26) file is not a database *****/
ROLLBACK; -- due to errors
sqlite> PRAGMA key='swordfish';
sqlite> .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE hello(x);
INSERT INTO hello VALUES('Hello, world!');
COMMIT;

Instead of PRAGMA commands, the key can also be provided in SQLite3 URI filename using key parameter.

[sqleet]% ./sqleet 'file:hello.db?key=swordfish' 'SELECT * FROM hello'
Hello, world!

Cryptography

  • PBKDF2-HMAC-SHA256 key derivation with a 16-byte salt and 12345 iterations.
  • ChaCha20 stream cipher with one-time keys.
  • Poly1305 authentication tags.

A low-level description of the database encryption scheme is available in sqleet.c:265.

sqleet API

The public sqleet API consists of C programming interface and URI configuration interface.

C programming interface

sqleet defines SQLITE_HAS_CODEC compile-time option to expose SQLite3 encryption API, i.e., C functions sqlite3_key() and sqlite3_rekey() for managing database encryption keys. These functions can be called directly from C code, while other programming languages need to call the C functions via FFI mechanism. Another way to invoke the functions is with PRAGMA key and PRAGMA rekey commands (see Example).

SQLITE_API int sqlite3_key(      /* Invoked by PRAGMA key='x' */
  sqlite3 *db,                   /* Database to key */
  const void *pKey, int nKey     /* Key (password) */
);

sqlite3_key() is typically called immediately after sqlite3_open() to specify an encryption key (password) for the opened database. The function validates the key by decrypting the first page of the database from disk. Return value is SQLITE_OK if the given key was correct; otherwise, a non-zero SQLite3 error code is returned and subsequent attempts to read or write the database will fail.

SQLITE_API int sqlite3_rekey(    /* Invoked by PRAGMA rekey='x' */
  sqlite3 *db,                   /* Database to rekey */
  const void *pKey, int nKey     /* New key (password) */
);

sqlite3_rekey() changes the database encryption key. This includes encrypting the database the first time, fully decrypting the database (if nKey == 0), as well as re-encrypting it with a new key. Internally, the function runs VACUUM command to encrypt or decrypt all pages of the database, whereas re-encryption with a new key is performed directly by processing each page sequentially. Return value is SQLITE_OK on success and an SQLite3 error code on failure.

In addition, there are sqlite3_key_v2() and sqlite3_rekey_v2() functions that accept name of the target database as the second parameter.


Note:
In sqleet, the contents of an encrypted database file are indistinguishable from random data (of the same length). This is a conscious design decision, but as a drawback, database settings cannot be read from the database file. Therefore, it is the user's responsibility to properly initialize database settings before accessing the database. The most common issue is that opening a database fails regardless of valid key because the page size of the database differs from the default 4096 and page_size has not been set to the correct value with PRAGMA or URI API.

The official SQLite Encryption Extension (SEE) leaves bytes 16..23 of the database header unencrypted so that page size and other settings can be directly read from encrypted databases, which obviously makes SEE-encrypted databases distinguishable from random data. In sqleet, this behavior can be optionally enabled with -DSKIP_HEADER_BYTES=24 compile-time flag (bytes 0..15 contain the KDF salt so only the bytes 16..23 are actually skipped and left unencrypted). At run-time, the compile-time default can be overridden with URI parameter skip=n where n is the skip amount.


URI configuration interface

Disclaimer: URI interface is experimental and subject to changes in future versions. Use at your own risk!

Run-time configuration of sqleet encryption is implemented based on SQLite3 URI filenames which contain configuration parameters for databases. List of URI parameters supported by sqleet:

Parameter Description
key Encryption key for sqlite3_key() after opening the database
salt 16-byte salt for the key derivation function (KDF)
header 16-byte header overwriting the database magic header (or salt)
kdf Key derivation function (only none supported for now)
skip Run-time setting overriding compile-time SKIP_HEADER_BYTES
page_size Equivalent to page_size PRAGMA

Parameters key, salt and header have additional hex-prefixed versions that accept hex input strings such as '73716c656574'.

Parameters salt and header expect 16-byte input strings. Shorter strings are zero-padded to 16-bytes, while longer inputs get automatically rejected.

Parameter header represents the first 16 bytes of the database file, that is, SQLite3 magic header string for unencrypted databases. For encrypted databases, header defaults to the value of salt unless explicitly set to other value. Remember that salt is a parameter for the key derivation function (KDF) which is stored in the beginning of the database file by default, in which case both salt and header contain the same value (KDF salt). Sometimes, however, the user may want to keep the salt secret, or control the first 16 bytes of the database file for some purpose. In such cases, the user stores the salt and then overwrites the beginning of the database file with any 16-byte header value. (If this explanation was too abstract or nonsensical to fully grasp, see the iOS workaround in the end of Android/iOS support for a practical real-world use-case of header feature).

URI parameter kdf=none disables the default PBKDF2-HMAC-SHA256 key derivation. If KDF is disabled, key and hexkey accept a 32-byte raw key that becomes the master encryption key which otherwise would be derived by the KDF from the key and salt. Disabling KDF is a powerful feature for advanced users who need full control of the key derivation process.

Parameters skip and page_size override the compile-time SKIP_HEADER_BYTES value and the database PRAGMA page_size configuration.

Changing URI settings of an existing database can be accomplished with VACUUM INTO (introduced in SQLite 3.27.0) by giving new URI parameter values in the INTO filename. For example, VACUUM INTO 'file:skipped.db?skip=24' vacuums the current main database to file skipped.db with skip set to 24. Other URI settings, including the encryption key, are inherited from the main database unless key parameter is specified, in which case any undefined parameters are initialized to default values. Database settings update with VACUUM INTO is complex operation with many special cases and important details (omitted here). So be prepared for some undocumented behavior, but please open an issue if encountering obviously broken on wrong behavior.

Erroneus parameters, such as unsupported parameter value or otherwise bad input, returns a non-zero SQLite3 error code when opening (or vacuuming) a database. The current version returns SQLITE_MISUSE error, in most cases, if URI parsing fails or the resulting configuration is invalid.

Android/iOS support

sqleet does not have an out-of-the-box support for Android. However, SQLite Android Bindings project provides an easy way to bundle a custom SQLite3 version (such as sqleet) into an Android application with the standard Android interface android.database.sqlite. In particular, see Using The SQLite Encryption Extension page for build & usage instructions.

Likewise, sqleet does not offer an iOS version either, but compiling a custom SQLite3 with sqleet encryption support for iOS is a straightforward task (e.g., compile switflyfalling/SQLiteLib with sqleet release amalgamation instead of the SQLite3 amalgamation). Moreover, iOS apps with an encrypted WAL-journaled SQLite3 database in a shared data container are terminated when sent to the background (see sqlcipher/sqlcipher#255, TN2408 and TN2151 for more information). A common workaround is to leave the first 32 bytes of the database file unencrypted so that iOS recognizes the file as a regular WAL-journaled SQLite3 database and does not terminate the app. Thus, an iOS-compatible sqleet database can be created with the following URI settings:

[sqleet]% ./sqleet 'file:ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.28.0 2019-04-16 19:49:53
Enter ".help" for usage hints.
sqlite> CREATE TABLE f(x,y);
sqlite> .quit
[sqleet]% xxd secrets.db | head -n5
00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.
00000010: 1000 0101 2040 2020 0000 0001 0000 0002  .... @  ........
00000020: 4640 824c 703e 3f72 dffc 3a19 23a6 c964  [email protected]>?r..:.#..d
00000030: a1b3 abf0 8f3c 996f 0eb8 c665 afe1 0d72  .....<.o...e...r
00000040: b864 57f7 2492 8c31 6398 61d0 5d49 5a28  .dW.$..1c.a.]IZ(

Versioning scheme

sqleet releases follow a perverse form of semantic versioning which requires some explanation. Major version number increments indicate compatibility breaks as usual, but the minor and patch version numbers match the targeted SQLite3 version. For instance, sqleet v0.25.1 corresponds to SQLite v3.25.1. Although the target SQLite3 version is the primarily supported, sqleet is typically forward and backward compatible across different SQLite3 versions without any changes to the code.

As a corollary, sqleet releases are published whenever a new SQLite3 version is released. A new sqleet release thus does not necessarily include bug fixes or new features (except updated SQLite3 version) if there has been no commits to sqleet master branch since the previous SQLite3 release. Releases page contains a changelog for each sqleet release version.

License

Like SQLite3, sqleet has been released in the public domain (specifically, under the UNLICENSE license). In other words, feel free to do whatever the fuck you want to with the code. In the unlikely case that your country's legal system is broken with respect to public domain software, contact [email protected] for a custom-licensed version.

sqleet's People

Contributors

hasselmm avatar joshuawise avatar resilar avatar utelle avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sqleet's Issues

Documentation of the new raw key feature

IMHO the new raw key feature deserves a slightly more elaborate documentation regarding its behaviour in certain "special" cases:

  1. If the length of the raw key is not exactly 32, it is handled as a normal key (including the "raw:" prefix).
  2. If the length of the raw key plus salt is not exactly 32+16 (48), it is handled as a normal key (including the "raw:" prefix).
  3. If the length of the hex encoded key is not exactly 64, it is handled as a normal key (including the "raw:" prefix).
  4. If the length of the hex encoded key plus salt is not exactly 64+32 (96), it is handled as a normal key (including the "raw:" prefix).
  5. If the hex encoding of key and/or salt is erroneous, the key is handled as a normal key (including the "raw:" prefix).
  6. Specifying the salt together with the key makes only sense when creating a new database. For an existing database the salt stored in the database header is used and the specified salt is silently ignored, even if it is different from the stored salt.

I'm fine with the behaviour as implemented. However it should be documented, so that users are aware of it.

Any way to compile on MacOS?

$gcc sqleet.c shell.c -o sqleet -lpthread -ldl
sqleet.c:229304:9: warning: 'syscall' is deprecated: first deprecated in macOS
      10.12 - syscall(2) is unsupported; please switch to a supported interface.
      For SYS_kdebug_trace use kdebug_signpost(). [-Wdeprecated-declarations]
    if (syscall(SYS_getentropy, buf, n) == 0)
        ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/unistd.h:742:6: note: 
      'syscall' has been explicitly marked deprecated here
int      syscall(int, ...);
         ^
1 warning generated.

[Fixed] encryption on Android not working

Hi,

i try to get this to work on Android (Pragma key doesn't encrypt the database).
I followed https://www.sqlite.org/android/doc/trunk/www/install.wiki#buildnative and downloaded sqlite, got the android project, had to upgrade gradle .... etc.
I exchanged the sqlite3.c and sqlite3.h files with the sqleet files (renamed them) from sqleet-v0.31.1-amalgamation.zip.

Then i was able to build the .aar file and included it into my project like shown here https://www.sqlite.org/android/doc/trunk/www/verysimpleapp.wiki.

Now i want to get the encryption working.
I use this code

//load the library
System.loadLibrary("sqliteX")
//read the database
val database = SQLiteDatabase.openOrCreateDatabase(getDatabasePath("sqleet.db"), null)
//set the encryption key
 database.execSQL("PRAGMA key='swordfish';")
//create table and insert data ...

My issue is, that the database is not encrypted and i'm not sure if my compiled library is not working, not loaded properly or if i am missing something here.

Edit:
SQLiteDatabase did not use the correct namespace, it's (obv) necessary to use the namespace of the library.

Leverage libsodium?

Looks like a really nice library!

I forwarded this to a colleague who is an experienced user of sqlcipher, and his feedback was:

ChaCha20 and Poly1305 are both provided by the well-regarded libSodium library, so that could be used instead of the implementations here

Compile fails on Windows with MSYS2/MinGW

Sqleet compiles fine on a Linux box, but I've had no such luck on Windows. I am trying to compile the current v0.28.0 release from the amalgamation download with MSYS2/MINGW32 on a Windows 10 machine.

$ gcc sqleet.c shell.c -o sqleet
sqleet.c:223659:10: schwerwiegender Fehler: sys/syscall.h: No such file or directory
#include <sys/syscall.h>
^~~~~~~~~~~~~~~
Kompilierung beendet.

By way of comparison, the corresponding v3.28 SQLite amalgamation compiles without problems:

$ gcc sqlite3.c shell.c -o sqlite.exe
$ ./sqlite.exe -version
3.28.0 2019-04-16 19:49:53 (...)

I am not very experienced in compiling C code, so I am stuck here. Are there any preconditions to be met that are not mentioned on the website? I surely hope for an easy solution without elaborate steps ;-)

Cannot set key again if having removed key

I am unable to set key again if I Set Key then remove key. Then open the DB again with no key, then neither SetKey or ReKey will work again. (Key is successfully removed as DB Browser for Sqlite can open it at that time)

A strange bug with rekey.

I am using PRAGMA to call the rekey function.

After decrypting the database with pragma rekey='';, the following pragma rekey='somenewkey'; almost always fails.

In order to encrypt the database again, I must reopen the database, or to execute pragma rekey=''; several times. After one of these, the following pragma rekey='somenewkey' successfully encrypts the database.

On encrypted database pragma rekey='' always successfully decrypts the database.

On all these commands there is no error returned by sqlite3_step(). The only difference is that the successful rekey (with nonempty key) returns SQLITE_ROW and the unsuccessful returns SQLITE_DONE.

On the other hand, rekey with empty key (decrypt database) always return SQLITE_DONE, but still decrypts the database.

Also the bug is related on the decrypt operation (rekey with empty string value). When calling rekey for re-encrypting the database with new key, it always success.

Database size limit

Is this limited to the size of the database?I have a database of 5M encrypted can not be opened, less than 1M encrypted can be opened

Releasing reserved bytes on decrypting

When a not-encrypted database is encrypted, your code uses SQLite's function sqlite3RunVacuum to reserve the bytes per page required for storing nonce and authentication, and to implicitly perform the encryption.

In principle the same procedure is used to decrypt an encrypted database. However, your code shows the below comment and does not touch the reserved space.

/* Truncating the reserved space is dangerous (may corrupt the DB)
   sqlite3BtreeSetPageSize(pBt, sqlite3BtreeGetPageSize(pBt), 0, 0); */

Have you actually experienced database corruption on trying to remove the reserved space on decrypting? If yes, under which circumstances?

For a test I uncommented the call to sqlite3BtreeSetPageSize, but it had no effect at all. The decrypted database still had 32 bytes of reserved space per page.

Inspecting the code of the SQLite function sqlite3RunVacuum the reason gets clear: the reserved space is determined by a call to sqlite3BtreeGetOptimalReserve. And the strategy seems to be to never decrease the number of reserved bytes.

Experimentally, I copied the code of function sqlite3RunVacuum into file sqleet.c, renamed the function, and applied a small modification (namely passing the value nRes as a parameter instead of determining it with sqlite3BtreeGetOptimalReserve). Then I modified your function sqlite3_rekey_v2 to call the new function with 0 on decrypting and 32 on encrypting for the parameter nRes. At least in my experiments I did not experience any problems.

Would you consider to apply this change?

Fatal signal 11 after set rekey for unencrypted database

Hello!
When using Sqleet in Sqlite, I encountered the problem of encrypting the database through PRAGMA rekey=NewPassword. Occurs when I import an unencrypted database and try to set a new password for it (the old password is empty). Drops on next error:

A/libc: Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c69ef8fb0 in tid 24406 (AsyncTask #2), pid 24259

/**
 * Changing the database password 
 * */
public void changeDatabaseEncryptionPassword(String password) throws Exception {
    try {
        // Encrypts open password
        String encryptionPassword = encryptPassword(password);
        SQLiteDatabase db = getWritableDatabase(); // 17Mb of unencrypted database 
        db.execSQL("PRAGMA rekey = " + DatabaseUtils.sqlEscapeString(encryptionPassword)); // the problem is here 
    } catch (Exception e) {
        throw new Exception(e);
    }
}

@Override
public void onConfigure(SQLiteDatabase db) {
    String encryptionPassword = getEncryptionPassword(currentContext);
    db.execSQL("PRAGMA key = " + DatabaseUtils.sqlEscapeString(encryptionPassword));
    super.onConfigure(db);
}

In general, I am satisfied with the library. The database can be either encrypted or decrypted by specifying an empty password. And for databases with low weight, everything works both in the direction of encryption and decryption.
Encrypt db

Questions arise:

  1. What could this problem be related to and how to fix it?
  2. Are there any restrictions on the size of the encrypted database? In some cases, the process of encrypting and decrypting the database goes without problems. And when I import an unencrypted database backup and try to encrypt it, it crashes on an error "Fatal signal 11..."
  3. Is there enough RAM for the Android device to perform this operation, especially for weighty databases?
  4. Is there a way to encrypt / decrypt large databases (several gigabytes) at the file structure level by reading them piece by piece?

Bug in function sqlite3_rekey_v2

When you use the command PRAGMA rekey='password' to encrypt a previously unencrypted database, this seems to work at first. However, the function sqlite3_rekey_v2 misses to set the codec member variable reader to the value of the codec member variable writer after sqlite3RunVacuum has finished. And that can lead to problems later on, if SQLite has to reread a page from disk. This will not work, because the reader is still a null pointer, so that it is assumed that the database is not encrypted, although pages were written to file encrypted.

Since SQLite caches database pages, the bug does not surface immediately, but you can demonstrate it with the following SQL commands:

create table t1 (c1 int, c2 char);
insert into t1 values (1, 'Arthur');
pragma rekey='Test';
attach database 'db_attached.db3' as db2; -- uses the encryption key of the main database
create table db2.t2 (c1 int, c2 char);
insert into db2.t2 values (2, 'Bernie');
detach database db2;
-- reattaching the database forces SQLite to read the schema from disk
attach database 'db_attached.db3' as db2; -- uses again the encryption key of the main database
-- ==>Error: unable to open database: db_attached.db3

Argon2

https://en.wikipedia.org/wiki/Argon2
Argon2 is a key derivation function that was selected as the winner of the Password Hashing Competition in July 2015. ... Argon2 is released under a Creative Commons CC0 license, and provides three related versions:

  • Argon2d maximizes resistance to GPU cracking attacks. It accesses the memory array in a password dependent order, which reduces the possibility of time-memory trade-off (TMTO) attacks, but introduces possible side channel attacks.
  • Argon2i is optimized to resist side-channel attacks. It accesses the memory array in a password independent order.
  • Argon2id is a hybrid version. It follows the Argon2i approach for the first pass over memory and the Argon2d approach for subsequent passes. The Internet-Draft[4] recommends using Argon2id except when there are reasons to prefer one of the other two modes.

All three modes allow specification by three parameters that control: (1) execution time (2) memory required (3) degree of parallelism

Future plans (for v1.0 due to the compatibility break) include switching to Argon2id key derivation function.

Question: Would it be possible to make the key salt accessible?

While I analyzed the encryption scheme of SQLCipher, I found out that SQLCipher stores the key salt in the first 16 bytes of the database header just as sqleet. However, SQLCipher does not include the key salt in the HMAC calculation of database page 1, while sqleet does.

In the end it seems to be irrelevant whether the key salt is included in the HMAC calculation or not. If the key salt bytes in the first 16 bytes of the database file are modified - either accidentally or on purpose -, this will make the database file inaccessible.

The problem I see is that there is almost no chance to recover a database corrupted in such a way, since the user only knows the passphrase he used, but he does not know the random key salt.

IMHO it would make sense to implement an option to access the generated key salt, so that it could be stored in a safe place, and/or to let the user set a specific key salt instead of generating a random one.

This would allow to create a tool to recover a corrupted encrypted database - at least partially.

SQLeet manifests itself as SQLite. Is it intentional?

In SQLeet, the string on sqlite3_version and sqlite3_sourceid() returns the version and commit ID of the SQLite, not of SQLeet.

Is this behavior intentional? Is there a way for the application to check and display the version of SQLeet?

Provide an easy way to compile with official sqlite3 amalgamation

The README states:

To use sqleet as a library, the recommended way is to download a preconfigured release package instead of cloning the master. The contained sqleet.c and sqleet.h files are drop-in replacements for the official sqlite3.c amalgamation and sqlite3.h header.

However, I want to use my own custom amalgamation (some compile-time options must be provided when generating the amalgamation itself).

The official SEE extension makes it very easy to turn a regular amalgamation into one that supports encryption:

  1. prepend see-prefix.txt
  2. append see.c

Could there be a simple way to do the same thing using sqleet?

Release 0.26.0 is not pre amalgamated

The 0.26.0 releases archives (zip and tarball) are not pre "amalgamated" ...

In previous releases, the downloaded content was ready to be used.

Is this something desired or an issue ?
If this is desired, then the README should be also updated accordingly ("Compiling" paragraph)

To use sqleet as a library, the recommended way is to download a preconfigured release package instead of cloning the master. The contained sqleet.c and sqleet.h files are drop-in replacements for the official sqlite3.c amalgamation and sqlite3.h header. The C interface of the sqleet encryption extension is described in section SQLite3 encryption API.

PS: Thanks for this nice piece of work !

Anyway to fix broken encrypted database?

I used a better-sqlite3-sqleet node package for encrypt database.
It's working well. But sometimes some of my user's DB is unexpectedly broken.
Is there anyway to fix a sqleet encrypted broken DB?

I could open unbroken DB with same key. (I changed some part of the key for security below)

./sqleet.exe ./my.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> PRAGMA key='AcQTjWnZy/B?E(hYq3t6waaaaABAH+AbPeS9z$C&F)J@r4uPdSgU7x!XP*G-';
ok
sqlite> SELECT * FROM config;
1|1|1|||0|0||1

But broken database can not open:

./sqleet.exe ./my-2.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> PRAGMA key='AcQTjWnZy/B?E(hYq3t6waaaaABAH+AbPeS9z$C&F)J@r4uPdSgU7x!XP*G-';
sqlite> SELECT * FROM config;
Error: file is not a database
sqlite> .quit

./hexdump.exe -C ./my-2.db
000000  47 ec ee 33 4c cf e5 c1 df fe fe a4 cf 20 f1 eb  G..3L........ ..
000010  00 01 3f 78 9d ac 3b ba 0b d6 fe cb 66 31 a2 8c  ..?x..;.....f1..
000020  51 57 51 f3 93 8e 0d e3 bf d4 c7 4c db 28 6a ae  QWQ........L.(j.
000030  16 34 f3 98 20 fc d6 02 a8 e5 b9 59 23 9c 11 ac  .4.. ......Y#...
000040  e3 c4 de 2c 45 a0 e7 45 0c d1 30 ad 85 eb 9b f4  ...,E..E..0.....
000050  1d 0d 19 d8 de d6 22 30 52 cf 71 dd 13 f4 eb b0  ......"0R.q.....
000060  ce 3f 86 25 49 ff 6c 4a cb 6a aa dd 4c 6f 94 95  .?.%I.lJ.j..Lo..
000070  b0 70 bf 83 71 2d ac 02 53 00 08 55 6c 3f 4a 11  .p..q-..S..Ul?J.
000080  08 64 95 f2 36 e9 16 8e 4e 45 74 91 10 bb 9f a2  .d..6...NEt.....
000090  0f 78 dd b2 34 39 53 16 95 39 90 c3 9b 3a e6 e1  .x..49S..9...:..
0000a0  88 94 c4 1b f4 8f 80 6a 2c 68 c7 93 45 5b 47 09  .......j,h..E[G.
0000b0  30 cf cd 3f 1d 0a 5c 2b 26 6b 28 17 aa 7c 55 a5  0..?..\+&k(..|U.
0000c0  1e 27 05 e2 ff 1e f7 0b c9 95 81 be f0 1c b1 74  .'.............t
0000d0  2c af 70 69 73 38 96 e9 d1 f4 98 ac 5b f7 c3 26  ,.pis8......[..&
0000e0  97 46 7d 37 1c be 42 fa 4c 0a 37 2c 3e b0 99 83  .F}7..B.L.7,>...
0000f0  71 a6 45 9d bd 7f 74 82 1f ef e9 c3 95 a1 91 b0  q.E...t.........
000100  0d 8b 58 97 8e 79 93 4b eb b8 4b 98 55 88 d0 74  ..X..y.K..K.U..t
000110  cd 9e a6 71 c4 c7 fa 6f 18 f4 41 d9 06 39 52 ed  ...q...o..A..9R.
000120  52 75 38 00 9b d2 cb 02 51 a8 25 93 4c 00 f7 25  Ru8.....Q.%.L..%
000130  8b d0 04 77 57 37 c0 50 42 96 a9 67 cd 1b 19 9e  ...wW7.PB..g....
000140  2c 24 48 06 2c b3 19 d5 80 b9 15 79 65 8f 54 71  ,$H.,......ye.Tq
000150  08 93 57 5a f2 6c 4c 46 7f 3e 05 4e 32 0a 7a 32  ..WZ.lLF.>.N2.z2
000160  f9 e5 20 f7 57 9b 25 78 61 0b 25 ec 7c f6 32 58  .. .W.%xa.%.|.2X
000170  0d 13 53 24 a5 d3 d1 c6 42 93 45 7e 28 8b 91 a1  ..S$....B.E~(...
000180  00 c6 16 0f c5 46 d7 63 f4 1d 03 98 b9 17 7a a5  .....F.c......z.
000190  ed 3b c8 fc ef cc 3a 4c d1 13 64 28 37 52 d7 01  .;....:L..d(7R..
0001a0  02 1e 84 79 4e 2d 37 39 35 41 06 fd 5b 9c 80 ee  ...yN-795A..[...
0001b0  76 2c 1a c3 03 f8 51 12 45 7b 5c 1a d9 93 e8 cd  v,....Q.E{\.....
0001c0  ec 6a 39 e3 4f f1 57 7b 06 2c a6 0b 10 b9 d3 22  .j9.O.W{.,....."
0001d0  48 b1 09 28 3c 88 85 5f 86 ca 95 c2 b6 c9 6d 15  H..(<.._......m.
0001e0  f5 fb 08 f1 db 6c d9 9f 2e 0a b5 01 1b d4 5e ab  .....l........^.
0001f0  4a e5 18 f1 69 25 25 05 c0 f0 c7 04 fd a5 2e d5  J...i%%.........
000200  ee e7 c6 52 0d 3e 26 2b a3 42 2d ca e3 c6 fb 7c  ...R.>&+.B-....|
000210  b0 c5 02 85 d2 67 c5 28 d9 11 6d dd 19 7f 83 f8  .....g.(..m.....
000220  aa 15 2d 4e a2 72 59 11 85 a2 d1 ed 80 0a 49 7e  ..-N.rY.......I~
000230  05 54 c0 50 98 3c 69 cc b9 5b c7 5c 5b dc 59 e6  .T.P.<i..[.\[.Y.
000240  ac f4 09 23 b7 4d dd 6c a7 a0 3e 56 fb 43 88 e1  ...#.M.l..>V.C..
000250  53 b6 06 a5 06 aa 2e 36 33 9d fc 2c bd 55 e4 8a  S......63..,.U..
000260  ac 23 4d 99 04 c4 8d bd f6 fc 14 3a 44 d4 8a 10  .#M........:D...
000270  7a d5 83 19 87 1b e1 55 9f ec 13 2a 1a 71 6d 4b  z......U...*.qmK
000280  68 d2 74 9e 59 7b 9b fd 0c 1f 90 ed 20 80 5f 2d  h.t.Y{...... ._-
...

Didn't run rekey again. Then it is not a key problem. After some insert query DB seems to broke.
Please help me.

Handling of URI parameters

Sorry that I give rather late feedback regarding the new URI parameter feature of sqleet.

  1. Handling of erroneous URI parameter values

You decided to use the return code SQLITE_MISUSE to signal the use of erroneous URI parameter values. Looking at the SQLite documentation, I'm not convinced that using this return code is really the best choice:

The SQLITE_MISUSE return code might be returned if the application uses any SQLite interface in a way that is undefined or unsupported. For example, using a prepared statement after that prepared statement has been finalized might result in an SQLITE_MISUSE error.

SQLite tries to detect misuse and report the misuse using this result code. However, there is no guarantee that the detection of misuse will be successful. Misuse detection is probabilistic. Applications should never depend on an SQLITE_MISUSE return value.

If SQLite ever returns SQLITE_MISUSE from any interface, that means that the application is incorrectly coded and needs to be fixed. Do not ship an application that sometimes returns SQLITE_MISUSE from a standard SQLite interface because that application contains potentially serious bugs.

Especially the last paragraph indicates that a shipped application should never happen to experience a return code SQLITE_MISUSE. However, this is difficult to accomplish, because the valid range of parameters could potentially change from version to version and there is no interface available to query the allowed range in advance, so that the application could check user input and only pass valid data to the interface.

Unfortunately, using another return code (for example, SQLITE_ERROR or SQLITE_CANTOPEN) wouldn't solve the problem either. IMHO the main problem is that no detailed error diagnostics are available, that is, there is no indication which URI parameter(s) is(are) in error.

Maybe one idea could be to add appropriate calls to sqlite3_log, thus giving an application a chance to get a more detailed information.

In my own implementation I chose to silently ignore invalid URI parameters. However, I admit that I'm not happy with that approach either.

One major problem in SQLite itself is that sqlite3_key_v2 is called internally in some cases (for example, if the URI parameter key is used, or if an encrypted database is attached), but its return code is silently ignored. That is, the application will not be informed properly, if anything goes wrong during initializing the codec.

  1. Naming of URI parameters

Of course you are free to name the URI parameters used in sqleet as you please. However, in my own encryption extension (which handles various different ciphers) it makes things harder to understand and use. For example, what you called salt for sqleet is named cipher_salt for SQLCipher; and while you offer 2 variation of the salt parameter (namely, salt and hexsalt), SQLCipher only accepts hex values - and IMHO this makes sense, because the salt is usually a binary value which is difficult to pass as an URI parameter in non-hex form anyway.

Another problem can arise from name clashes, because any SQLite extension may allow configuration parameters via URI parameters. That's the main reason why I decided to silently ignore URI parameters which do not provide valid values for the encryption extension: they might belong to another extension, for which the given values are acceptable. At the moment I have no conclusive idea how this problem could or should be tackled.

  1. URI parameter salt vs header

I have to admit that I have not yet fully understood the implications of specifying both parameters, salt and header, or just one of them. Could you please elaborate in greater detail how it is meant to use these 2 parameters in sqleet? Thanks.

Public License Fallback

Thanks for your great work and interesting choice of cipher. Even more impressive to release it to the public domain and make it so open!

There was an interresting thread on reddit about the problem in countries where there is no concept of public domain. Please spare others the legal uncertainty and add a permissive fallback license,
like the one in clause 3 of creative commons 0 public domain

  1. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.

~ kwinz

compilation error on OSX

$ gcc sqleet.c shell.c -o sqleet

In file included from sqleet.c:7:0:
crypto.c:531:2: error: #error "Secure pseudorandom number generator unimplemented for this OS"
 #error "Secure pseudorandom number generator unimplemented for this OS"
  ^~~~~
crypto.c: In function 'chacha20_rng':
crypto.c:547:21: warning: implicit declaration of function 'entropy'; did you mean 'encrypt'? [-Wimplicit-function-declaration]
                 if (entropy(key, sizeof(key)) != sizeof(key))
                     ^~~~~~~
                     encrypt

output of gcc -v

Using built-in specs.
COLLECT_GCC=gcc-7
COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc/7.2.0/libexec/gcc/x86_64-apple-darwin16.7.0/7.2.0/lto-wrapper
Target: x86_64-apple-darwin16.7.0
Configured with: ../configure --build=x86_64-apple-darwin16.7.0 --prefix=/usr/local/Cellar/gcc/7.2.0 --libdir=/usr/local/Cellar/gcc/7.2.0/lib/gcc/7 --enable-languages=c,c++,objc,obj-c++,fortran --program-suffix=-7 --with-gmp=/usr/local/opt/gmp --with-mpfr=/usr/local/opt/mpfr --with-mpc=/usr/local/opt/libmpc --with-isl=/usr/local/opt/isl --with-system-zlib --enable-checking=release --with-pkgversion='Homebrew GCC 7.2.0' --with-bugurl=https://github.com/Homebrew/homebrew-core/issues --disable-nls
Thread model: posix
gcc version 7.2.0 (Homebrew GCC 7.2.0)

Cannot open SQLCiper database

I believe DB Browser for SQLite is able to open a SQLCipher databse however I am unable to find where to apply the database Password.

The database opens fine in SQLiteStudio however I would prefer to use DB Browser for SQLite if possible.

I am just getting a messsage - Could not open database file Reason: file is not a database.

Thanks

sqleet v1 cryptosystem

A draft of a new cryptosystem for the next major version of sqleet. The current sqleet v0 cryptosystem is strong and the goal here is improving secure enclave (TPM/SGX/TrustZone) support and reducing the security impact of nonce reuse attacks (if an attacker obtains a Poly1305 key of a single page somehow, the attacker can reuse the key and nonce to compromise the security of other database pages). See the description below for more details.

Warning: This is just a draft and subject to changes. I'm still in the process of getting the proposed cryptosystem verified by "professionals", so some changes are expected.

Encrypted database page format
==============================

  +----------------------------------------+----------------+----------------+
  | (n - 16 - 16)-byte Page data           | 16-byte Nonce  | 16-byte MAC    |
  +----------------------------------------+----------------+----------------+
     n = 512, 1024, 2048, ... (page size)  | Reserved area of 32 bytes total |


Old implementation (sqleet v0.x.y)
==================================

Basic ChaCha20-Poly1305 AEAD construction using 128-bit random nonces.
Basically, RFC7539 with minor modifications

    EncryptV0(K, PageNumber, Data[0..n])
    |
    | Nonce = randombytes(16)
    | K_stream, K_mac = ChaCha20(K, Nonce ^ PageNumber)
    | Data ^= ChaCha20(K_stream, (Nonce ^ PageNumber) + 1)
    | MAC = Poly1305(K_mac, Data || Nonce)
    |
    | Output: (Data[0..n-32], Nonce[0..16], MAC[0..16])

    DecryptV0(K, PageNumber, Data[0..n-32], Nonce[0..16], MAC[0..16])
    |
    | K_stream, K_mac = ChaCha20(K, Nonce ^ PageNumber)
    | Verify MAC = Poly1305(K_mac, Data || Nonce)
    | Data ^= ChaCha20(K_stream, (Nonce ^ PageNumber) + 1)
    |
    | Output: Data[0..n-32]

Run-time cost: (n + 1) × ChaCha20 + n × Poly1305 blocks in total.

List of known (theoretical) issues:

 1. Random nonces allow nonce reuse attacks

    - Preventing nonce reuses is infeasible, in practice, with random nonces.

    - Exploitation can be detected by checking the nonce values of a database
      file. Non-malicious nonces are uniform-randomly distributed.

 2. C implementations of Poly1305/ChaCha20 may leak one-time K_mac & K_stream
    keys onto the stack. ChaCha20(K, Nonce) cannot leak master key K because the
    following ChaCha20(K_stream, ...) overwrites stack memory and registers.

 3. If Poly1305 leaks K_mac (by whatever means; let's say via a side channel,
    bad RNG or direct process memory inspection), then the attacker can generate
    valid Poly1305 MACs for arbitrary messages by reusing the nonce and K_mac.

    - The ability to produce valid MACs allows bit-flipping bits in plaintext
      by changing the corresponding ciphertext ("stream cipher attack").

    - K_mac is not properly page-specific to prevent nonce & K_mac reuse across
      database pages. Let's force page j to reuse a leaked K_mac from page i:

      +----------------------------------------------------------------------+
      | Page i with nonce N:                                                 |
      | K_stream, K_mac = ChaCha20(K, N ^ i)                                 |
      +----------------------------------------------------------------------+
      | Page j with nonce N ^ i ^ j:                                         |
      | K_stream, K_mac = ChaCha20(K, (N ^ i ^ j) ^ j) = ChaCha20(K, N ^ i)  |
      +----------------------------------------------------------------------+

      In other words, the nonce N and K_mac of page i can be reused at page j by
      with the adjusted nonce value N ^ i ^ j. Notice K_stream is reused also.

    - In conclusion, a single *one-time* key K_mac is sufficient to break the
      whole cryptosystem. This is poor design, despite the fact that attacker
      obtaining K_mac is only a theoretical threat and a very unlikely attack
      scenario in practice. Requires Heartbleed type of a information disclosure
      vulnerability but in stack memory (more powerful exploits such as code
      execution exploits can already bypass the encryption by other means).

    - K_stream, if leaked, compromises only the corresponding database page.
      This is correct and expected behavior since *one-time* keys are limited
      and not intended to be powerful enough to break the full cryptosystem.

    - K (master key) leak would be devastating to the cryptosystem, obviously.
      There is nothing we can do about this, given the definition of master key.

  4. ChaCha20 streams of distinct pages overlap (with negligible probability) if
     randomly generated nonces are within distance (page_size / 64) - 1 from
     each other. XOR between the ciphertexts encrypted with the overlapping
     stream segment reveals the XOR of the associated plaintexts, breaking the
     confidentiality of data.


New cryptosystem (sqleet v1.x.y)
================================

ChaCha20-Poly1305 AEAD construction with deterministic nonces. Draft!

    EncryptV1(K, PageNumber, Data[0..n]):
    |
    | K_i, K_j = ChaCha20(K, PageNumber)
    | Hash = Poly1305(K_i, Data)
    | Nonce = Hash ^ randombytes(16)
    | K_stream, K_mac = ChaCha20(K_j, Nonce)
    | Data ^= ChaCha20(K_stream ^ K_i, 0)
    | MAC = Poly1305(K_mac, Hash)
    |
    | Output: (Data[0..n-32], Nonce[0..16], MAC[0..16])

    DecryptV1(K, PageNumber, Data[0..n-32], Nonce[0..16], MAC[0..16])
    |
    | K_i, K_j = ChaCha20(K, PageNumber)
    | K_stream, K_mac = ChaCha20(K_j, Nonce)
    | Data ^= ChaCha20(K_stream ^ K_i, 0)
    | Hash = Poly1305(K_i, Data)
    | Verify MAC = Poly1305(K_mac, Hash)
    |
    | Output: Data[0..n-32]

Run-time cost: (n + 2) × ChaCha20 + (n + 1) × Poly1305 blocks in total.

Improvements:

 1. Page subkeys K_i, K_j allow a secure enclave implementation with SGX/TPM/TZ
    technologies (derive subkeys from K within a hardware-enforced safe space).
    Probably not going to be implemented in sqleet, but a nice option to have.

 2. Deterministic hash-based nonces prevents nonce reuse attacks.

 3. One-time keys K_stream & K_mac are truly one-time and, if leaked, compromise
    only the associated page and nonce/data combination.

 4. C implementations (most likely) do not leak master key or subkeys via stack.
    Subsequent ChaCha20/Poly1305 function calls overwrite secrets spilled to the
    stack before exiting the function. Only K_stream & K_mac can be exposed, but
    they are truly one-time keys and compromise only the current page and data.

 5. Power/EM side-channel resistance (in addition to the usual time and cache
    side-channel resistance). Published side-channel attacks against ChaCha20
    require an attacker-controlled nonce, making ChaCha(K_j, Nonce) vulnerable.
    To mitigate the security impact of leaked K_j, we use K_stream ^ K_i as the
    ChaCha20 encryption key (if the attacker knows K_j, she can also calculate
    K_stream). Power/EM side-channel attacks do not expose K_i because it is
    derived from K with non-attacker-controlled nonce (PageNumber) and never
    used with attacker-controlled nonce.

    Power/EM attacks are not my area of expertise, so the reasoning above may be
    flawed. The biggest assumption is that Poly1305 is secure against power and
    EM side-channel attacks, which is true to my limited understanding.

 5. MAC-then-Encrypt is fine in this particular context.

Disadvantages (in comparison to old version):

 1. Requires additional ChaCha20 and Poly1305 block computations.

 2. Complexity. Ugly. Not battletested nor approved by crypto wizards (yet).

Any feedback is appreciated.

compile error libsqleet

ubuntu 18.04
gcc build sqleet into libsqleet.so (sqleet version : sqleet-v0.31.1-amalgamation.zip)
but error:
./libsqleet.so: undefined reference to dlopen' ./libsqleet.so: undefined reference to pthread_mutexattr_settype'
./libsqleet.so: undefined reference to dlclose' ./libsqleet.so: undefined reference to pthread_mutex_trylock'
./libsqleet.so: undefined reference to dlerror' ./libsqleet.so: undefined reference to dlsym'
./libsqleet.so: undefined reference to pthread_join' ./libsqleet.so: undefined reference to pthread_create'
./libsqleet.so: undefined reference to pthread_mutexattr_init' ./libsqleet.so: undefined reference to pthread_mutexattr_destroy'
i dont why.

Rekey example not working in 3.30.0 release

I was updating my project's sqleet to 3.30.0 and noticed that the sqlite3_rekey function was no longer able to rekey my database. I was getting SQLITE_MISUSE back when attempting to rekey the database.

Dug around a bit and haven't been able to find the cause of the issue, but I have also found that the example in your readme no longer works:

buchmanj@Accio ~/D/sqleet-v0.30.0> hexdump -C test.sqlite
00000000  53 51 4c 69 74 65 20 66  6f 72 6d 61 74 20 33 00  |SQLite format 3.|
00000010  10 00 01 01 00 40 20 20  00 00 00 01 00 00 00 02  |.....@  ........|
00000020  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 04  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 01  |................|
00000060  00 2e 3b f0 0d 00 00 00  01 0f d4 00 0f d4 00 00  |..;.............|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000fd0  00 00 00 00 2a 01 06 17  15 15 01 39 74 61 62 6c  |....*......9tabl|
00000fe0  65 74 65 73 74 74 65 73  74 02 43 52 45 41 54 45  |etesttest.CREATE|
00000ff0  20 54 41 42 4c 45 20 74  65 73 74 20 28 69 64 29  | TABLE test (id)|
00001000  0d 00 00 00 00 10 00 00  00 00 00 00 00 00 00 00  |................|
00001010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00002000
buchmanj@Accio ~/D/sqleet-v0.30.0> ./sqleet test.sqlite
SQLite version 3.30.0 2019-10-04 15:03:17
Enter ".help" for usage hints.
sqlite> PRAGMA rekey='swordfish';
sqlite> .quit
buchmanj@Accio ~/D/sqleet-v0.30.0> hexdump -C test.sqlite
00000000  53 51 4c 69 74 65 20 66  6f 72 6d 61 74 20 33 00  |SQLite format 3.|
00000010  10 00 01 01 00 40 20 20  00 00 00 01 00 00 00 02  |.....@  ........|
00000020  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 04  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 01  |................|
00000060  00 2e 3b f0 0d 00 00 00  01 0f d4 00 0f d4 00 00  |..;.............|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000fd0  00 00 00 00 2a 01 06 17  15 15 01 39 74 61 62 6c  |....*......9tabl|
00000fe0  65 74 65 73 74 74 65 73  74 02 43 52 45 41 54 45  |etesttest.CREATE|
00000ff0  20 54 41 42 4c 45 20 74  65 73 74 20 28 69 64 29  | TABLE test (id)|
00001000  0d 00 00 00 00 10 00 00  00 00 00 00 00 00 00 00  |................|
00001010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00002000
buchmanj@Accio ~/D/sqleet-v0.30.0>

Am I doing something wrong?

Sqleet Shell's URI Interface miss(behavior) across different platforms & compilers

This post refers to the URi Interface as handled by the Sqleet shell. Based on my preliminary tests, plugging Sqleet in my apps leads to a bit more robust behavior, although there too the URI Interface fails to encrypt databases if a path is supplied on Windows (if I remember correctly MSVC 2017 64 bit builds show that behavior, while MinGW builds work properly). But that is a subject of a different post.

The binaries used for testing are built with Qt 5.12.7 LTS on Windows, Linux and Mac and are available here.

If you wish to build them from source yourself, the project files are available here .

For Windows you will need :

  • Qt, installer available here. Expand Qt 5.12.7 component and select: MSVC 2017 32-bit, MSVC 2017 64-bit, MinGW 7.3.0 32-bit and MinGW 7.3.0 64-bit kits.
  • Build Tools for Visual Studio 2017 (version 15.9) available for download on Microsoft website. Alternatively you can use the installer available on the release page together with the test binaries. Run the installer and check the box Visual C++ build tools to install them.

For Mac you will need :

  • Qt, installer available here. Expand Qt 5.12.7 component and select: macOS kit;
  • XCode + Command Line Tools Package (starting XCode for the first time will install these);

For Linux you will need :

  • Qt, installer available here. Expand Qt 5.12.7 component and select: Linux gcc 64-bit kit;
  • GCC compiler

Note: on Windows, if you wish to use Format-Hex cmdlet with -Count parameter you will need Power Shell 7. Start Power Shell and run iex "& { $(irm https://aka.ms/install-powershell.ps1) } -UseMSI" to install Power Shell 7.

Summary of observations:

  • Linux GCC 64 bit build : well behaved;
  • Windows MinGW 32 bit build: broken;
  • Windows MinGW 64 bit build: well behaved;
  • Windows MSVC 2017 32 bit: misbehaved;
  • Windows MSVC 2017 64 bit: misbehaved;
  • MacOS CLANG 64 bit: misbehaved;

The meat & potatoes:

Sqleet Windows 32 bit build @ MSVC 2017

PS C:\Temp\bin> .\sqleet0311-win32-x86-msvc1916.exe 'file:ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex .\ios.db -Count 64


   Label: C:\Temp\bin\ios.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04            �   �
0000000000000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00            �

PS C:\Temp\bin> del .\ios.db
PS C:\Temp\bin> .\sqleet0311-win32-x86-msvc1916.exe 'file:same_folder_32.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex .\same_folder_32.db -Count 64


   Label: C:\Temp\bin\same_folder_32.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04            �   �
0000000000000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00            �

PS C:\Temp\bin> del .\same_folder_32.db
PS C:\Temp\bin> .\sqleet0311-win32-x86-msvc1916.exe 'file:/C:/Temp/sql/ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex -Path C:/Temp/sql/ios.db -Count 64


   Label: C:\Temp\sql\ios.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04            �   �
0000000000000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00            �

PS C:\Temp\bin> del C:/Temp/sql/ios.db
PS C:\Temp\bin> .\sqleet0311-win32-x86-msvc1916.exe 'file:/C:/Temp/sql/different_path_32.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex -Path 'C:/Temp/sql/different_path_32.db' -Count 64


   Label: C:\Temp\sql\different_path_32.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04            �   �
0000000000000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00            �

PS C:\Temp\bin> del C:/Temp/sql/different_path_32.db

Conclusion(s):

  • For Sqleet x86 built with MSVC2 2017, the URI Interface fails to encrypt the database in all tested scenarios;
  • PRAGMA rekey works correctly for all tested scenarios;

Sqleet Windows 64 bit build @ MSVC 2017

PS C:\Temp\bin> .\sqleet0311-win32-x86_64-msvc1916.exe 'file:ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex .\ios.db -Count 64


   Label: C:\Temp\bin\ios.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 20 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 FB B1 B9 EF 1E 59 69 2E 29 C7 83 3E A6 EB E6 8C û±¹ï�Yi.)Ç�>¦ëæ�
0000000000000030 7A 81 88 B6 C0 19 52 09 2E 38 4A 2D 24 2F 7F CB z��¶À�R�.8J-$/�Ë

PS C:\Temp\bin> del .\ios.db
PS C:\Temp\bin> .\sqleet0311-win32-x86_64-msvc1916.exe 'file:same_folder_64.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex .\same_folder_64.db -Count 64


   Label: C:\Temp\bin\same_folder_64.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04            �   �
0000000000000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00            �

PS C:\Temp\bin> del .\same_folder_64.db
PS C:\Temp\bin> .\sqleet0311-win32-x86_64-msvc1916.exe 'file:/C:/Temp/sql/ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex -Path 'C:/Temp/sql/ios.db' -Count 64


   Label: C:\Temp\sql\ios.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04            �   �
0000000000000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00            �

PS C:\Temp\bin> del C:/Temp/sql/ios.db
PS C:\Temp\bin> .\sqleet0311-win32-x86_64-msvc1916.exe 'file:/C:/Temp/sql/different_path_64.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex -Path 'C:/Temp/sql/different_path_64.db' -Count 64


   Label: C:\Temp\sql\different_path_64.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04            �   �
0000000000000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00            �

PS C:\Temp\bin> del C:/Temp/sql/different_path_64.db

Conclusion(s);

  • For Sqleet x86_64 built with MSVC2 2017, the URI Interface encrypts the db if it is created in the same folder as the exe ;
  • If a PATH is supplied OR the file name contains non-letter characters, the encryption fails;
  • PRAGMA rekey works correctly for all tested scenarios;

Sqleet Windows 32 bit build @ MINGW 7.3.0

PS C:\Temp\bin> .\sqleet0311-win32-x86-mingw730.exe 'file:ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
PS C:\Temp\bin> 

Conclusion(s):

  • Broken build, the application exists immediately;

Sqleet Windows 64 bit build @ MINGW 7.3.0

PS C:\Temp\bin> .\sqleet0311-win32-x86_64-mingw730.exe 'file:ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> cretae table t(x,y);
Error: near "cretae": syntax error
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex .\ios.db -Count 64


   Label: C:\Temp\bin\ios.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 20 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 11 E4 DF 8D 25 C3 4E 1E 5D 26 D8 96 1C 0B 60 D2 �äß�%ÃN�]&Ø���`Ò
0000000000000030 63 B1 F9 AA A6 B1 AF 25 F9 BD 94 1D 12 AD 3E 12 c±ùª¦±¯%ù½���­>�

PS C:\Temp\bin> del .\ios.db
PS C:\Temp\bin> .\sqleet0311-win32-x86_64-mingw730.exe 'file:same_folder_64.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex .\same_folder_64.db -Count 64


   Label: C:\Temp\bin\same_folder_64.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 20 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 C7 1E 06 1A 14 B0 DD B8 D4 3B 16 6E EE 6A B9 8F Ç����°Ý¸Ô;�nîj¹�
0000000000000030 0D 1D C9 24 8C 7C AA F0 C5 11 DB B2 8E 92 5D B3 ��É$�|ªðÅ�Û²��]³

PS C:\Temp\bin> del .\same_folder_64.db
PS C:\Temp\bin> .\sqleet0311-win32-x86_64-mingw730.exe 'file:/C:/Temp/sql/ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex -Path 'C:/Temp/sql/ios.db' -Count 64


   Label: C:\Temp\sql\ios.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 20 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 A7 A9 26 78 BA 3F A9 BA E2 09 E8 09 BD 79 D5 7F §©&xº?©ºâ�è�½yÕ�
0000000000000030 C4 88 DF EA 7D 5C E6 B6 EC 58 10 A0 F9 7B DB A0 Ä�ßê}\æ¶ìX� ù{Û 

PS C:\Temp\bin> del C:/Temp/sql/ios.db
PS C:\Temp\bin> .\sqleet0311-win32-x86_64-mingw730.exe 'file:/C:/Temp/sql/different_path_64.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
PS C:\Temp\bin> Format-Hex -Path 'C:/Temp/sql/different_path_64.db' -Count 64


   Label: C:\Temp\sql\different_path_64.db

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3
0000000000000010 10 00 01 01 20 40 20 20 00 00 00 01 00 00 00 02 � �� @     �   �
0000000000000020 0E B4 2A 72 F8 2C 95 E3 76 CC 07 BA A0 34 8D EB �´*rø,�ãvÌ�º 4�ë
0000000000000030 49 C8 26 7B 37 B4 BC 69 40 4C 64 E2 C2 E4 59 ED IÈ&{7´¼i@LdâÂäYí

PS C:\Temp\bin> del C:/Temp/sql/different_path_64.db

Conclusion(s):

  • The 64 bit MINGW build is well-behaved. The URI Interface successfully encrypts the database in all tested scenarios;
  • PRAGMA rekey works correctly for all tested scenarios;

Sqleet MacOS 64 bit build @ CLANG 11.0.0

dev-osx-0:temp dev$ ./sqleet0311-osx-x86_64-clang1100 'file:ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
dev-osx-0:temp dev$ xxd ios.db | head -n4
00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.
00000010: 1000 0101 0040 2020 0000 0001 0000 0002  .....@  ........
00000020: 0000 0000 0000 0000 0000 0001 0000 0004  ................
00000030: 0000 0000 0000 0000 0000 0001 0000 0000  ................
dev-osx-0:temp dev$ rm -f ios.db
dev-osx-0:temp dev$ ./sqleet0311-osx-x86_64-clang1100 'file:same_folder_64.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
dev-osx-0:temp dev$ xxd same_folder_64.db | head -n4
00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.
00000010: 1000 0101 0040 2020 0000 0001 0000 0002  .....@  ........
00000020: 0000 0000 0000 0000 0000 0001 0000 0004  ................
00000030: 0000 0000 0000 0000 0000 0001 0000 0000  ................
dev-osx-0:temp dev$ rm -f same_folder_64.db
dev-osx-0:temp dev$ ./sqleet0311-osx-x86_64-clang1100 'file:/Users/dev/temp/sql/ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
dev-osx-0:temp dev$ xxd /Users/dev/temp/sql/ios.db | head -n4
00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.
00000010: 1000 0101 0040 2020 0000 0001 0000 0002  .....@  ........
00000020: 0000 0000 0000 0000 0000 0001 0000 0004  ................
00000030: 0000 0000 0000 0000 0000 0001 0000 0000  ................
dev-osx-0:temp dev$ rm -f /Users/dev/temp/sql/ios.db
dev-osx-0:temp dev$ ./sqleet0311-osx-x86_64-clang1100 'file:/Users/dev/temp/sql/different_path_64.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
dev-osx-0:temp dev$ xxd /Users/dev/temp/sql/different_path_64.db | head -n4
00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.
00000010: 1000 0101 0040 2020 0000 0001 0000 0002  .....@  ........
00000020: 0000 0000 0000 0000 0000 0001 0000 0004  ................
00000030: 0000 0000 0000 0000 0000 0001 0000 0000  ................
dev-osx-0:temp dev$ rm -f /Users/dev/temp/sql/different_path_64.db

Conclusion(s):

  • For Sqleet MAcOS 64 bit built with CLANG 11.0.0, the URI Interface fails to encrypt the database in all tested scenarios;
  • PRAGMA rekey works correctly for all tested scenarios;

Sqleet Linux 64 bit build @ GCC 7.5.0

dev@dev-nux-0:~/temp$ ./sqleet0311-linux-x86_64-gcc750 'file:ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
dev@dev-nux-0:~/temp$ xxd ios.db | head -n4
00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.
00000010: 1000 0101 2040 2020 0000 0001 0000 0002  .... @  ........
00000020: d54f be4f 035f a4d4 2d1a 615f ea0a f417  .O.O._..-.a_....
00000030: 96ab b062 39b6 bf23 828b 55f9 13fc 549e  ...b9..#..U...T.
dev@dev-nux-0:~/temp$ rm -f ios.db
dev@dev-nux-0:~/temp$ ./sqleet0311-linux-x86_64-gcc750 'file:same_folder_64.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
dev@dev-nux-0:~/temp$ xxd same_folder_64.db | head -n4
00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.
00000010: 1000 0101 2040 2020 0000 0001 0000 0002  .... @  ........
00000020: c230 fc9d 35f5 a75b 1317 ad3b a271 6398  .0..5..[...;.qc.
00000030: 9234 3ea7 f1a6 adbf 6a6c 09b6 ec8d 8a3b  .4>.....jl.....;
dev@dev-nux-0:~/temp$ rm -f same_folder_64.db
dev@dev-nux-0:~/temp$ ./sqleet0311-linux-x86_64-gcc750 'file:/home/dev/temp/sql/ios.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
dev@dev-nux-0:~/temp$ xxd /home/dev/temp/sql/ios.db | head -n4
00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.
00000010: 1000 0101 2040 2020 0000 0001 0000 0002  .... @  ........
00000020: b84b ea32 8753 d5f6 4348 8569 f10f 58b2  .K.2.S..CH.i..X.
00000030: 545b e40b f089 1e77 ae27 8c29 03dd ae20  T[.....w.'.)... 
dev@dev-nux-0:~/temp$ rm -f /home/dev/temp/sql/ios.db
dev@dev-nux-0:~/temp$ ./sqleet0311-linux-x86_64-gcc750 'file:/home/dev/temp/sql/different_path_64.db?key=swordfish&salt=SodiumChloride42&header=SQLite%20format%203&skip=32'
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> create table t(x,y);
sqlite> .quit
dev@dev-nux-0:~/temp$ xxd /home/dev/temp/sql/different_path_64.db | head -n4
00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300  SQLite format 3.
00000010: 1000 0101 2040 2020 0000 0001 0000 0002  .... @  ........
00000020: eed0 51b4 4613 838b a75c d9e2 4eda 9a9c  ..Q.F....\..N...
00000030: 5c9d 5383 c03d 3465 98a8 3896 7d0c 31b7  \.S..=4e..8.}.1.
dev@dev-nux-0:~/temp$ rm -f /home/dev/temp/sql/different_path_64.db

Conclusion(s):

  • The Linux 64 bit GCC build is well-behaved. The URI Interface successfully encrypts the database in all tested scenarios;
  • PRAGMA rekey works correctly for all tested scenarios;

How to support such a statement "ATTACH DATABASE x AS y KEY z"?

How to support such a statement "ATTACH DATABASE x AS y KEY z"?

sqlite3* db = 0;
ret = sqlite3_open("test2.db", &db);
std::string sqlTemp = "ATTACH DATABASE 'test2.db' AS test2 KEY '123456';";
sqlite3_exec(db, sqlTemp.c_str(), NULL, NULL, &pErrMsg);

But it made a mistake: file is not a database.

Problem with memory databases and encryption

A few days ago an issue was opened for my wxSQLite3 component regarding the use of rekey for a memory database. As mentioned on the SQLite SEE documentation page in-memory databases are not encrypted (using PRAGMA key='passphrase'; has absolutely no effect). The user was satisfied with that information and simply removed the call to the rekey function that was causing problems for him.

However, IMHO the current behaviour of the key and rekey functions if called accidentally for in-memory databases can cause problems and should therefore be addressed.

The problem affects not only wxSQLite3, but also sqleet, as can be verified by executing the following SQL command sequence in the sqleet shell:

.open :memory:
create table t1 (c1 int, c2 char);
insert into t1 values (1,'A text value');
pragma rekey='test';
select * from t1;

The last select command results in displaying Error: file is not a database.

I have not yet tracked down from where this error results. All I can say is that the page encryption/decryption function is never called while executing rekey.

AFAICT not only in-memory databases, but also temporary databases are affected. The question is how wxSQLite3 and sqleet should behave under those circumstances. Should the key and rekey functions detect whether they operate on an in-memory or temporary database and silently do nothing? Or should they issue an error message? What's your opinion?

python instructions

Hello and thanks for this awesome project.
I just wanted to know how can i use this library as the default sqlite library in my python projects?
i am naive developer i was curios if i should download the source of sqlite3 amalgamation and provide c headers of this project to compile with. am i right?
BTW what should i do after compiling sqlite3 for integrating it into my python projects?
thanks in advance.

Error compiling in Xcode - Clang

Hi!

I am a self-teaching programmer, no commercial, just hobbyist and smalls programs to my hobbies or helping automating some processes on my job. I learn a lot from people posting source codes, so I would like to thank you first of all.

I am in love with sqlite since I found it was a cross-platform and embeddable database years ago, and I have been looking for a way to encrypt sqlite db since then. I have come to sqleet, but when trying to compile it in Xcode found lots of errors preventing me from compiling it.

I have downloaded the amalgamation of sqlite source from its web, dropped it in my project of c++ console, and just tried to open and close a connection to a db. It compiled perfectly and showed "It worked!!", just with 73 warnings (several MIN and MAX macros duplicated with <sys/format.h>, some loose integer precision, and few code will never be executed):

`

#include "../SQLite3/sqlite3.h"

int main(int argc, const char * argv[]) {
sqlite3 *db;
if(sqlite3_open("/Users/Raul/Desktop/Aviacion.db", &db)==SQLITE_OK) {
std::cout << "It worked!!" << std::endl;
}
sqlite3_close(db);

return 0;

}

`

A continuation I have done, manually, my own sqleet.c amalgamation with sqlite3.c downloaded from sqlite's web that worked in previous example. To my surprise, I get 83 warnings and lots of errors (Xcode stop showing me at 20 errors) on sqlite3.c part of the amalgamation of sqleet.c. An example of errors were:

assigning to sqlite3_mutex* from incompatible void*
assigning to char* from incompatible type void*
assigning to struct SrcList_item* (aka SrcList_item* from incompatible type SrcList::SrcList_item*
cannot initialize a variable of type unsigned char* with an lvalue of type void*

It is very strange, as it was the same part of sqlite that was compiling before, and give same errors as the almagalmation done via almagalmation.sh of sqleet.

¿any clues?

sqleet vs. SQLCipher comparison

A brief comparison of SQLCipher and sqleet features. Last updated: 2019-02-17

General

_ SQLCipher sqleet
First release November 2008 September 2017
SQLite version v3.26.0 v3.27.1
Lines of code ~3785 ~1385
License BSD Public domain

Cryptographic primitives (default configuration)

_ SQLCipher sqleet
Cipher AES-256-CBC ChaCha20
Authentication HMAC-SHA512 Poly1305
Key derivation PBKDF2-HMAC-SHA512 PBKDF2-HMAC-SHA256
PBKDF2 iterations 256000 12345

Cryptographically wise, they implement pretty much the exact same functionality but using different primitives. ChaCha20-Poly1305 makes sqleet probably a better choice for embedded systems and mobile devices with slow CPUs, but there is not much else to say about the cryptographic primitive choices. PBKDF2 iteration count and cipher are run-time configurable in SQLCipher, while sqleet supports only easy configuration of PBKDF2 iterations at compile time.

Practicalities

_ SQLCipher sqleet
Dependencies OpenSSL or LibTomCrypt or CommonCrypto None (self-contained)
Configurability Compile-time and run-time Compile-time (limited)
Popularity De facto standard Neverheard

SQLCipher offers no backward/forward source version compatibility, that is, upgrading SQLite3 version always requires major adjustments to the code of SQLCipher (there are typically one or two SQLCipher releases in a year; thus, SQLCipher users might have to wait up to 1 year for support of the latest SQLite3 version). In comparison, sqleet's backward/forward compatibility usually spans several SQLite3 versions, that is, the current version of sqleet source code most likely works with the upcoming SQLite3 versions without any modifications (just drop in the new SQLite3 amalgamation).

Additionally, the crypto of sqleet has been implemented from scratch and not validated, which simplifies compiling & linking (especially in embedded environments) but is a red flag to some people. The cryptographic primitives implemented in sqleet are reasonably resistant to side channel attacks (ChaCha20 and PBKDF2-HMAC-SHA256 by design, and the Poly1305 implementation is constant time).

#12 (comment)
For many users it is important however that the crypto provider is FIPS-validated - and this is the case for OpenSSL (FIPS validation). Whether this offers better security is a different question. Fact is, that decision makers in companies often rely on FIPS validation and the like.

Behaviour of sqleet library if call to entropy function fails

In the implementation of the functions entropy and read_urandom you chose to call system function abort in case of failure to provide random data.

I don't know under which exact conditions a failure might happen. However, for a library I find it inappropriate to abort the process/application. The application should at least get a chance to handle this situation and to gracefully shut down the application. Yes, it is possible to implement a signal handler, but the library behaviour should be at least documented, so that a developer is aware of this.

Personally, I would prefer a solution where some sort of fallback function would be called in case the system function fails. For example, one could provide an own PRNG - yes, maybe slightly less secure, but certainly better than aborting the process. In addition, a warning message could be written to the log.

PRAGMA for header, salt, kdf rounds and skip

Any plans on adding PRAGMA(s) for header, salt, kdf rounds and skip ? For example one could do:

PRAGMA file_header='Sqlite%20format%203'
PRAGMA header_skip=32
PRAGMA pbkdf2_rounds=12345
PRAGMA pbkdf2_salt='\x63\x61\x63\x61\x5f\x6d\x61\x63\x61'

and then PRAGMA rekey='\x6d\x61\x63\x61\x5f\x63\x61\x63\x61' and be done with it. Wouldn't that be nice?

SQLite 3.32.3 build issues

I downloaded the latest SQLite amalgamation and dropped those files in the root of the repo.

I committed them and then ran the following:

$ ./script/rekeyvacuum.sh > rekeyvacuum.c 
$ ./script/release.sh 
[+] SQLite version 3.32.3
[+] Checking rekeyvacuum.c
[+] Creating release directory sqleet-v0.32.3
[+] Generating sqleet.c amalgamation
[+] Generating sqleet.h amalgamation
[+] Updating shell.c #include "sqlite3.h" -> "sqleet.h"
[+] Copying sqlite3ext.h to release directory
[+] Creating release archives
[+] Success!
git tag v0.32.3 && git push origin v0.32.3

When I go to build I receive many compilation errors:

$ cd sqleet-v0.32.3/
$ ls
README.md  shell.c  sqleet.c  sqleet.h  sqlite3ext.h
$ cc ./sqleet.c 
./sqleet.c: In function ‘codec_set_to’:
./sqleet.c:231095:9: warning: implicit declaration of function ‘sqlite3PagerSetCodec’; did you mean ‘sqlite3PagerSetFlags’? [-Wimplicit-function-declaration]
231095 |         sqlite3PagerSetCodec(pager, codec_handle, size_hook, codec_free, codec);
       |         ^~~~~~~~~~~~~~~~~~~~
       |         sqlite3PagerSetFlags
./sqleet.c: In function ‘sqlite3CodecGetKey’:
./sqleet.c:231156:20: warning: implicit declaration of function ‘sqlite3PagerGetCodec’; did you mean ‘sqlite3PagerGetExtra’? [-Wimplicit-function-declaration]
231156 |     Codec *codec = sqlite3PagerGetCodec(sqlite3BtreePager(db->aDb[nDb].pBt));
       |                    ^~~~~~~~~~~~~~~~~~~~
       |                    sqlite3PagerGetExtra
./sqleet.c:231156:20: warning: initialization of ‘Codec *’ {aka ‘struct codec *’} from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
./sqleet.c: In function ‘sqlite3CodecAttach’:
./sqleet.c:231191:15: warning: assignment to ‘Codec *’ {aka ‘struct codec *’} from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
231191 |         codec = sqlite3PagerGetCodec(sqlite3BtreePager(db->aDb[mDb].pBt));
       |               ^
./sqleet.c: In function ‘sqlite3_rekey_v2’:
./sqleet.c:231249:12: warning: assignment to ‘Codec *’ {aka ‘struct codec *’} from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
231249 |     reader = sqlite3PagerGetCodec(pager);
       |            ^
/usr/bin/ld: /usr/lib/gcc/x86_64-pc-linux-gnu/10.1.0/../../../../lib/Scrt1.o: in function `_start':
(.text+0x24): undefined reference to `main'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `pthreadMutexAlloc':
sqleet.c:(.text+0x4499): undefined reference to `pthread_mutexattr_init'
/usr/bin/ld: sqleet.c:(.text+0x44aa): undefined reference to `pthread_mutexattr_settype'
/usr/bin/ld: sqleet.c:(.text+0x44c9): undefined reference to `pthread_mutexattr_destroy'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `pthreadMutexTry':
sqleet.c:(.text+0x4592): undefined reference to `pthread_mutex_trylock'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `sqlite3ThreadCreate':
sqleet.c:(.text+0x9284): undefined reference to `pthread_create'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `sqlite3ThreadJoin':
sqleet.c:(.text+0x9316): undefined reference to `pthread_join'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `unixDlOpen':
sqleet.c:(.text+0x11a42): undefined reference to `dlopen'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `unixDlError':
sqleet.c:(.text+0x11a61): undefined reference to `dlerror'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `unixDlSym':
sqleet.c:(.text+0x11ab0): undefined reference to `dlsym'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `unixDlClose':
sqleet.c:(.text+0x11ae6): undefined reference to `dlclose'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `codec_set_to':
sqleet.c:(.text+0xccd47): undefined reference to `sqlite3PagerSetCodec'
/usr/bin/ld: sqleet.c:(.text+0xccd6f): undefined reference to `sqlite3PagerSetCodec'
/usr/bin/ld: sqleet.c:(.text+0xccf46): undefined reference to `sqlite3PagerSetCodec'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `sqlite3CodecGetKey':
sqleet.c:(.text+0xcd003): undefined reference to `sqlite3PagerGetCodec'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `sqlite3CodecAttach':
sqleet.c:(.text+0xcd1a6): undefined reference to `sqlite3PagerGetCodec'
/usr/bin/ld: /tmp/cckYon8Q.o: in function `sqlite3_rekey_v2':
sqleet.c:(.text+0xcd3e0): undefined reference to `sqlite3PagerGetCodec'
/usr/bin/ld: sqleet.c:(.text+0xcd5cc): undefined reference to `sqlite3PagerSetCodec'
collect2: error: ld returned 1 exit status

Possible SQLite3 compatibility issue

I came across your SQLite3 encryption extension by chance. I'm very interested in adopting your encryption scheme for my own SQLite3 encryption extension (packaged with wxSQLite3), since nowadays one has to face increasing security requirements.

However, in your implementation might be a possible compatibility issue. According to the documentation of the official SQLite Encryption Extension (SEE) (at the bottom of the page) some bytes of the first database page are not encrypted:

Bytes 16 through 23 of the database are unencrypted. Thus, you can always check to see how much nonce is being used, even on an encrypted database file, just by looking at byte 20. It is recommended that any product that uses encryption check this byte to make sure it is being set to 4 or 12 or 32 and not 0.

And indeed, the database header is read in the function sqlite3BtreeOpen before the enryption codec gets a chance to decrypt page 1. Probably this does no harm - at least I did not experience actual problems up to now -, but maybe bytes 16 through 23 should be saved unencrypted to increase compatibility with the official SQLite3 version.

Thread synchronization issues in shared-cache mode (when opening a database)

In shared-cache mode (disabled by default), sqlite3_key_v2() sometimes returns SQLITE_NOTADB (or SQLITE_LOCKED?) due to synchronization issues with respect to reading the database page 1 for codec verification in codec_set_to(). The core problem seems to be the synchronization of shared page cache usage and, in particular, the clearing of the page cache in order to read the page 1 from disk (cached page is decrypted and therefore useless for verifying the codec).

What are the plans for SQLeet?

Is this project still alive? Are there plans to accept the new API of SQLite and to allow updates beyond v3.32 of the engine?

I am using SQLeet in my project and it is good to know the plans of the project. It is not an urgent issue, but stalling the version of SQLite to 3.32 is serious reason to look for a replacement.

On the other hand, SQLeet is the most compact encryption solution I was able to found, so I will look for replacement with а great regret.

Loadable as a runtime extension?

Sqlite3 provides the ability to load extensions at runtime with the load_extension() function. Is it possible, or will it ever be possible to load this encryption library at runtime? Runtime loading is useful for binding to dynamic languages like Python or JavaScript

Optionally skip key verification in codec_set_to()

Other SQLite3 encryption extesions (e.g., wxSQLite3 & SQLCipher) do not verify the encryption key in sqlite_key_v2(). Instead, the validity of the key is determined later based on the success/failure of the following database access (source SQLCipher API: Testing the key).

Verifying the key automatically in sqlite_key_v2() is generally better, but in certain situations skipping the key verification is useful. For example, a multi-threaded app may want to verify the key once in the main thread, and then skip unnecessary key verifications in child threads using the same (known to be correct) key. Moreover, the verification process involves reading the 1st page of the database (from disk bypassing page caches), which may be unacceptable in rare cases.

Straightforward implementation consists of a compile-time macro and a run-time URI parameter controlling the skipping of the key verification in codec_set_to().

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.