WingMan

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 33  →  ?path2? @ 34
/trunk/WingMan/Communication/MqttCommunication.cs
@@ -1,8 +1,10 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using LZ4;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Extensions.ManagedClient;
@@ -182,8 +184,24 @@
{
try
{
e.ApplicationMessage.Payload = await AES.Decrypt(e.ApplicationMessage.Payload, Password);
using (var inputStream = new MemoryStream(e.ApplicationMessage.Payload))
{
using (var decryptedStream = await AES.Decrypt(inputStream, Password))
{
using (var lz4Decompress = new LZ4Stream(decryptedStream, CompressionMode.Decompress))
{
using (var outpuStream = new MemoryStream())
{
await lz4Decompress.CopyToAsync(outpuStream);
 
outpuStream.Position = 0L;
 
e.ApplicationMessage.Payload = outpuStream.ToArray();
}
}
}
}
 
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnMessageReceived?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
@@ -268,8 +286,24 @@
{
try
{
e.ApplicationMessage.Payload = await AES.Decrypt(e.ApplicationMessage.Payload, Password);
using (var inputStream = new MemoryStream(e.ApplicationMessage.Payload))
{
using (var decryptedStream = await AES.Decrypt(inputStream, Password))
{
using (var lz4Decompress = new LZ4Stream(decryptedStream, CompressionMode.Decompress))
{
using (var outpuStream = new MemoryStream())
{
await lz4Decompress.CopyToAsync(outpuStream);
 
outpuStream.Position = 0L;
 
e.ApplicationMessage.Payload = outpuStream.ToArray();
}
}
}
}
 
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnMessageReceived?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
@@ -342,32 +376,44 @@
 
public async Task Broadcast(string topic, byte[] payload)
{
using (var payloadStream = new MemoryStream(await AES.Encrypt(payload, Password)))
using (var compressStream = new MemoryStream())
{
switch (Type)
using (var lz4Stream = new LZ4Stream(compressStream, CompressionMode.Compress))
{
case MqttCommunicationType.Client:
await Client.PublishAsync(new[]
await lz4Stream.WriteAsync(payload, 0, payload.Length);
await lz4Stream.FlushAsync();
 
compressStream.Position = 0L;
 
using (var outputStream = await AES.Encrypt(compressStream, Password))
{
var data = outputStream.ToArray();
switch (Type)
{
new MqttApplicationMessage
{
Topic = topic,
Payload = payloadStream.ToArray(),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
}
});
break;
case MqttCommunicationType.Server:
await Server.PublishAsync(new[]
{
new MqttApplicationMessage
{
Topic = topic,
Payload = payloadStream.ToArray(),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
}
});
break;
case MqttCommunicationType.Client:
await Client.PublishAsync(new[]
{
new MqttApplicationMessage
{
Topic = topic,
Payload = data,
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
}
});
break;
case MqttCommunicationType.Server:
await Server.PublishAsync(new[]
{
new MqttApplicationMessage
{
Topic = topic,
Payload = data,
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
}
});
break;
}
}
}
}
}
/trunk/WingMan/Utilities/AES.cs
@@ -19,14 +19,15 @@
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Encrypts a string given a key and initialization vector.
/// Encrypts an input stream given a key and initialization vector.
/// </summary>
/// <param name="data">the string to encrypt</param>
/// <param name="inputStream">the stream to encrypt</param>
/// <param name="key">the encryption key</param>
/// <param name="separator">the separator to use between the cyphertext and the IV</param>
/// <returns>Base64 encoded encrypted data</returns>
public static async Task<byte[]> Encrypt(byte[] data, string key, string separator = ":")
/// <returns>an encrypted stream containing the salt and the data</returns>
public static async Task<MemoryStream> Encrypt(Stream inputStream, string key)
{
var outputStream = new MemoryStream();
 
using (var rijdanelManaged = new RijndaelManaged())
{
// FIPS-197 / CBC
@@ -47,12 +48,60 @@
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
await inputStream.CopyToAsync(cryptoStream);
cryptoStream.FlushFinalBlock();
 
memoryStream.Position = 0L;
 
await outputStream.WriteAsync(salt, 0, AesKeySaltBytes);
await memoryStream.CopyToAsync(outputStream);
 
outputStream.Position = 0L;
 
return outputStream;
}
}
}
}
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Encrypts a byte array given a key and initialization vector.
/// </summary>
/// <param name="data">the byte array to encrypt</param>
/// <param name="key">the encryption key</param>
/// <returns>an encrypted byte array</returns>
public static async Task<byte[]> Encrypt(byte[] data, string key)
{
using (var rijdanelManaged = new RijndaelManaged())
{
// FIPS-197 / CBC
rijdanelManaged.BlockSize = AesBlockSize;
rijdanelManaged.Mode = AesCipherMode;
rijdanelManaged.Padding = AesPaddingMode;
 
// Compute the salt and the IV from the key.
var salt = new byte[AesKeySaltBytes];
Rng.GetBytes(salt);
var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
 
using (var encryptor = rijdanelManaged.CreateEncryptor(rijdanelManaged.Key, rijdanelManaged.IV))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using (var inputStream = new MemoryStream(data))
{
await inputStream.CopyToAsync(cryptoStream);
cryptoStream.FlushFinalBlock();
 
inputStream.Position = 0L;
memoryStream.Position = 0L;
 
return salt.Concat(memoryStream.ToArray()).ToArray();
}
@@ -66,17 +115,64 @@
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Decrypts a Base64 encoded string using AES with a given key and initialization vector.
/// Decrypts a byte array using a salt and a key.
/// </summary>
/// <param name="data">
/// a string consisting of the cyphertext to decrypt in Base64 and the IV in Base64 separated by the
/// separator
/// </param>
/// <param name="inputStream">a salt and data stream to decrypt</param>
/// <param name="key">the encryption key</param>
/// <param name="separator">the separator to use between the cyphertext and the IV</param>
/// <returns>the decrypted data</returns>
public static async Task<byte[]> Decrypt(byte[] data, string key, string separator = ":")
/// <returns>a memory stream containing the decrypted data</returns>
public static async Task<MemoryStream> Decrypt(Stream inputStream, string key)
{
var outputStream = new MemoryStream();
 
var salt = new byte[AesKeySaltBytes];
await inputStream.ReadAsync(salt, 0, AesKeySaltBytes);
 
var text = new byte[inputStream.Length - AesKeySaltBytes];
await inputStream.ReadAsync(text, 0, (int) (inputStream.Length - AesKeySaltBytes));
 
//var salt = data.Take(AesKeySaltBytes).ToArray();
//var text = data.Skip(AesKeySaltBytes).ToArray();
 
using (var rijdanelManaged = new RijndaelManaged())
{
// FIPS-197 / CBC
rijdanelManaged.BlockSize = AesBlockSize;
rijdanelManaged.Mode = AesCipherMode;
rijdanelManaged.Padding = AesPaddingMode;
 
// Retrieve the key and the IV from the salt.
var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
 
using (var decryptor = rijdanelManaged.CreateDecryptor(rijdanelManaged.Key, rijdanelManaged.IV))
{
using (var memoryStream = new MemoryStream(text))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
await cryptoStream.CopyToAsync(outputStream);
 
outputStream.Position = 0L;
 
return outputStream;
}
}
}
}
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Decrypts a byte array using a salt and a key.
/// </summary>
/// <param name="data">a salt and data byte array to decrypt</param>
/// <param name="key">the encryption key</param>
/// <returns>a byte array containing the decrypted data</returns>
public static async Task<byte[]> Decrypt(byte[] data, string key)
{
var salt = data.Take(AesKeySaltBytes).ToArray();
var text = data.Skip(AesKeySaltBytes).ToArray();
 
@@ -123,4 +219,4 @@
return sb.ToString(0, size);
}
}
}
}
/trunk/WingMan/WingMan.csproj
@@ -42,6 +42,9 @@
<Reference Include="Gma.System.MouseKeyHook, Version=5.6.130.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MouseKeyHook.5.6.0\lib\net40\Gma.System.MouseKeyHook.dll</HintPath>
</Reference>
<Reference Include="LZ4, Version=1.0.15.93, Culture=neutral, PublicKeyToken=62e1b5ec1eec9bdd, processorArchitecture=MSIL">
<HintPath>..\packages\lz4net.1.0.15.93\lib\net4-client\LZ4.dll</HintPath>
</Reference>
<Reference Include="Mono.Data.Sqlite, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Data.Sqlite.Portable.1.0.3.5\lib\net4\Mono.Data.Sqlite.dll</HintPath>
</Reference>
/trunk/WingMan/packages.config
@@ -2,6 +2,7 @@
 
<packages>
<package id="InputSimulatorPlus" version="1.0.7" targetFramework="net452" />
<package id="lz4net" version="1.0.15.93" targetFramework="net452" />
<package id="Microsoft.Tpl.Dataflow" version="4.5.24" targetFramework="net452" />
<package id="Mono.Data.Sqlite.Portable" version="1.0.3.5" targetFramework="net452" />
<package id="MouseKeyHook" version="5.6.0" targetFramework="net452" />