andpor / react-native-sqlite-storage

Full featured SQLite3 Native Plugin for React Native (Android and iOS)
MIT License
2.75k stars 520 forks source link

Encryption support #394

Open alnorris opened 4 years ago

alnorris commented 4 years ago

Is there a plan for encryption support anytime soon?

mo22 commented 4 years ago

I've used this patch

diff --git a/node_modules/react-native-sqlite-storage/platforms/android/build.gradle b/node_modules/react-native-sqlite-storage/platforms/android/build.gradle
index 084752e..7fbd196 100644
--- a/node_modules/react-native-sqlite-storage/platforms/android/build.gradle
+++ b/node_modules/react-native-sqlite-storage/platforms/android/build.gradle
@@ -36,4 +36,6 @@ repositories {

 dependencies {
     implementation 'com.facebook.react:react-native:+'
+
+    implementation 'net.zetetic:android-database-sqlcipher:4.2.0'
 }
diff --git a/node_modules/react-native-sqlite-storage/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java b/node_modules/react-native-sqlite-storage/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java
index 4f2391b..b0d12f0 100644
--- a/node_modules/react-native-sqlite-storage/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java
+++ b/node_modules/react-native-sqlite-storage/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java
@@ -8,10 +8,15 @@
 package org.pgsqlite;

 import android.annotation.SuppressLint;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteStatement;
+//import android.database.Cursor;
+//import android.database.sqlite.SQLiteDatabase;
+//import android.database.sqlite.SQLiteException;
+//import android.database.sqlite.SQLiteStatement;
+import net.sqlcipher.Cursor;
+import net.sqlcipher.database.SQLiteCursor;
+import net.sqlcipher.database.SQLiteDatabase;
+import net.sqlcipher.database.SQLiteException;
+import net.sqlcipher.database.SQLiteStatement;
 import android.content.Context;
 import android.util.Base64;

@@ -335,7 +340,7 @@ public class SQLitePlugin extends ReactContextBaseJavaModule {
      * @return instance of SQLite database
      * @throws Exception
      */
-    private SQLiteDatabase openDatabase(String dbname, String assetFilePath, int openFlags, CallbackContext cbc) throws Exception {
+    private SQLiteDatabase openDatabase(String dbname, String key, String assetFilePath, int openFlags, CallbackContext cbc) throws Exception {
         InputStream in = null;
         File dbfile = null;
         try {
@@ -411,7 +416,7 @@ public class SQLitePlugin extends ReactContextBaseJavaModule {

             FLog.v(TAG, "DB file is ready, proceeding to OPEN SQLite DB: " + dbfile.getAbsolutePath());

-            SQLiteDatabase mydb = SQLiteDatabase.openDatabase(dbfile.getAbsolutePath(), null, openFlags);
+            SQLiteDatabase mydb = SQLiteDatabase.openDatabase(dbfile.getAbsolutePath(), key, null, openFlags);

             if (cbc != null)
                 cbc.success("Database opened");
@@ -868,7 +873,16 @@ public class SQLitePlugin extends ReactContextBaseJavaModule {
         }
     }

+    private void closeQuietly(SQLiteCursor closeable) {
+        closeable.close();
+    }
+
+    private void closeQuietly(SQLiteStatement closeable) {
+        closeable.close();
+    }
+
     private class DBRunner implements Runnable {
+        final String key;
         final String dbname;
         final int openFlags;
         private String assetFilename;
@@ -880,6 +894,7 @@ public class SQLitePlugin extends ReactContextBaseJavaModule {

         DBRunner(final String dbname, ReadableMap options, CallbackContext cbc) {
             this.dbname = dbname;
+            this.key = options.getString("key");
             int openFlags = SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY;
             try {
                 this.assetFilename = SQLitePluginConverter.getString(options,"assetFilename",null);
@@ -901,7 +916,7 @@ public class SQLitePlugin extends ReactContextBaseJavaModule {

         public void run() {
             try {
-                this.mydb = openDatabase(dbname, this.assetFilename, this.openFlags, this.openCbc);
+                this.mydb = openDatabase(dbname, key, this.assetFilename, this.openFlags, this.openCbc);
             } catch (SQLiteException ex) {
                 FLog.e(TAG, "SQLite error opening database, stopping db thread", ex);
                 if (this.openCbc != null) {
@@ -930,7 +945,7 @@ public class SQLitePlugin extends ReactContextBaseJavaModule {
                     if (androidLockWorkaround && dbq.queries.length == 1 && dbq.queries[0].equals("COMMIT")) {
                         // FLog.v(TAG, "close and reopen db");
                         closeDatabaseNow(dbname);
-                        this.mydb = openDatabase(dbname, "", this.openFlags, null);
+                        this.mydb = openDatabase(dbname, key, "", this.openFlags, null);
                         // FLog.v(TAG, "close and reopen db finished");
                     }

diff --git a/node_modules/react-native-sqlite-storage/platforms/android/src/main/java/org/pgsqlite/SQLitePluginPackage.java b/node_modules/react-native-sqlite-storage/platforms/android/src/main/java/org/pgsqlite/SQLitePluginPackage.java
index be25027..ddec9cc 100644
--- a/node_modules/react-native-sqlite-storage/platforms/android/src/main/java/org/pgsqlite/SQLitePluginPackage.java
+++ b/node_modules/react-native-sqlite-storage/platforms/android/src/main/java/org/pgsqlite/SQLitePluginPackage.java
@@ -13,6 +13,8 @@ import com.facebook.react.bridge.NativeModule;
 import com.facebook.react.bridge.ReactApplicationContext;
 import com.facebook.react.uimanager.ViewManager;

+import net.sqlcipher.database.SQLiteDatabase;
+
 import java.util.ArrayList;
 import java.util.Collections;

@@ -34,6 +36,8 @@ public class SQLitePluginPackage implements ReactPackage {
     @Override
     public List<NativeModule> createNativeModules(
                                 ReactApplicationContext reactContext) {
+      SQLiteDatabase.loadLibs(reactContext);
+
       List<NativeModule> modules = new ArrayList<>();

       modules.add(new SQLitePlugin(reactContext));
diff --git a/node_modules/react-native-sqlite-storage/platforms/ios/SQLite.m b/node_modules/react-native-sqlite-storage/platforms/ios/SQLite.m
index 6ba5355..1ce1b5f 100644
--- a/node_modules/react-native-sqlite-storage/platforms/ios/SQLite.m
+++ b/node_modules/react-native-sqlite-storage/platforms/ios/SQLite.m
@@ -32,7 +32,12 @@
  * See http://opensource.org/licenses/alphabetical for full text.
  */

+#ifdef SQLCIPHER
+#define SQLITE_HAS_CODEC
+#import <SQLCipher/sqlite3.h>
+#else
 #import "sqlite3.h"
+#endif

 #include <regex.h>

@@ -75,7 +80,7 @@ @implementation SQLite

 - (id) init
 {
-  RCTLog(@"Initializing SQLitePlugin");
+//  RCTLog(@"Initializing SQLitePlugin");
   self = [super init];
   if (self) {
     openDBs = [NSMutableDictionary dictionaryWithCapacity:0];
@@ -86,18 +91,18 @@ - (id) init
 #endif

     NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
-    RCTLog(@"Detected docs path: %@", docs);
+//    RCTLog(@"Detected docs path: %@", docs);
     [appDBPaths setObject: docs forKey:@"docs"];

     NSString *libs = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
-    RCTLog(@"Detected Library path: %@", libs);
+//    RCTLog(@"Detected Library path: %@", libs);
     [appDBPaths setObject: libs forKey:@"libs"];

     NSString *nosync = [libs stringByAppendingPathComponent:@"LocalDatabase"];
     NSError *err;
     if ([[NSFileManager defaultManager] fileExistsAtPath: nosync])
     {
-      RCTLog(@"no cloud sync at path: %@", nosync);
+//      RCTLog(@"no cloud sync at path: %@", nosync);
       [appDBPaths setObject: nosync forKey:@"nosync"];
     }
     else
@@ -109,7 +114,7 @@ - (id) init
         {
           RCTLog(@"IGNORED: error setting nobackup flag in LocalDatabase directory: %@", err);
         }
-        RCTLog(@"no cloud sync at path: %@", nosync);
+//        RCTLog(@"no cloud sync at path: %@", nosync);
         [appDBPaths setObject: nosync forKey:@"nosync"];
       }
       else
@@ -161,7 +166,7 @@ -(id) getDBPath:(NSString *)dbFile at:(NSString *)atkey {
     } else {
       NSDictionary *dbInfo = openDBs[dbfilename];
       if (dbInfo != NULL && dbInfo[@"dbPointer"] != NULL) {
-        RCTLog(@"Reusing existing database connection for db name %@", dbfilename);
+//        RCTLog(@"Reusing existing database connection for db name %@", dbfilename);
         pluginResult = [SQLiteResult resultWithStatus:SQLiteStatus_OK messageAsString:@"Database opened"];
       } else {
         NSString *assetFilePath = options[@"assetFilename"];
@@ -171,17 +176,17 @@ -(id) getDBPath:(NSString *)dbFile at:(NSString *)atkey {
               NSString *targetBundleDirPath = [[NSBundle mainBundle] resourcePath];
               targetBundleDirPath = [targetBundleDirPath stringByAppendingPathComponent: @"www"];
               assetFilePath = [targetBundleDirPath stringByAppendingPathComponent: dbfilename];
-              RCTLog(@"Built path to pre-populated DB asset from app bundle www subdirectory: %@",assetFilePath);
+//              RCTLog(@"Built path to pre-populated DB asset from app bundle www subdirectory: %@",assetFilePath);
             } else if ([assetFilePath hasPrefix:@"~"]) {
               assetFilePath = [assetFilePath substringFromIndex:1];
               NSString *targetBundleDirPath = [[NSBundle mainBundle] resourcePath];
               assetFilePath = [targetBundleDirPath stringByAppendingPathComponent: assetFilePath];
-              RCTLog(@"Built path to pre-populated DB asset from app bundle subdirectory: %@",assetFilePath);
+//              RCTLog(@"Built path to pre-populated DB asset from app bundle subdirectory: %@",assetFilePath);
             } else {
               NSURL * documentsDirUrl = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
                                                                         inDomains:NSUserDomainMask] lastObject];
               assetFilePath = [documentsDirUrl.path stringByAppendingPathComponent:assetFilePath];
-              RCTLog(@"Built path to pre-populated DB asset from app sandbox documents directory: %@",assetFilePath);
+//              RCTLog(@"Built path to pre-populated DB asset from app sandbox documents directory: %@",assetFilePath);
             }
           } @catch(NSException *ex){
             RCTLog(@"Error building path for pre-populated DB asset %@",ex.reason);
@@ -194,18 +199,18 @@ -(id) getDBPath:(NSString *)dbFile at:(NSString *)atkey {
         } else {
           NSString *dblocation = options[@"dblocation"];
           if (dblocation == NULL) dblocation = @"nosync";
-          RCTLog(@"target database location: %@", dblocation);
+//          RCTLog(@"target database location: %@", dblocation);

           dbname = [self getDBPath:dbfilename at:dblocation];

           /* Option to create from resource (pre-populated) if db does not exist: */
           if (![[NSFileManager defaultManager] fileExistsAtPath:dbname] && assetFilePath != NULL) {
-            RCTLog(@"Copying pre-populated asset to the destination directory");
+//            RCTLog(@"Copying pre-populated asset to the destination directory");
             [self createFromResource:assetFilePath withDbname:dbname];
           }
         }

-        RCTLog(@"Opening db in mode %@, full path: %@", (sqlOpenFlags == SQLITE_OPEN_READONLY) ? @"READ ONLY" : @"READ_WRITE",dbname);
+//        RCTLog(@"Opening db in mode %@, full path: %@", (sqlOpenFlags == SQLITE_OPEN_READONLY) ? @"READ ONLY" : @"READ_WRITE",dbname);

         const char *name = [dbname UTF8String];
         sqlite3 *db;
@@ -222,7 +227,7 @@ -(id) getDBPath:(NSString *)dbFile at:(NSString *)atkey {
           if (dbkey != NULL) {
             key = [dbkey UTF8String];
             if (key != NULL) {
-              sqlite3_key(db, key, strlen(key));
+              sqlite3_key(db, key, (int)strlen(key));
             }
           }
 #endif
@@ -232,7 +237,7 @@ -(id) getDBPath:(NSString *)dbFile at:(NSString *)atkey {
             openDBs[dbfilename] = @{ @"dbPointer": dbPointer, @"dbPath" : dbname};
             NSString *msg = (key != NULL) ? @"Secure database opened" : @"Database opened";
             pluginResult = [SQLiteResult resultWithStatus:SQLiteStatus_OK messageAsString: msg];
-            RCTLog(@"%@", msg);
+//            RCTLog(@"%@", msg);
           } else {
             NSString *msg = [NSString stringWithFormat:@"Unable to open %@", (key != NULL) ? @"secure database with key" : @"database"];
             pluginResult = [SQLiteResult resultWithStatus:SQLiteStatus_ERROR messageAsString:msg];
@@ -246,25 +251,25 @@ -(id) getDBPath:(NSString *)dbFile at:(NSString *)atkey {
   }

   if (sqlite3_threadsafe()) {
-    RCTLog(@"Good news: SQLite is thread safe!");
+//    RCTLog(@"Good news: SQLite is thread safe!");
   } else {
     RCTLog(@"Warning: SQLite is not thread safe.");
   }

   [pluginResult.status intValue] == SQLiteStatus_OK ? success(@[pluginResult.message]) : error(@[pluginResult.message]);
-  RCTLog(@"open cb finished ok");
+//  RCTLog(@"open cb finished ok");
 }

 -(void)createFromResource:(NSString *)prepopulatedDb withDbname:(NSString *)dbname {
-  RCTLog(@"Looking for prepopulated DB at: %@", prepopulatedDb);
+//  RCTLog(@"Looking for prepopulated DB at: %@", prepopulatedDb);

   if ([[NSFileManager defaultManager] fileExistsAtPath:prepopulatedDb]) {
-    RCTLog(@"Found prepopulated DB: %@", prepopulatedDb);
+//    RCTLog(@"Found prepopulated DB: %@", prepopulatedDb);
     NSError *error;
     BOOL success = [[NSFileManager defaultManager] copyItemAtPath:prepopulatedDb toPath:dbname error:&error];

     if(success) {
-      RCTLog(@"Copied prepopulated DB content to: %@", dbname);
+//      RCTLog(@"Copied prepopulated DB content to: %@", dbname);
     } else {
       RCTLog(@"Unable to copy DB file: %@", [error localizedDescription]);
     }
@@ -299,16 +304,16 @@ -(void)createFromResource:(NSString *)prepopulatedDb withDbname:(NSString *)dbna
           RCTLog(@"close: db name was not open: %@", dbFileName);
           pluginResult = [SQLiteResult resultWithStatus:SQLiteStatus_ERROR messageAsString:@"Specified db was not open"];
         } else {
-          RCTLog(@"close: closing db: %@", dbFileName);
+//          RCTLog(@"close: closing db: %@", dbFileName);
             sqlite3_close (db);
             [openDBs removeObjectForKey:dbFileName];
             pluginResult = [SQLiteResult resultWithStatus:SQLiteStatus_OK messageAsString:@"DB closed"];
         }

         if ([[NSFileManager defaultManager]fileExistsAtPath:dbPath]) {
-          RCTLog(@"database file still exists after close");
+//          RCTLog(@"database file still exists after close");
         } else {
-          RCTLog(@"database file doesn't exists after close");
+//          RCTLog(@"database file doesn't exists after close");
         }
       }
     }
diff --git a/node_modules/react-native-sqlite-storage/react-native-sqlite-storage.podspec b/node_modules/react-native-sqlite-storage/react-native-sqlite-storage.podspec
index afc3e92..630228c 100644
--- a/node_modules/react-native-sqlite-storage/react-native-sqlite-storage.podspec
+++ b/node_modules/react-native-sqlite-storage/react-native-sqlite-storage.podspec
@@ -18,5 +18,8 @@ Pod::Spec.new do |s|
   s.source_files   = "platforms/ios/*.{h,m}"

   s.dependency 'React'
-  s.library = 'sqlite3'
+#  s.library = 'sqlite3'
+
+  s.dependency 'SQLCipher'
+  s.compiler_flags = ['-DSQLCIPHER=1']
 end