About Encryption & Decryption

Discussion and help for Easy Save 3
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: About Encryption & Decryption

Post by Joel »

Hi there,

The link I sent was for the exception you need to catch, not an example of catching exceptions.

As catching exceptions is a basic part of programming rather than Easy Save, I recommend taking a look at tutorials on catching exceptions to understand how this works, as they're an essential part of coding:

https://www.w3schools.com/cs/cs_exceptions.asp

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Johnny Star
Posts: 29
Joined: Sat Mar 21, 2020 2:17 pm

Re: About Encryption & Decryption

Post by Johnny Star »

Hi Joel, hope you are doing well!

I took a break from this for some time... Now I'm back. I did read up on the link you shared (w3schools). It's a good read, thank you!
I also tried it on another phone and I have this new error.

Code: Select all

ArgumentException: Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.
ES3Internal.ES3Stream.CreateStream (ES3Settings settings, ES3Internal.ES3FileMode fileMode) (at F:/Desktop/Shark Pew Pew/SharkPewPew/Assets/Settings/Asset Store Packages/Easy Save 3/Scripts/Streams/ES3Stream.cs:65)
ES3Reader.Create (ES3Settings settings) (at F:/Desktop/Shark Pew Pew/SharkPewPew/Assets/Settings/Asset Store Packages/Easy Save 3/Scripts/Readers/ES3Reader.cs:342)
ES3.KeyExists (System.String key, ES3Settings settings) (at F:/Desktop/Shark Pew Pew/SharkPewPew/Assets/Settings/Asset Store Packages/Easy Save 3/Scripts/ES3.cs:1097)
ES3.KeyExists (System.String key) (at F:/Desktop/Shark Pew Pew/SharkPewPew/Assets/Settings/Asset Store Packages/Easy Save 3/Scripts/ES3.cs:1066)
User.LoadUser (System.Boolean newEntrySaveDataLocal, System.Boolean newEntrySaveDataCloud) (at F:/Desktop/Shark Pew Pew/SharkPewPew/Assets/Scripts/Game Data Classes/User/User.cs:63)
GameData.Start () (at F:/Desktop/Shark Pew Pew/SharkPewPew/Assets/Scripts/Photon Networking/GameData.cs:62)
***EDIT01***

Code: Select all

    public ES3Settings ES3Settings { get; private set; }


    private void SetupEasySaveSettings()
    {
        // Apply defaults as set within the inspector Runtime Settings of EasySave3.
        ES3Settings = new ES3Settings(true);

        if (ES3.FileExists())
        {
            try
            {
                ES3.KeyExists("test");
            }
            catch (FormatException)
            {
                // A FormatException was thrown in EasySave3 from:
                //   ES3Internal.ES3JSONReader..ctor(System.IO.Stream stream, ES3Settings settings, System.Boolean readHeaderAndFooter)(at Assets / Settings / Asset Store Packages / Easy Save 3 / Scripts / Readers / ES3JSONReader.cs:39)
                // The message:
                //   FormatException("Cannot load from file because the data in it is not JSON data, or the data is encrypted.\nIf the save data is encrypted, please ensure that encryption is enabled when you load, and that you are using the same password used to encrypt the data.");
                // Solution:
                //   Create a new ES3Settings object with password.
                Debug.Log($"FormatException - Exception caught. Attempting to load using password.");
                ES3Settings = new ES3Settings(ES3.EncryptionType.AES, "JohnnySuperShiningStar");
            }
        }
    }
I managed to handle the FormatException as mentioned in the very first post of this thread. All the loading will be using example:

Code: Select all

playerUserID = ES3.KeyExists("playerUserID", GameData.Instance.ES3Settings) == true ? ES3.Load<string>("playerUserID", GameData.Instance.ES3Settings) : "";
Is this how it should be done?
Is there any way to check if a bytes array was password protected and if it was, what was the password it was protected with (for debugging purposes only)?

But how do I handle the new ArgumentException error mentioned above?

***EDIT02***
As mentioned in EDIT01 on how I handle FormatException there's another issue I encounter on further testing.
Is there a way to open encrypted files and then save it without encryption? Why I'm asking is because I need to know if it's doable.

So what I did:
> clear slate: deleted save file
> inspector runtime settings have aes encryption on with a password
> ran the game
> created a save file normally in-game with save functions "ES3.Save<string>("playerNickname", playerNickname);"
> stopped the game
> inspector runtime settings set encryption to none
> ran the game
> loading data works fine due to how I handle the FormatException and load data as mentioned in EDIT01
> but I can no longer save using "ES3.Save<string>("playerNickname", playerNickname);" as it will throw FormatException
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: About Encryption & Decryption

Post by Joel »

Hi there,
Is there any way to check if a bytes array was password protected
The code you've posted does this.
...and if it was, what was the password it was protected with (for debugging purposes only)?
If this were possible then it would be possible for anyone to find out the password, so I'm afraid this isn't possible.
I also tried it on another phone and I have this new error.
This is likely because the password doesn't match their password.

In your case instead of detecting whether a file is encrypted, you're better off detecting whether the file can be decrypted. For example:

Code: Select all

    private void SetupEasySaveSettings()
    {
        // Apply defaults as set within the inspector Runtime Settings of EasySave3.
        ES3Settings = new ES3Settings(ES3.Encryption.AES, "JohnnySuperShiningStar");

        if (ES3.FileExists())
        {
            try
            {
                ES3.KeyExists("test", ES3Settings);
            }
            catch (Exception e)
            {
            	// If we can't load it with encryption, either the data isn't encrypted, the password is wrong, or the data is corrupt.
            	// Try loading without encryption to see whether the data is encrypted.
            	try
            	{
            		ES3.KeyExists("test", new ES3Settings(ES3.EncryptionType.None));
            	}
            	catch(Exception e)
            	{
                	// This means that the issue is that the password was wrong, or the data is corrupted.
                	// It's impossible to know which is the case as this would make it much easier for hackers to crack the password.
                	// In this case, just throw an error to the user saying that the password is incorrect or the save file is invalid.
                	// Most likely the password is incorrect, so enable encryption by default:
                	ES3Settings = new ES3Settings(ES3.EncryptionType.AES, "JohnnySuperShiningStar");
            }
        }
    }
Regarding the format exception, please could you create a brand new project with a basic scene which replicates this and private message it to me so I can take a closer look?

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Johnny Star
Posts: 29
Joined: Sat Mar 21, 2020 2:17 pm

Re: About Encryption & Decryption

Post by Johnny Star »

Hi Joel... I know this has been a long topic. Again I took a break from this to work on something else.
Getting ES3, my game logics and another third-party cloud saving plugin (not mentioning the name as you removed it the last time) is a big stress.
Regarding the format exception, please could you create a brand new project with a basic scene which replicates this and private message it to me so I can take a closer look?
I'll repro it when everything else fails. Because my project is in a big mess and taking the things out for the repro is another stress.

I have a question. I'm using encryption set in the inspector.
Saving to bytes before pushing to the cloud is this:

Code: Select all

...
        var cacheSettings = new ES3Settings(ES3.Location.Cache);
        ES3.Save<Dictionary<string, string>>("bagInventory", temp, cacheSettings);
        byte[] bytes = ES3.LoadRawBytes(cacheSettings);
...
Then pulling and reading off from cloud bytes it this:

Code: Select all

...
        var cacheSettings = new ES3Settings(ES3.Location.Cache);
        ES3.SaveRaw(data, cacheSettings);

        // Overwrite bag inventory items.
        if (ES3.KeyExists("bagInventory", cacheSettings))
        {
...
I'm doing this to read the bytes before encryption was implemeted for testing:

Code: Select all

        JUtil.Log(JUtil.LogHandle().Bag, $"<b>Bag ►</b> Attempting to save to cloud. Bytes to save:" +
            $"\nConvert.ToBase64String:\n{Convert.ToBase64String(bytes)}" +
            //$"\nSystem.Text.Encoding.Default.GetString: {System.Text.Encoding.Default.GetString(bytes)}" +
            $"\nSystem.Text.Encoding.UTF8.GetString:\n{System.Text.Encoding.UTF8.GetString(bytes)}");
Now when using encryption, obviously the bytes don't make sense. How can decrypt the bytes? I want to view what I'm sending and receiving. This is only for my debug messages.
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: About Encryption & Decryption

Post by Joel »

Hi there,

If you want to get the unencrypted bytes you would need to resave the data to a different file with encryption disabled and output this.

One thing to note: you're converting your data to a Base 64 String, when the data is UTF8 encoded. Instead to covert bytes to a string you should do:

Code: Select all

var str = cacheSettings.encoding.GetString(bytes);
All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Johnny Star
Posts: 29
Joined: Sat Mar 21, 2020 2:17 pm

Re: About Encryption & Decryption

Post by Johnny Star »

Thank you for:

Code: Select all

var str = cacheSettings.encoding.GetString(bytes);
So I changed it something like this now:

Code: Select all

    public void ReplaceWithCloudSavedData(byte[] data)
    {
        var cacheSettings = new ES3Settings(ES3.Location.Cache);
        ES3.SaveRaw(data, cacheSettings);
        JUtil.Log(JUtil.LogHandle().Bag, $"<b>Bag ►</b> Overwriting local data with the data retrieved from cloud. Bytes retrieved:\n{cacheSettings.encoding.GetString(data)}");

        var debugSettings = new ES3Settings("Debug", ES3.Location.File, ES3.EncryptionType.None);
        ES3.SaveRaw(data, debugSettings);
        JUtil.Log(JUtil.LogHandle().Bag, $"<b>Bag ►</b> Overwriting local data with the data retrieved from cloud. Bytes retrieved (decrypted):\n{debugSettings.encoding.GetString(data)}");
...
But both are showing encrypted data. Am I typing my codes wrong?

[EDIT01]
OR... Do you mean I have to do double saving? One encrypted to one file and another unencrypted to another file?
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: About Encryption & Decryption

Post by Joel »

OR... Do you mean I have to do double saving? One encrypted to one file and another unencrypted to another file?
This is correct.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Johnny Star
Posts: 29
Joined: Sat Mar 21, 2020 2:17 pm

Re: About Encryption & Decryption

Post by Johnny Star »

Ok. But why is there no way to decrypt it for debugging? The 'Load'/'LoadInto' functions decrypts it so is there no way to actually decrypt all the bytes to a string for easy debugging? I'm a bit confused on this approach.
[EDIT01]
Also, this seems ok when you are preparing to push data to the cloud. You can actually push the bytes and view it unencrypted by double saving.
But on receiving the encrypted bytes from the cloud, how can I then view what I'm receiving?
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: About Encryption & Decryption

Post by Joel »

Hi there,

If you private message me your invoice number I can send over an update which adds an ES3.EncryptBytes and ES3.DecryptBytes method which will do what you want.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Johnny Star
Posts: 29
Joined: Sat Mar 21, 2020 2:17 pm

Re: About Encryption & Decryption

Post by Johnny Star »

Alright, but hang on. I think I might have found a way.
I update as an '[EDITxx]' once done.

[EDIT01]
Ok it's working now!

Code: Select all

    public void ReplaceWithCloudSavedData(byte[] data)
    {
        var cacheSettings = new ES3Settings(ES3.Location.Cache);
        ES3.SaveRaw(data, cacheSettings);
        JUtil.Log(JUtil.LogHandle().Bag, $"<b>Bag ►</b> Overwriting local data with the data retrieved from cloud. Bytes retrieved:\n{cacheSettings.encoding.GetString(data)}");

        JUtil.Log(JUtil.LogHandle().Bag, $"<b>Bag ►</b> Overwriting local data with the data retrieved from cloud. Bytes retrieved (decrypted):\n{JUtil.RawBytesDecryptedToString(cacheSettings)}");
...

Code: Select all

    //----------------------------------------------------------------
    internal static Dictionary<string, ES3File> cachedFiles = new Dictionary<string, ES3File>();
    public static string RawBytesDecryptedToString(ES3Settings settings)
    {
        if (!cachedFiles.TryGetValue(settings.path, out ES3File cachedFile))
        {
            cachedFile = new ES3File(settings, false);
            cachedFiles.Add(settings.path, cachedFile);
        }
        cachedFile.settings = settings;

        var unencryptedSettings = (ES3Settings)settings.Clone();
        unencryptedSettings.encryptionType = ES3.EncryptionType.None;

        return unencryptedSettings.encoding.GetString(ES3.LoadRawBytes(unencryptedSettings));
    }
Post Reply