robertohuertasm / SQLite4Unity3d

SQLite made easy for Unity3d

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

password database

cabanel opened this issue · comments

hi,
is possible insert a password for open database?

needing password to my project too!

I also need this - I found this fork -

https://github.com/shreks7/SqliteForUnity

Which adds AES encryption - but it is an old set of code to what is found here, thus is missing a lot of updates that are in the existing DLL - the changes could do with been added / merged into this project, or the same approach used for adding AES.

me too!

@AshleyBates i find this too, but like u said is too old the dlls so would be great to merge then but how? the guys didn't event add source do dll.

I did a bit of digging into the other source - the following can be used as imports for the key from the latest version of sqlite3 -

    [DllImport("sqlite3", EntryPoint = "sqlite3_key", CallingConvention = CallingConvention.Cdecl)]
    public static extern SQLite3.Result Key(IntPtr db, [MarshalAs(UnmanagedType.LPStr)] string pKey, int nkey);

    [DllImport("sqlite3", EntryPoint = "sqlite3_rekey", CallingConvention = CallingConvention.Cdecl)]
    public static extern SQLite3.Result Rekey(IntPtr db, [MarshalAs(UnmanagedType.LPStr)] string pKey, int nkey);

From this point I pulled out the additional routines for setting and accessing the database via the key -

public void SetDbKey(string key)
{
SQLite3.Result r = SQLite3.Rekey(this.Handle, key, key.Length);
if (r != SQLite3.Result.OK)
{
string errmsg = SQLite3.GetErrmsg(this.Handle);
throw SQLiteException.New(r, errmsg);
}
}

    public void Key(string key)
    {
        SQLite3.Result r = SQLite3.Key(this.Handle, key, key.Length);
        if (r != SQLite3.Result.OK)
        {
            string errmsg = SQLite3.GetErrmsg(this.Handle);
            throw SQLiteException.New(r, errmsg);
        }
    }

and finally the dataservice calls -

//Set Database Key
public void SetKey(string key)
{
_connection.SetDbKey(key);
}

//Send Key
public void Key(string key)
{
    _connection.Key(key);
}

I had a quick play around and it did seem to encrypt the database and then allow me to unlock it again, but I did get some strange results - I haven't got the time to go into this fully at the moment but if anyone else wants to take a look then there is the code.

Basically set the key as you create your db then send the key over when you want to access it / open up the connection.

@AshleyBates i will take a look into it. thank's!

@AshleyBates i try but i keep receiveing crash when i try to open or get some error about database encryption!

somehow i thing the decrypt capabilities have to be inside an open func! so he can decrypt while open and "out" the handle with the decrypt database.

after some change i make it work! the good news at that i work! the bad ones are that i can't figure it out how to open using sqlite brownser! it detect sqlitecypher encryption but it can't open! but with unity and inside the game works like a charm! i will be posting what i modify based on the beggining code of @AshleyBates to thank's for the help!

Can you post your changes please? I will need this in the future.

Thank You

On SQLITE3, i change both SetDbKey(string key) and Key(string key) for the above:

key.Length

change to

System.Text.Encoding.UTF8.GetByteCount(key)

public void SetDbKey(string key) {
    SQLite3.Result r = SQLite3.Rekey(this.Handle, key, System.Text.Encoding.UTF8.GetByteCount(key));
    if (r != SQLite3.Result.OK) {
        string errmsg = SQLite3.GetErrmsg(this.Handle);
        throw SQLiteException.New(r, errmsg);
    }
}

public void Key(string key) {
    SQLite3.Result r = SQLite3.Key(this.Handle, key, System.Text.Encoding.UTF8.GetByteCount(key));
    if (r != SQLite3.Result.OK) {
        string errmsg = SQLite3.GetErrmsg(this.Handle);
        throw SQLiteException.New(r, errmsg);
    }
}

Then i create a SQLiteConnection constructor that accept password as a new parameter.

after

var r = SQLite3.Open(databasePathAsBytes, out handle, (int)openFlags, IntPtr.Zero);

i just add to decrypt the db with the key i passed!

SQLite3.Result r2 = SQLite3.Key(handle, _password, System.Text.Encoding.UTF8.GetByteCount(_password));
if (r2 != SQLite3.Result.OK) {
    string errmsg = SQLite3.GetErrmsg(handle);
    throw SQLiteException.New(r2, errmsg);
}
public SQLiteConnection(string _password, string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = false) {

           

            if (string.IsNullOrEmpty(databasePath))
                throw new ArgumentException("Must be specified", "databasePath");

            DatabasePath = databasePath;
            mayCreateSyncObject(databasePath);

#if NETFX_CORE
			SQLite3.SetDirectory(/*temp directory type*/2, Windows.Storage.ApplicationData.Current.TemporaryFolder.Path);
#endif


            Sqlite3DatabaseHandle handle = new Sqlite3DatabaseHandle();

#if SILVERLIGHT || USE_CSHARP_SQLITE
			var r = SQLite3.Open (databasePath, out handle, (int)openFlags, IntPtr.Zero);
#else
            // open using the byte[]
            // in the case where the path may include Unicode
            // force open to using UTF-8 using sqlite3_open_v2         


            var databasePathAsBytes = GetNullTerminatedUtf8(DatabasePath);

            //var r2 = SQLite3.SetPassword(_password);
            var r = SQLite3.Open(databasePathAsBytes, out handle, (int)openFlags, IntPtr.Zero);
            SQLite3.Result r2 = SQLite3.Key(handle, _password, System.Text.Encoding.UTF8.GetByteCount(_password));
            if (r2 != SQLite3.Result.OK) {
                string errmsg = SQLite3.GetErrmsg(handle);
                throw SQLiteException.New(r2, errmsg);
            }
            #endif

            Handle = handle;
            if (r != SQLite3.Result.OK) {
                throw SQLiteException.New(r, String.Format("Could not open database file: {0} ({1})", DatabasePath, r));
            }
            _open = true;

            StoreDateTimeAsTicks = storeDateTimeAsTicks;

            BusyTimeout = TimeSpan.FromSeconds(0.1);
        }

I for last but mostly important i create a Dataservice constructor that accept the password and use the new created sqliteconnection with password.

public DataService(string DatabaseName, string _pass) {
    _databaseName = DatabaseName;
    // check if file exists in Application.persistentDataPath
    Debug.Log("Streaming Location - " + Application.streamingAssetsPath);
    Debug.Log("Persistent Location - " + Application.persistentDataPath);
    Debug.Log("Data Location - " + Application.dataPath);

    var filepath = Application.persistentDataPath + "/" + DatabaseName;
    if (File.Exists(filepath)) {
        Debug.Log("filepath - " + filepath);
        _connection = new SQLiteConnection(_pass,filepath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create);

        /*if(_connection._open == false){
            Debug.Log("problem opening");
            File.Delete(filepath);
            CreateDBOnPath(DatabaseName,filepath);
        }*/
        //Debug.Log("<color=#ffffff>Final PATH:</color> <color=#33bfea>" + filepath + "</color>"); 
    } else {
        Debug.Log("not found");
        CreateDBOnPath(DatabaseName, filepath);
    }
}

and remenber it work but the password must be set on the db using the set key that we created.

so i did this to set the password first before using the new constructors.

public void SetPassword(string _pass) {
    _connection.SetDbKey(_pass);
    _connection.Close();
}

OBS: Sorry my bad non-native english!

Thank you, @GilbertoBitt ! Worked perfectly for me.

So...this works great via the unity editor, but just tested on android and it doesn't work for that. I'll let ya''ll know if I come up a solution. Let me know if you can eyeball what the issue is. :)

Alrighty so I'm just gonna go ahead and share what I have with y'all since you probably know better than me as to why this isn't working for me on Android (but it definitely does when run via Unity Editor on Windows 10). I've attached the pertinent files. When testing on either my android phone (android vers 7.0) and via bluestacks, the whole application is stopped when this file isn't found. No real errors retrieved, etc. It really just seems to time out. :/

My SQLite.cs file: SQLite.cs.txt

I've been creating my connection using this DataService constructor:

   public DataService(string DatabaseName, string _pass)
    {

#if UNITY_EDITOR
        var dbPath = string.Format(@"Assets/StreamingAssets/{0}", DatabaseName);
#else
        // check if file exists in Application.persistentDataPath
        var filepath = string.Format("{0}/{1}", Application.persistentDataPath, DatabaseName);

        if (!File.Exists(filepath))
        {
            Debug.Log("Database not in Persistent path");
            // if it doesn't ->
            // open StreamingAssets directory and load the db ->

#if UNITY_ANDROID
            var loadDb = new WWW("jar:file://" + Application.dataPath + "!/assets/" + DatabaseName);  // this is the path to your StreamingAssets in android
            while (!loadDb.isDone) { }  // CAREFUL here, for safety reasons you shouldn't let this while loop unattended, place a timer and error check
            // then save to Application.persistentDataPath
            File.WriteAllBytes(filepath, loadDb.bytes);
#elif UNITY_IOS
                 var loadDb = Application.dataPath + "/Raw/" + DatabaseName;  // this is the path to your StreamingAssets in iOS
                // then save to Application.persistentDataPath
                File.Copy(loadDb, filepath);
#elif UNITY_WP8
                var loadDb = Application.dataPath + "/StreamingAssets/" + DatabaseName;  // this is the path to your StreamingAssets in iOS
                // then save to Application.persistentDataPath
                File.Copy(loadDb, filepath);

#elif UNITY_WINRT
		var loadDb = Application.dataPath + "/StreamingAssets/" + DatabaseName;  // this is the path to your StreamingAssets in iOS
		// then save to Application.persistentDataPath
		File.Copy(loadDb, filepath);
		
#elif UNITY_STANDALONE_OSX
		var loadDb = Application.dataPath + "/Resources/Data/StreamingAssets/" + DatabaseName;  // this is the path to your StreamingAssets in iOS
		// then save to Application.persistentDataPath
		File.Copy(loadDb, filepath);
#else
	var loadDb = Application.dataPath + "/StreamingAssets/" + DatabaseName;  // this is the path to your StreamingAssets in iOS
	// then save to Application.persistentDataPath
	File.Copy(loadDb, filepath);

#endif

            Debug.Log("Database written");
        }

        var dbPath = filepath;
#endif
        //_connection = new SQLiteConnection(dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create);
        _connection = new SQLiteConnection(_pass, dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create);
        Debug.Log("Final PATH: " + dbPath);

    }

@spiffai i will try it on android to see how it work! and come back right away with the answer.

Main issue on android seems to be that sqlite3_rekey is not found.

So...this is what I'm getting back on android (but not in Unity Editor):

I/Unity ( 4624): (Filename: ./artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51) I/Unity ( 4624): E/Unity ( 4624): EntryPointNotFoundException: sqlite3_rekey E/Unity ( 4624): at (wrapper managed-to-native) SQLite4Unity3d.SQLite3:Rekey (intptr,string,int) E/Unity ( 4624): at SQLite4Unity3d.SQLiteConnection.SetDbKey (System.String key) [0x00000] in <filename unknown>:0 E/Unity ( 4624): at DataService.SetPassword (System.String _pass) [0x00000] in <filename unknown>:0 E/Unity ( 4624): at ButtonManager.FireBaseLogin () [0x00000] in <filename unknown>:0 E/Unity ( 4624): at UnityEngine.Events.InvokableCall.Invoke (System.Object[] args) [0x00000] in <filename unknown>:0 E/Unity ( 4624): at UnityEngine.Events.InvokableCallList.Invoke (System.Object[] parameters) [0x00000] in <filename unknown>:0 E/Unity ( 4624): at UnityEngine.Events.UnityEventBase.Invoke (System.Object[] parameters) [0x00000] in <filename unknown>:0 E/Unity ( 4624): at UnityEngine.Events.UnityEvent.Invoke () [0x00000] in <filename unknown>:0 E/Unity ( 4624): at UnityEngine.UI.Button.Press () [0x00000] in <filename unknown>:0 E/Unity ( 4624): at UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) [0x00000] in <filename unknown>:0 E/Unity ( 4624): at UnityEngine.EventSystems.Ex

I got it working! I had to update the sqlite3 drivers from the github repo y'all mentioned at the beginning of the thread. Sorry about that :-0

Thank You for this - I have this working on the PC - I haven't attempted to use it on an android tablet yet which I intend to do.

Does anyone have an editor that they can open these databases in? SQLite Studio doesn't seem to support the encryption type.

@spiffai - I just tried this on android and I get a similar error to you - only mine is related to the Key - what did you change / do exactly? You say you updated the drivers - did you grab the dll from the other github? This was a branch of an older version of the project and is missing a few updates from here hence why trying to merge it over.

Thanks.

@AshleyBates Where is the updated version of this project?? I'm having the same issues building to Android, but on Standalone PC it's working. Made a whole new project to test and that isn't working either. Also @spiffai where exactly did you find those drivers?

@srivers8424 There is no actual update for this project just what code is included in the thread. It was taken out of this branch -
https://github.com/shreks7/SqliteForUnity

the above branch though is quite old and is missing items from this project, so we just took the encryption parts out. The problem is that on android it doesn't work, I believe there is something with the .so files that is causing this not to work properly. I don't know what @spiffai did to correct this, or if he is just using the older project.

I do need to get this to work on android I just haven't figured out how... yet.

@AshleyBates probably the .so need to be recompiled with the same code. or updated but where and how i'm trying to do it.

@GilbertoBitt - Yeah the original source for the .so files is here -

https://github.com/shreks7/AndroidSqlite3Encrypted

but I am out of my depth a bit with these, I have never worked with them before. I tried using the exisitng files in the other repository - and although I get past the initial error of the call not being found, I then get a database malformed error.

As you say we need to recompile with the new code and all should be good but I don't know how to compile up .so files.

first i don't think we need tu uso this https://github.com/shreks7/AndroidSqlite3Encrypted repository source code. the one that we have here already has the encryption but only on the dll files not on the .so file for the reading on android... i think is needed just to recompile the .so with the latest sqlite version possible similar to what here is done once a year updating the macos to support something basicly on the plugin version for windows have the encription and on android(.so file) don't becouse they r not on the same sqlite version. that must explain why the windows and macos work but not android or maybe even ios. becouse i never work generating .so file i make think complicated for me too. but i was trying.

@GilbertoBitt I have managed to create the .so files - but it's still not working - how I did it

  • Got the sql amalgamation files from sqlite.org
  • Installed Android Studio along with NDK
  • Created a c++ enabled project with NDK,
  • Added the amalgamation files
  • changed the CMakeList.txt to include the new files
  • Compiled
  • Grabbed the .so files out of the apk (can rename the apk to zip and access it) and placed them into Unity

Without Encryption I used these .so files successfully on my android device and my program worked as expected so I believe the .so files to be correct.

I enabled encryption and got the same errors as above - can't find sqlite3_key. Now in the Sqlite.c source file i used i found the following -

SQLITE_API int sqlite3_key(
sqlite3 db, / Database to be rekeyed */
const void pKey, int nKey / The key */
);

SQLITE_API int sqlite3_rekey(
sqlite3 db, / Database to be rekeyed */
const void pKey, int nKey / The new key */
);

which are the correct routines - however - they have the following around them

/**#ifdef SQLITE_HAS_CODEC*/

Not sure if this is a requirement and I am not sure how to ensure this is set inside Unity.

Either way I am stuck again, as these are modern up to date amalgamation files and the routines as far as I can tell exist - so I don't know why it isn't working correctly. I am delving into the unknown a lot with all of this though so it may be something simple / obvious that I have missed - I am hoping someone reads this and knows what it is!

@AshleyBates http://www.sqlite.org/android/doc/trunk/www/install.wiki i find this i will try soon. if u can try it u already have everything configured.

By default, SQLite is built with the following options:

-DSQLITE_ENABLE_FTS5
-DSQLITE_ENABLE_RTREE
-DSQLITE_ENABLE_JSON1
-DSQLITE_ENABLE_FTS3

i will try to compile the sqlcypher too but i'm worry about the working on ios too. since i use on all major plataforms and i want to use IL2CPP! but the one here. https://github.com/sqlcipher/sqlcipher that is a sqlite source code + cypher too! at same dll just needing replace the current ones.

Success!

Or at least for my scenario but I can give everyone else some big clues.

Ok so in my situation I am creating a SQLite database on a webservice, then I add a password to it, and download it to my android device - where up until now I couldn't access it.

Turns out you can't just encrypt a database that is not encrypted, you have to create a new database and export to it. As explained here -

https://discuss.zetetic.net/t/how-to-encrypt-a-plaintext-sqlite-database-to-use-sqlcipher-and-avoid-file-is-encrypted-or-is-not-a-database-errors/868

Documentation for the above is here (plain text to encrypted) -
https://www.zetetic.net/sqlcipher/sqlcipher-api/#sqlcipher_export

Now in my case I had to take the source of cipher, compile it up, and use that on my Windows side to create the new encrypted db, once I did this I could open them within unity using the key command.

If anyone needs a guide for compiling up SQLCipher for windows see here (a bit painful!)-
https://github.com/sqlitebrowser/sqlitebrowser/wiki/Win64-setup-%E2%80%94-Compiling-SQLCipher

Altneratively you can buy sqlcipher and the job is done for you.

What your problem is at this point, is that I don't believe that export option is available within Unity currently. I haven't looked into it there, but you need to ensure you are creating an encrypted db rather than creating one and then setting a key on it using rekey.

In addition I also found a tool to open and browse the encrypted files -
https://nightlies.sqlitebrowser.org/latest/

@AshleyBates
i try this
https://github.com/sqlitebrowser/sqlitebrowser/wiki/Win64-setup-%E2%80%94-Compiling-SQLCipher
but in this step :nmake /f Makefile.msc
i got some fatal error for missing some tcl library file

the file "Makefile.msc" witch downloaded from github little diffrent with the file in this tutorial
in the file from github they add some new code
i need to download earlier version or something?

@aysha319 I just used the link above - https://github.com/sqlcipher/sqlcipher

Search for each part of the make file you can see in the tutorial and make the edits.

Make sure you are using ActiveTcl 8.5 NOT 8.6, and that you have the correct version of OpenSSL (64bit)

@AshleyBates
yes i downloaded correct version
maybe try it one more time
Thank You

@AshleyBates sorry bother you again
finaly encrypt my sqlite db in ubuntu terminal by sqlcipher
and set a password
annnd change the codes for accept password
again got error
"SQLiteException: file is encrypted or is not a database"
in unity editor
you talking about latest version of sqlite3 in other source. where can i find that?
in this repository in scripts we have SQLite class. i changed that file and add dllimports codes

@aysha319 Sorry I am using the SQLCipher DLL inside unity.

I attached the file - this is the x86 dll.

I changed my SQLite.cs code inside unity to take a paremeter on the DLLImports so I could easily change the dll name, but I guess you could rename the DLL or change the code to the direct name.

sqlcipher.zip

@AshleyBates thank you :)
just use this library
and it's finally working