Showing posts with label sqlcipher. Show all posts
Showing posts with label sqlcipher. Show all posts

Monday, December 24, 2012

Using SQLCipher for Android with Cordova/PhoneGap

NOTICE (June 2015): These instructions are completely out-of-date, the following Cordova plugin supports sqlcipher out-of-the-box: https://github.com/litehelpers/Cordova-sqlcipher-adapter

The project brodyspark / PhoneGap-SQLitePlugin-Android provides native sqlite database access with an API that is very close to the HTML5 SQL API and can also provide an interface to SQLCipher for Android. Here are the steps to use SQLCipher with a Cordova/PhoneGap project for Android.


Install sqlite plugin for Android


The first step is to install but not build the sqlite plugin for Android. A good starting point is the Cordova/PhoneGap Android example project, found in the lib/android/example subdirectory of Cordova/PhoneGap as downloaded from phonegap.com/download. These steps are also covered in README.md of brodyspark / PhoneGap-SQLitePlugin-Android. Here are the steps in brief to install the sqlite plugin from PhoneGap-SQLitePlugin-Android:

  • Install SQLitePlugin.js from the Android/assets/www subdirectory into assets/www; for example:
$ cp -v ../PhoneGap-SQLitePlugin-Android/Android/assets/www/SQLitePlugin.js assets/www
  • From the Android/src subdirectory copy the tree com/phonegap/plugin/sqlitePlugin with SQLitePlugin.java into src; for example:
$ cp -rv ../PhoneGap-SQLitePlugin-Android/Android/src/com src
  • Add the plugin element to res/xml/config.xml:
--- config.xml.old  2012-07-24 19:44:49.000000000 +0200
+++ res/xml/config.xml  2012-07-24 19:39:43.000000000 +0200
@@ -32,6 +32,7 @@
     <log level="DEBUG"/>
     <preference name="useBrowserHistory" value="false" />
 <plugins>
+    <plugin name="SQLitePlugin" value="com.phonegap.plugin.sqlitePlugin.SQLitePlugin"/>
     <plugin name="App" value="org.apache.cordova.App"/>
     <plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/>
     <plugin name="Device" value="org.apache.cordova.Device"/>


Install prebuilt SQLCipher for Android


  • Download SQLCipher for Android from android-database-sqlcipher / downloads & unzip the package. Note that the package seems to contain an extra __MACOSX subdirectory which can be ignored.
  • Copy the contents of the lib subdirectory from the package into the libs subdirectory except for commons-codec.jar, for example:
$ cp -rv SQLCipher\ for\ Android\ 2.1.1/libs/* libs
$ rm libs/commons-codec.jar
  • Install the ICU file icudt46l.zip from the assets subdirectory in assets, for example:
$ cp SQLCipher\ for\ Android\ 2.1.1/assets/icudt46l.zip assets

Notes:

  • commons-codec.jar is already part of Cordova/PhoneGap
  • SQLCipher for Android attempts to use the system-provided ICU localisation file but it may not included with all target systems.
  • The x86 subdirectory is only necessary when using x86 as a target; for example, some developers prefer to use an x86 Android simulation platform.

Patches to sqlite plugin to use SQLCipher


In src/com/phonegap/plugin/sqlitePlugin/SQLitePlugin.java make the following changes:

  • In the imports:
@@ -24,7 +24,7 @@
 
 import android.database.Cursor;
 
-import android.database.sqlite.*;
+import net.sqlcipher.database.*;
 
 import android.util.Log;
 
  • In SQLitePlugin.execute():
@@ -63,7 +63,8 @@
     JSONObject o = args.getJSONObject(0);
     String dbname = o.getString("name");
 
-    this.openDatabase(dbname, null);
+    String key = o.getString("key");
+    this.openDatabase(dbname, key);
    }
    else if (action.equals("close")) {
     this.closeDatabase(args.getString(0));
  • In SQLitePlugin.openDatabase():
@@ -152,13 +153,15 @@
   */
  private void openDatabase(String dbname, String password)
  {
+  SQLiteDatabase.loadLibs(this.cordova.getActivity());
+
   if (this.getDatabase(dbname) != null) this.closeDatabase(dbname);
 
   File dbfile = this.cordova.getActivity().getDatabasePath(dbname + ".db");
 
   Log.v("info", "Open sqlite db: " + dbfile.getAbsolutePath());
 
-  SQLiteDatabase mydb = SQLiteDatabase.openOrCreateDatabase(dbfile, null);
+  SQLiteDatabase mydb = SQLiteDatabase.openOrCreateDatabase(dbfile, password, null);
 
   dbmap.put(dbname, mydb);
  }
  • In SQLitePlugin.results2string():
@@ -348,6 +351,7 @@
      for (int i = 0; i < colCount; ++i) {
       key = cur.getColumnName(i);
 
+      /**
       // for old Android SDK remove lines from HERE:
       if(android.os.Build.VERSION.SDK_INT >= 11)
       {
@@ -371,6 +375,7 @@
        }
       }
       else // to HERE.
+      **/
       {
        row.put(key, cur.getString(i));
       }

Build and test


Make sure the project is updated for a target platform:

$ android update project --path . --target <id>


Add a small test program to assets/www/index.html, for example:

--- assets/www/index.html.orig 2012-12-24 22:57:45.000000000 +0100
+++ assets/www/index.html 2012-12-24 23:05:40.000000000 +0100
@@ -34,9 +34,35 @@
             </div>
         </div>
         <script type="text/javascript" src="cordova-2.2.0.js"></script>
-        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript" src="SQLitePlugin.js"></script>
         <script type="text/javascript">
-            app.initialize();
+        document.addEventListener("deviceready", onDeviceReady, false);
+
+        // Cordova is ready
+        function onDeviceReady() {
+          var db = window.sqlitePlugin.openDatabase({name: "DB", key: "secret1"});
+
+          db.transaction(function(tx) {
+            tx.executeSql('DROP TABLE IF EXISTS test_table');
+            tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
+
+            tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(tx, res) {
+              console.log("insertId: " + res.insertId + " -- probably 1");
+              alert("insertId: " + res.insertId + " -- probably 1");
+
+              db.transaction(function(tx) {
+                tx.executeSql("select count(id) as cnt from test_table;", [], function(tx, res) {
+                  console.log("res.rows.length: " + res.rows.length + " -- should be 1");
+                  console.log("res.rows.item(0).cnt: " + res.rows.item(0).cnt + " -- should be 1");
+                  alert("res.rows.item(0).cnt: " + res.rows.item(0).cnt + " -- should be 1");
+                });
+              });
+
+            }, function(e) {
+              console.log("ERROR: " + e.message);
+            });
+          });
+        }
         </script>
     </body>
 </html>



Try a debug install:

$ ant debug install


and the test should run.

To test that the encryption is working, try changing the password key in assets/www/index.html:

--- assets/www/index.html 2012-12-24 23:07:43.000000000 +0100
+++ assets/www/index.html.new 2012-12-24 23:13:38.000000000 +0100
@@ -40,7 +40,7 @@
 
         // Cordova is ready
         function onDeviceReady() {
-          var db = window.sqlitePlugin.openDatabase({name: "DB", key: "secret1"});
+          var db = window.sqlitePlugin.openDatabase({name: "DB", key: "secret2"});
 
           db.transaction(function(tx) {
             tx.executeSql('DROP TABLE IF EXISTS test_table');

and try installing and running again. The program should not work if a different encryption key is used to open the database.

Using cursor type enhancements


As described in this recent posting, some enhancements were made in brodyspark / sqlcipher-android-database to use the actual row/column data type information instead of treating all row/column data as strings. To use these changes:
Now as a test, revert the changes to SQLitePlugin.results2string() then compile & test but first uninstall the old version from the simulator or device.

Sunday, December 23, 2012

Enhancements to SQLCipher db classes for Android

NOTICE (June 2015): These instructions are completely out-of-date, the following Cordova plugin supports sqlcipher out-of-the-box: https://github.com/litehelpers/Cordova-sqlcipher-adapter

SQLCipher provides a special, modified version of SQLite to store data in encrypted form using the Native Development Kit (NDK) on the Android platform. How to rebuild SQLCipher for Android was covered in a recent posting. Unfortunately, SQLCipher for Android is based on an old version of the Android database API and is missing a couple important enhancements:
  • get the data type of each row & column in the results for a SQL query, using Cursor.getType()
  • number of rows affected by a SQL UPDATE or DELETE statement, using SQLiteStatement.executeUpdateDelete()
These enhancements are now integrated in brodyspark / sqlcipher-android-database, which was made as a fork of sqlcipher / android-database-sqlcipher.

These enhancements are especially important for the Cordova/PhoneGap sqlite plugin for Android, in order to properly follow the requirements of the Web SQL API.

The enhancements were obtained from the Android SDK 11 (Honeycomb) version. Only some changes to the cursor classes, database utilities, and SQLiteStatement classes were necessary to get these enhancements working with SQLCipher.

Also the brodyspark / sqlcipher-android-tests fork was made to test these changes.

To compile and test these changes please see my previous posting but with the following changes:

  • For the API: $ git clone git://github.com/brodyspark/sqlcipher-android-database.git
  • Test project: $ git clone git://github.com/brodyspark/sqlcipher-android-tests.git
  • The Makefile is already adapted to work with OSX Homebrew.
NOTE: it is best to use API 11 or greater to compile the code, however it should not matter when running the code. Unfortunately, it still has some problems on API 17, to be fixed sometime in the future.

UPDATES January 2013:

Tuesday, December 18, 2012

Integrating SQLCipher with Cordova/PhoneGap sqlite plugin for iOS

NOTICE (June 2015): These instructions are completely out-of-date, the following Cordova plugin supports sqlcipher out-of-the-box: https://github.com/litehelpers/Cordova-sqlcipher-adapter

These directions are based on the sqlcipher iOS tutorial, with a few adaptations to integrate with a Cordova/PhoneGap project.

Start with a Cordova/PhoneGap iOS project (documented here for Cordova/PhoneGap 2.2.0).

Download & extract the OpenSSL source from www.openssl.org/source in a location that will be referenced later (I used OpenSSL 1.0.1c).

Inside the PhoneGap iOS project folder, clone the github sqlcipher & openssl-xcode projects using commands like the following:

$ git clone git@github.com:sqlcipher/sqlcipher.git
$ git clone git@github.com:sqlcipher/openssl-xcode.git

Download the OpenSSL source from www.openssl.org/source (I used OpenSSL 1.0.1c) & put the source tar.gz file in the openssl-xcode sub-folder.


Add the subproject references for sqlcipher & openssl-xcode. This can be achieved by:

  • selecting the top-level target at the top of the tree control;
  • press alt-command-a;
  • for openssl-xcode: select the openssl-xcode folder and then select openssl-xcode.xcodeproj &
  • repeat these steps for sqlcipher (select the sqlcipher folder & select sqlcipher.xcodeproj).
Configure build dependencies:

  • select the top-level target again;
  • click the Build Phases tab;
  • expand the Target Dependencies;
  • add crypto (click "+", select crypto, and press Add);
  • add sqlcipher

and add the libraries to link:

  • expand the Link Binary With Libraries;
  • click "+" to add
  • add both libcrypto.a & libsqlcipher.a

IMPORTANT: please make sure that no sqlite3 library is being added here.

CHECK POINT: at this point, it should be possible to build the project with the openssl & sqlcipher dependencies.

NOTE: a couple steps from the sqlcipher iOS tutorial were omitted since they should not be necessary. If the project does not build, here are some things to check & try:

  • Please double-check that all dependencies, including Target Dependencies & Link Binary With Libraries have been fulfilled.
  • If the sqlcipher/libcrypto does not build, please read through README.md under openssl-xcode. You may have to configure the location of the OpenSSL sources if nothing else works.
  • The SQLITE_HAS_CODEC C flag, which is necessary to build sqlcipher with its cipher capabilities, should be defined within the sqlcipher subproject. It should not be necessary to define this flag within the top-level application project, however it is noted here just in case.

It should now be possible to add SQLitePlugin.[hm] to the project Plugins folder & build again. The following patch to SQLitePlugin.m should enable the plugin to use database encryption:


diff --git a/iOS/Plugins/SQLitePlugin.m b/iOS/Plugins/SQLitePlugin.m
index 871bd89..3d62208 100644
--- a/iOS/Plugins/SQLitePlugin.m
+++ b/iOS/Plugins/SQLitePlugin.m
@@ -79,6 +79,9 @@
         return;
     }

+    const char *key = [[options objectForKey:@"key"] UTF8String];
+    sqlite3_key(db, key, strlen(key));
+
     dbPointer = [NSValue valueWithPointer:db];
     [openDBs setObject:dbPointer forKey: dbname];
     [self respond:callback withString: @"{ message: 'Database opened' }" withType:@"success"];



Add SQLitePlugin to Cordova.plist resources, add SQLitePlugin.js to the www folder, and include SQLitePlugin.js in index.html. Try a small test program, like the one from the brodyspark/ PhoneGap-SQLitePlugin-iOS homepage, but open the database with a statement like this:


var db = window.sqlitePlugin.openDatabase({name: "DB",
    key: "secret1"});


If you try the program again but with a different key, you should see a db error in the console log.

In the future, I would like to provide a script or boilerplate to make it easy to create Cordova/PhoneGap projects with encrypted storage working from the beginning.