Skip to content

Commit

Permalink
Added encryption to events!
Browse files Browse the repository at this point in the history
For custom objects you can do param.EncryptObject(key), remember that if you encrypt with a key.. you must decrypt with the same key!
  • Loading branch information
manups4e committed Dec 20, 2023
1 parent 4a40dc3 commit 40c9803
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 68 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/

# scripts folder
.scripts/
*.bat
*.log

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

Expand Down
2 changes: 2 additions & 0 deletions FxEvents.sln
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Global
SolutionGuid = {46F9C3B8-35E3-4A06-8775-EF96C3B56EA8}
EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
FxEvents\FxEvents.Shared\FxEvents.Shared.projitems*{4f57e7a2-97b7-499b-8f9a-57f0af74c9ac}*SharedItemsImports = 5
FxEvents\FxEvents.Shared\FxEvents.Shared.projitems*{5c4fecb5-c970-46aa-8536-e6dd716bdce0}*SharedItemsImports = 5
FxEvents\FxEvents.Shared\FxEvents.Shared.projitems*{7d81b737-b66a-4961-bfa0-6a69347eed78}*SharedItemsImports = 13
EndGlobalSection
EndGlobal
10 changes: 9 additions & 1 deletion FxEvents/FxEvents.Client/EventDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class EventDispatcher : BaseScript
internal static ClientGateway Events;
internal static bool Debug { get; set; }
internal static bool Initialized = false;
internal static string EncryptionKey = "";

internal static EventDispatcher Instance;

Expand Down Expand Up @@ -45,8 +46,15 @@ private static string SetOutboundPipelineString(string outboundString)
return @event;
}

public static void Initalize(string inboundEvent, string outboundEvent, string signatureEvent)
public static void Initalize(string inboundEvent, string outboundEvent, string signatureEvent, string encryptionKey)
{
if (string.IsNullOrWhiteSpace(encryptionKey))
{
Logger.Fatal("FXEvents: Encryption key cannot be empty, please add an encryption key or use generatekey command in console to generate one to save");
return;
}
EncryptionKey = encryptionKey;

if (string.IsNullOrWhiteSpace(signatureEvent))
{
Logger.Error("SignaturePipeline cannot be null, empty or whitespace");
Expand Down
4 changes: 2 additions & 2 deletions FxEvents/FxEvents.Client/EventSystem/ClientGateway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ public ClientGateway()

internal void AddEvents()
{
_eventDispatcher.AddEventHandler(InboundPipeline, new Action<byte[]>(async serialized =>
_eventDispatcher.AddEventHandler(InboundPipeline, new Action<byte[]>(async encrypted =>
{
try
{
await ProcessInboundAsync(new ServerId().Handle, serialized);
await ProcessInboundAsync(new ServerId().Handle, encrypted);
}
catch (Exception ex)
{
Expand Down
8 changes: 5 additions & 3 deletions FxEvents/FxEvents.Client/FxEvents.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Shared\**\*.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
Expand All @@ -102,4 +99,9 @@
<ItemGroup>
<EditorConfigFiles Remove="I:\workspace\fivem\curiosity\Curiosity\_vendor\FxEvents\FxEvents\FxEvents.Client\.editorconfig" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Copy SkipUnchangedFiles="true" SourceFiles="@(SourceFiles)" DestinationFolder="..\CompiledLibs\Client\" />
<Exec Command="IF EXIST &quot;$(SolutionDir)FxEvents\scripts\post_client.bat&quot; call &quot;$(SolutionDir)FxEvents\scripts\post_client.bat&quot; &gt; &quot;$(SolutionDir)FxEvents\scripts\post_client.log&quot; 2&gt;&amp;1" IgnoreExitCode="true" />
</Target>
<Import Project="..\FxEvents.Shared\FxEvents.Shared.projitems" Label="Shared" />
</Project>
20 changes: 19 additions & 1 deletion FxEvents/FxEvents.Server/EventDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class EventDispatcher : BaseScript
internal static ServerGateway Events { get; set; }
internal static bool Debug { get; set; }
internal static bool Initialized = false;
internal static string EncryptionKey = "";

internal static EventDispatcher Instance;

Expand All @@ -28,6 +29,16 @@ public EventDispatcher()
Instance = this;
string debugMode = API.GetResourceMetadata(API.GetCurrentResourceName(), "fxevents_debug_mode", 0);
Debug = debugMode == "yes" || debugMode == "true" || int.TryParse(debugMode, out int num) && num > 0;
API.RegisterCommand("generatekey", new Action<int, List<object>, string>(async (a, b, c) =>
{
if (a != 0) return;
Logger.Info("Generating random passfrase with a 50 words dictionary...");
Tuple<string, string> ret = await Encryption.GenerateKey();
string print = $"Here is your generated encryption key, save it in a safe place.\nThis key is not saved by FXEvents anywhere, so please store it somewhere safe, if you save encrypted data and loose this key, your data will be lost.\n" +
$"You can always generate new keys by using \"generatekey\" command.\n" +
$"Passfrase: {ret.Item1}\nEncrypted Passfrase: {ret.Item2}";
Logger.Info(print);
}), false);
}

private static string SetSignaturePipelineString(string signatureString)
Expand All @@ -49,8 +60,15 @@ private static string SetOutboundPipelineString(string outboundString)
return @event;
}

public static void Initalize(string inboundEvent, string outboundEvent, string signatureEvent)
public static void Initalize(string inboundEvent, string outboundEvent, string signatureEvent, string encryptionKey)
{
if (string.IsNullOrWhiteSpace(encryptionKey))
{
Logger.Fatal("FXEvents: Encryption key cannot be empty, please add an encryption key or use generatekey command in console to generate one to save.");
return;
}
EncryptionKey = encryptionKey;

if (string.IsNullOrWhiteSpace(signatureEvent))
{
Logger.Error("SignaturePipeline cannot be null, empty or whitespace");
Expand Down
16 changes: 6 additions & 10 deletions FxEvents/FxEvents.Server/EventSystem/ServerGateway.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FxEvents.Shared.EventSubsystem;
using FxEvents.Shared;
using FxEvents.Shared.EventSubsystem;
using FxEvents.Shared.Message;
using FxEvents.Shared.Serialization;
using FxEvents.Shared.Serialization.Implementations;
Expand Down Expand Up @@ -74,18 +75,15 @@ private void GetSignature([FromSource] string source)
}
}

private async void Inbound([FromSource] string source, byte[] buffer)
private async void Inbound([FromSource] string source, byte[] encrypted)
{
try
{
int client = int.Parse(source.Replace("net:", string.Empty));

if (!_signatures.TryGetValue(client, out string signature)) return;

using SerializationContext context = new SerializationContext(InboundPipeline, null, Serialization, buffer);

EventMessage message = context.Deserialize<EventMessage>();

EventMessage message = encrypted.DecryptObject<EventMessage>(EventDispatcher.EncryptionKey);

if (!VerifySignature(client, message, signature)) return;

Expand Down Expand Up @@ -115,17 +113,15 @@ public bool VerifySignature(int source, IMessage message, string signature)
return false;
}

private void Outbound([FromSource] string source, byte[] buffer)
private void Outbound([FromSource] string source, byte[] encrypted)
{
try
{
int client = int.Parse(source.Replace("net:", string.Empty));

if (!_signatures.TryGetValue(client, out string signature)) return;

using SerializationContext context = new SerializationContext(OutboundPipeline, null, Serialization, buffer);

EventResponseMessage response = context.Deserialize<EventResponseMessage>();
EventResponseMessage response = encrypted.DecryptObject<EventResponseMessage>(EventDispatcher.EncryptionKey);

if (!VerifySignature(client, response, signature)) return;

Expand Down
11 changes: 6 additions & 5 deletions FxEvents/FxEvents.Server/FxEvents.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,7 @@
<DebugType>portable</DebugType>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\Shared\**\*.cs" />
</ItemGroup>

<ItemGroup>
<ItemGroup>
<PackageReference Include="CitizenFX.Core.Server" Version="1.0.6335" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" ExcludeAssets="Compile" GeneratePathProperty="true" />
Expand All @@ -64,4 +60,9 @@
<ItemGroup>
<EditorConfigFiles Remove=".editorconfig" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Copy SkipUnchangedFiles="true" SourceFiles="@(SourceFiles)" DestinationFolder="..\CompiledLibs\Server\" />
<Exec Command="IF EXIST &quot;$(SolutionDir)FxEvents\scripts\post_server.bat&quot; call &quot;$(SolutionDir)FxEvents\scripts\post_server.bat&quot; &gt; &quot;$(SolutionDir)FxEvents\scripts\post_server.log&quot; 2&gt;&amp;1" IgnoreExitCode="true" />
</Target>
<Import Project="..\FxEvents.Shared\FxEvents.Shared.projitems" Label="Shared" />
</Project>
83 changes: 70 additions & 13 deletions FxEvents/FxEvents.Shared/Encryption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,119 @@
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace FxEvents.Shared
{
public static class Encryption
{
public static byte[] EncryptBytes(byte[] data, string strEncrKey)
static readonly Random random = new Random(DateTime.Now.Millisecond);
#region Byte encryption
private static byte[] EncryptBytes(byte[] data, string strEncrKey)
{
byte[] rgbIV = new byte[16];
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
using (RNGCryptoServiceProvider rng = new())
{
rng.GetBytes(rgbIV);
}

byte[] bytes;
using (SHA256Managed sha256 = new SHA256Managed())
using (SHA256Managed sha256 = new())
{
bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(strEncrKey));
}

using AesManaged aesAlg = new AesManaged();
using AesManaged aesAlg = new();
aesAlg.Key = bytes;
aesAlg.IV = rgbIV;

ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

using MemoryStream msEncrypt = new MemoryStream();
using MemoryStream msEncrypt = new();
// prepend the IV to the encrypted data
msEncrypt.Write(rgbIV, 0, rgbIV.Length);
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write);
csEncrypt.Write(data, 0, data.Length);
csEncrypt.FlushFinalBlock();
return msEncrypt.ToArray();
}

public static byte[] DecryptBytes(byte[] data, string sDecrKey)
private static byte[] DecryptBytes(byte[] data, string sDecrKey)
{
byte[] rgbIV = new byte[16];
Array.Copy(data, 0, rgbIV, 0, rgbIV.Length);

byte[] bytes;
using (SHA256Managed sha256 = new SHA256Managed())
using (SHA256Managed sha256 = new())
{
bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(sDecrKey));
}

using AesManaged aesAlg = new AesManaged();
using AesManaged aesAlg = new();
aesAlg.Key = bytes;
aesAlg.IV = rgbIV;

ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

using MemoryStream msDecrypt = new MemoryStream(data, rgbIV.Length, data.Length - rgbIV.Length);
using CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
using MemoryStream msDecrypted = new MemoryStream();
using MemoryStream msDecrypt = new(data, rgbIV.Length, data.Length - rgbIV.Length);
using CryptoStream csDecrypt = new(msDecrypt, decryptor, CryptoStreamMode.Read);
using MemoryStream msDecrypted = new();
csDecrypt.CopyTo(msDecrypted);
return msDecrypted.ToArray();
}
#endregion

public static byte[] EncryptObject<T>(this T obj, string key)
{
if (string.IsNullOrWhiteSpace(key))
throw new Exception("FXEvents: Encryption key cannot be empty!");
return EncryptBytes(obj.ToBytes(), key);
}

public static T DecryptObject<T>(this byte[] data, string key)
{
if (string.IsNullOrWhiteSpace(key))
throw new Exception("FXEvents: Encryption key cannot be empty!");
return DecryptBytes(data, key).FromBytes<T>();
}

internal static async Task<Tuple<string, string>> GenerateKey()
{
string[] words = ["Scalder", "Suscipient", "Sodalite", "Maharanis", "Mussier", "Abouts", "Geologized", "Antivenins", "Volcanized", "Heliskier", "Bedclothes", "Streamier", "Postulant", "Grizzle", "Folkies", "Poplars", "Stalls", "Chiefess", "Trip", "Untarred", "Cadillacs", "Fixings", "Overage", "Upbraider", "Phocas", "Galton", "Pests", "Saxifraga", "Erodes", "Bracketing", "Rugs", "Deprecate", "Monomials", "Subtracts", "Kettledrum", "Cometic", "Wrvs", "Phalangids", "Vareuse", "Pinchbecks", "Moony", "Scissoring", "Sarks", "Victresses", "Thorned", "Bowled", "Bakeries", "Printable", "Beethoven", "Sacher"];
int i = 0;
int length = random.Next(5, 10);
string passfrase = "";
while (i <= length)
{
await BaseScript.Delay(5);
string symbol = "";
if (i > 0)
symbol = "-";
passfrase += symbol + words[random.Next(words.Length - 1)];
i++;
}
return new(passfrase, passfrase.EncryptObject(GetRandomString(random.Next(30, 50))).BytesToString());
}

private static string GetRandomString(int size, bool lowerCase = false)
{
StringBuilder builder = new StringBuilder(size);
// Unicode/ASCII Letters are divided into two blocks
// (Letters 65�90 / 97�122):
// The first group containing the uppercase letters and
// the second group containing the lowercase.

// char is a single Unicode character
char offset = lowerCase ? 'a' : 'A';
const int lettersOffset = 26; // A...Z or a..z: length=26

for (int i = 0; i < size; i++)
{
char @char = (char)random.Next(offset, offset + lettersOffset);
builder.Append(@char);
}

return lowerCase ? builder.ToString().ToLower() : builder.ToString();
}
}
}
}
Loading

0 comments on commit 40c9803

Please sign in to comment.