How I got SQLCipher working with Electron on macOS:
- First compile SQLCipher and dynamically link it to macOS' crypto libraries. Don't use OpenSSL. Zetetic (makers of SQLCipher) say that using macOS' crypto libraries is definitely going to be easiest on a Mac. Note that using SQLCipher libraries installed via Homebrew is going to seem like it works, and it will on your computer, but Homebrew's SQLCipher dynamcially links OpenSSL, so your app will crash when deployed to other computers (i.e. dynamic linking will fail at run timer because other computer wont have OpenSSL installed). So you want to dynamically link macOS crypto.
- Then you want to rebuild node-sqlite3 with a modified binding.gyp so that the SQLCipher library is statically linked into your SQLite binding. You should end up with a binary file ./node_modules/sqlite3/lib/binding/electron-v3.0-darwin-x64/node_sqlite3.node that has libsqlcipher.a statically linked within it. The statically linked libsqlcipher.a will in-turn dynamically link macOS' libcrypto.
- So in summary: dynamically link macOS' libcrypto when compiling SQLCipher, and statically link libsqlcipher when compiling (a.k.a. rebuilding) node-sqlite3 bindings.
- Also be sure you're using the Electron flags (check version!) for rebuilding. (see RebuildSQLite.sh below).
- The core things I didn't understand and that slowed me down:
- Never having dealt with rebuilding native node modules. node-gyp and electron-rebuild are used to do this.
- Not understanding dynamic and static linking well enough. This was especially challenging because crashes are just hard crashes with horrible error messages, and this setup requires multiple steps in linking (once when you compile SQLCipher and once when you rebuild node-sqlite3).
-
Clone sqlcipher.
cd ~/code git clone git@github.com:sqlcipher/sqlcipher.git cd sqlcipher git checkout -b v.3.4.2 tags/v3.4.2
-
Configure the build.
make clean ./configure --enable-tempstore=yes --enable-load-extension --disable-tcl --with-crypto-lib=commoncrypto
CFLAGS="-DSQLITE_HAS_CODEC -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS5"
LDFLAGS="-framework Security -framework Foundation"
(was -framework CoreServices instead of -framework Foundation at one point)
-
Run the build.
make
This will produce the file ./.lib/libsqlcipher.a
which will be referenced when
we run npm rebuild
. If you want to clean up the built files, run make clean
.
Other notes on compiling SQLCipher
I've spent countless hours trying to get SQLCipher to work by statically linking
OpenSSL's libcrypto.a. Building libsqlcipher.a seems to work, and so does
npm rebuild
ing node-sqlite3, but when Command E is deployed everything seems
to work, but the resulting database is just NOT encrypted. It seems there may be
some problem or core thing I'm missing with how statically linking OpenSSL to
SQLCipher works. ¯_(ツ)_/¯
- Rebuild node-sqlite3 pointing to libsqlcipher.a
This is done in our script tesla/sqlcipher/RebuildSQLite.sh. You can just run
yarn rebuild-sqlite
.
echo "Copying custom binding.gyp (node-sqlite3)"
cp sqlcipher/custom-binding.gyp node_modules/sqlite3/binding.gyp
echo "Rebuilding node-sqlite3 bindings with statically linked SQLCipher (libsqlcipher.a)"
npm rebuild sqlite3 --build-from-source --static_linking_path=/Users/ben/code/sqlcipher/.libs/libsqlcipher.a --runtime=electron --target=3.0.6 --dist-url=https://atom.io/download/electron
libsqlcipher.a is pulled into the node-sqlite3 bindings and a new binary file is
placed at
./node_modules/sqlite3/lib/binding/electron-v3.0-darwin-x64/node_sqlite3.node
.
This binary has SQLCipher bundled in it, which in-turn dynamically links Common
Crypto. Running yarn package
from here will build the app with the SQLCipher
features enabled.
otool -L /Users/ben/code/sqlcipher/.libs/libsqlcipher.dylib
strings .libs/libsqlcipher.a | grep -i RAND_add
strings .libs/libsqlcipher.a | grep -i RAND_add
- Beware of accidental dynamic includes or configuration issues in binding.gyp.
For what it's worth, node-sqlite3 4.2.0 says it's on SQLite 3.31.1, not SQLite 3.31.0 as SQLCipher 4.4.0 is built on. I'm not sure if that matters, but wanted to point that out.
I will say from experience of using this combination of libraries for 2 years, it's hard to have the bleeding edge version of each of them all at the same time. I'd recommend getting "any working build" first, then playing with versions. Here is the versions we're using... why don't you try to get your build working with these versions and these exact commands, then upgrade the pieces once you have a working build based on these versions?
Here's our rebuild command from package.json
custom-binding.gyp
SQLCIPHER_PATH in my .zshrc
Current Instructions for installing SQLCipher
SQLCipher
(was -framework CoreServices instead of -framework Foundation at one point)
This will produce the file
./.lib/libsqlcipher.a
which will be referenced whenwe run
yarn rebuild
. If you want to clean up the built files, runmake clean
.In order complile the sqlite node module as described below, node-gyp needs to
be made aware of the location of our sqlcipher static library files. The gyp
binding file handles this by using the $SQLCIPHER_PATH environment variable.
Export the environment variable to point to sqlcipher:
Other notes on compiling SQLCipher
I've spent countless hours trying to get SQLCipher to work by statically linking
OpenSSL's libcrypto.a. Building libsqlcipher.a seems to work, and so does
npm rebuild
ing node-sqlite3, but when Command E is deployed everything seemsto work, but the resulting database is just NOT encrypted. It seems there may be
some problem or core thing I'm missing with how statically linking OpenSSL to
SQLCipher works. ¯_(ツ)_/¯
This is with the logic in
yarn rebuild
andyarn rebuild-sqlite
.libsqlcipher.a is pulled into the node-sqlite3 bindings and a new binary file is
placed at
./node_modules/sqlite3/lib/binding/electron-v3.0-darwin-x64/node_sqlite3.node
.This binary has SQLCipher bundled in it, which in-turn dynamically links Common
Crypto. Running
yarn package
from here will build the app with the SQLCipherfeatures enabled.
Debugging commands
What goes wrong