#pragma warning disable SA1512,SA1124 // Single-line comments should not be followed by blank line #if !NUGETFORUNITY_CLI using JetBrains.Annotations; #else using System.Security.Cryptography; #endif #region No ReShaper // ReSharper disable All // needed because 'JetBrains.Annotations.NotNull' and 'System.Diagnostics.CodeAnalysis.NotNull' collide if this file is compiled with a never version of Unity / C# using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; // ReSharper restore All #endregion using System; using System.Text; using UnityEngine; namespace NugetForUnity.Configuration { /// /// Helper to encrypt sensitive data so they don't need to be stored in plain-text inside the configuration file. /// internal static class ConfigurationEncryptionHelper { private static readonly byte[] EntropyBytes = Encoding.UTF8.GetBytes("NuGet"); #if !NUGETFORUNITY_CLI // on .net framework the type lives in 'System.Security' on .net standard it in 'System.Security.Cryptography.ProtectedData' [ItemCanBeNull] private static readonly Lazy ProtectedDataTypeLazy = new Lazy( () => Type.GetType("System.Security.Cryptography.ProtectedData, System.Security.Cryptography.ProtectedData") ?? Type.GetType("System.Security.Cryptography.ProtectedData, System.Security")); #endif /// /// Encrypts a string using the Windows only protected data library. /// /// The value to encrypt. /// The encrypted string. [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Keep for later.")] public static string EncryptString(string value) { if (Application.platform != RuntimePlatform.WindowsEditor) { Debug.LogError("Encrypted passwords are not supported on non-Windows platforms."); return value; } try { var decryptedByteArray = Encoding.UTF8.GetBytes(value); #if NUGETFORUNITY_CLI #pragma warning disable CA1416 // Validate platform compatibility var encryptedByteArray = ProtectedData.Protect(decryptedByteArray, EntropyBytes, DataProtectionScope.CurrentUser); #pragma warning restore CA1416 // Validate platform compatibility #else // when not compiled inside a unity editor we need to use reflection to access Windows only API var encryptedByteArray = ProtectOrUnprotectUsingReflection("Protect", decryptedByteArray); if (encryptedByteArray == null) { return value; } #endif var encryptedString = Convert.ToBase64String(encryptedByteArray); return encryptedString; } catch (Exception e) { Debug.LogError($"Failed to encrypt string, error: {e}"); return value; } } /// /// Decrypts a string using the Windows only protected data library. /// /// The value to decrypt. /// The encrypted string. public static string DecryptString(string encryptedString) { if (Application.platform != RuntimePlatform.WindowsEditor) { Debug.LogError("Encrypted passwords are not supported on non-Windows platforms."); return encryptedString; } try { var encryptedByteArray = Convert.FromBase64String(encryptedString); #if NUGETFORUNITY_CLI #pragma warning disable CA1416 // Validate platform compatibility var decryptedByteArray = ProtectedData.Unprotect(encryptedByteArray, EntropyBytes, DataProtectionScope.CurrentUser); #pragma warning restore CA1416 // Validate platform compatibility #else // when not compiled inside a unity editor we need to use reflection to access Windows only API var decryptedByteArray = ProtectOrUnprotectUsingReflection("Unprotect", encryptedByteArray); if (decryptedByteArray == null) { return encryptedString; } #endif return Encoding.UTF8.GetString(decryptedByteArray); } catch (Exception e) { Debug.LogError($"Failed to decrypt string, error: {e}"); return encryptedString; } } #if !NUGETFORUNITY_CLI [CanBeNull] private static byte[] ProtectOrUnprotectUsingReflection(string methodName, byte[] data) { var protectedDataType = ProtectedDataTypeLazy.Value; if (protectedDataType == null) { Debug.LogError("Encrypted passwords are not supported: type 'System.Security.Cryptography.ProtectedData' was not found."); return null; } var method = protectedDataType.GetMethod(methodName) ?? throw new InvalidOperationException($"Can't find '{methodName}' type inside type: '{protectedDataType}'."); return (byte[])method.Invoke(null, new object[] { data, EntropyBytes, 0 }); } #endif } }