diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 8168c1121f..5fa78bbd5f 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1367,6 +1367,117 @@ namespace OpenSim.Framework return uuid; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string AESEncrypt(string secret, string plainText) + { + return AESEncryptString(secret, plainText, string.Empty); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string AESEncryptTo(string secret, string plainText, string ivString) + { + return AESEncryptString(secret, plainText, ivString); + } + /// + /// AES Encrypt a string using a password and a random or custom Initialization + /// Vector + /// + /// The secret encryption password or key. + /// The string or text to encrypt. + /// (optional) A string used to generate the Initialization Vector eg; an avatarID, a SecureSessionID, an object or script + /// ID... + /// A string composed by the Initialization Vector bytes and the + /// encrypted text bytes converted to lower case HexString and separated by " : " + private static string AESEncryptString(string secret, string plainText, string ivString= null) + { + if(string.IsNullOrEmpty(secret) || string.IsNullOrEmpty(plainText)) + return string.Empty; + + byte[] iv = string.IsNullOrEmpty(ivString) ? MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(UUID.Random().ToString())) : MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(ivString)); + byte[] aesKey = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(secret)); + byte[] encryptedText; + + using (Aes aes = Aes.Create()) + { + aes.Key = aesKey; + aes.IV = iv; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.PKCS7; + + ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); + using MemoryStream memoryStream = new(); + using CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write); + using (StreamWriter streamWriter = new(cryptoStream)) + { + streamWriter.Write(plainText); + streamWriter.Close(); + } + + encryptedText = memoryStream.ToArray(); + memoryStream.Dispose(); + cryptoStream.Dispose(); + } + + return string.Format("{0}:{1}", Convert.ToHexString(iv), Convert.ToHexString(encryptedText)).ToLower(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string AESDecrypt(string secret, string encryptedText) + { + return AESDecryptString(secret, encryptedText, string.Empty); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string AESDecryptFrom(string secret, string encryptedText, string ivString) + { + return AESDecryptString(secret, encryptedText, ivString); + } + + /// + /// AES Decrypt the string encrypted by AESEncryptString with the same password + /// and ivString used in the encryption. + /// + /// The secret decryption password or key. + /// The encrypted string or text. + /// The string used to generate the Initialization Vector + /// if used in the encription. eg; an avatarID, a SecureSessionID, an object or + /// script ID... + /// The decrypted string. + private static string AESDecryptString(string secret, string encryptedText, string ivString= null) + { + if(string.IsNullOrEmpty(secret) || string.IsNullOrEmpty(encryptedText)) + return string.Empty; + + byte[] iv; + byte[] buffer; + try{ + iv = string.IsNullOrEmpty(ivString) ? Convert.FromHexString(encryptedText.Split(":")[0].ToUpper()) : MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(ivString)); + buffer = Convert.FromHexString(encryptedText.Split(":")[1].ToUpper()); + }catch + { + return string.Empty; + } + + byte[] aesKey = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(secret)); + + using Aes aes = Aes.Create(); + aes.Key = aesKey; + aes.IV = iv; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.PKCS7; + + ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); + using MemoryStream memoryStream = new(buffer); + using CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read); + using StreamReader streamReader = new(cryptoStream); + + string ret = streamReader.ReadToEnd(); + memoryStream.Dispose(); + cryptoStream.Dispose(); + streamReader.Close(); + return ret; + } + public static int fast_distance2d(int x, int y) { x = Math.Abs(x); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 6306f088fb..c247fe16cb 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -6374,5 +6374,65 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return World.TryGetSceneObjectPart(id, out SceneObjectPart part) ? part.ParentGroup.GetSittingAvatarsCount() : 0; } + + public LSL_String osAESEncrypt(string secret, string plainText) + { + if(string.IsNullOrEmpty(secret) || string.IsNullOrEmpty(plainText)) + return LSL_String.Empty; + + LSL_String ret = Util.AESEncrypt(secret, plainText); + if(string.IsNullOrEmpty(ret)){ + OSSLError("osAESEncrypt: Failed to encrypt!"); + return LSL_String.Empty; + }else + { + return ret; + } + } + + public LSL_String osAESDecrypt(string secret, string encryptedText) + { + if(string.IsNullOrEmpty(secret) || string.IsNullOrEmpty(encryptedText)) + return LSL_String.Empty; + + LSL_String ret = Util.AESDecrypt(secret, encryptedText); + if(string.IsNullOrEmpty(ret)){ + OSSLError("osAESDecrypt: Failed to Decrypt!"); + return LSL_String.Empty; + }else + { + return ret; + } + } + + public LSL_String osAESEncryptTo(string secret, string plainText, string ivString) + { + if(string.IsNullOrEmpty(secret) || string.IsNullOrEmpty(plainText) || string.IsNullOrEmpty(ivString)) + return LSL_String.Empty; + + LSL_String ret = Util.AESEncryptTo(secret, plainText, ivString); + if(string.IsNullOrEmpty(ret)){ + OSSLError("osAESEncryptTo: Failed to encrypt!"); + return LSL_String.Empty; + }else + { + return ret; + } + } + + public LSL_String osAESDecryptFrom(string secret, string encryptedText, string ivString) + { + if(string.IsNullOrEmpty(secret) || string.IsNullOrEmpty(encryptedText) || string.IsNullOrEmpty(ivString)) + return LSL_String.Empty; + + LSL_String ret = Util.AESDecryptFrom(secret, encryptedText, ivString); + if(string.IsNullOrEmpty(ret)){ + OSSLError("osAESDecryptFrom: Failed to decrypt!"); + return LSL_String.Empty; + }else + { + return ret; + } + } } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs index e4a9ae9607..aaa79c64ee 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -617,5 +617,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces LSL_Integer osGetPrimCount(LSL_Key object_id); LSL_Integer osGetSittingAvatarsCount(); LSL_Integer osGetSittingAvatarsCount(LSL_Key object_id); + LSL_String osAESEncrypt(string secret, string plainText); + LSL_String osAESEncryptTo(string secret, string plainText, string ivString); + LSL_String osAESDecrypt(string secret, string encryptedText); + LSL_String osAESDecryptFrom(string secret, string encryptedText, string ivString); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index 046a6d3af1..9f31f80d67 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -1750,5 +1750,29 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase return m_OSSL_Functions.osGetSittingAvatarsCount(object_id); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public LSL_String osAESEncrypt(string secret, string plainText) + { + return m_OSSL_Functions.osAESEncrypt(secret, plainText); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public LSL_String osAESEncryptTo(string secret, string plainText, string ivString) + { + return m_OSSL_Functions.osAESEncryptTo(secret, plainText, ivString); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public LSL_String osAESDecrypt(string secret, string encryptedText) + { + return m_OSSL_Functions.osAESDecrypt(secret, encryptedText); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public LSL_String osAESDecryptFrom(string secret, string encryptedText, string ivString) + { + return m_OSSL_Functions.osAESDecryptFrom(secret, encryptedText, ivString); + } + } } diff --git a/bin/ScriptSyntax.xml b/bin/ScriptSyntax.xml index 176432a194..081c063e6f 100644 --- a/bin/ScriptSyntax.xml +++ b/bin/ScriptSyntax.xml @@ -8757,5 +8757,39 @@ fc2639fd-5d16-66bf-d8ab-2e4d754c816d returnstring arguments + osAESEncrypt + + returnstring + arguments + secrettypestring + plainTexttypestring + + + osAESEncryptTo + + returnstring + arguments + secrettypestring + plainTexttypestring + ivStringtypestring + + + osAESDecrypt + + returnstring + arguments + secrettypestring + encryptedTexttypestring + + + osAESDecryptFrom + + returnstring + arguments + secrettypestring + encryptedTexttypestring + ivStringtypestring + + \ No newline at end of file