You've already forked lidgren-network-gen3
mirror of
https://github.com/lidgren/lidgren-network-gen3.git
synced 2026-05-15 14:46:29 +09:00
Net encryption generalized to support more algorithms
This commit is contained in:
14
Lidgren.Network/Encryption/INetEncryption.cs
Normal file
14
Lidgren.Network/Encryption/INetEncryption.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public interface INetEncryption
|
||||
{
|
||||
bool Encrypt(NetOutgoingMessage msg);
|
||||
bool Decrypt(NetIncomingMessage msg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
78
Lidgren.Network/Encryption/NetBlockEncryptionBase.cs
Normal file
78
Lidgren.Network/Encryption/NetBlockEncryptionBase.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Base for a non-threadsafe encryption class
|
||||
/// </summary>
|
||||
public abstract class NetBlockEncryptionBase : INetEncryption
|
||||
{
|
||||
// temporary space for one block to avoid reallocating every time
|
||||
private byte[] m_tmp;
|
||||
|
||||
/// <summary>
|
||||
/// Block size in bytes for this cipher
|
||||
/// </summary>
|
||||
public abstract int BlockSize { get; }
|
||||
|
||||
public NetBlockEncryptionBase()
|
||||
{
|
||||
m_tmp = new byte[BlockSize];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
int payloadBitLength = msg.LengthBits;
|
||||
int numBytes = msg.LengthBytes;
|
||||
int blockSize = BlockSize;
|
||||
int numBlocks = (int)Math.Ceiling((double)numBytes / (double)blockSize);
|
||||
int dstSize = numBlocks * blockSize;
|
||||
|
||||
msg.EnsureBufferSize(dstSize * 8 + 2); // add 2 bytes for payload length at end
|
||||
msg.LengthBits = dstSize * 8; // length will automatically adjust +4 bytes when payload length is written
|
||||
|
||||
for(int i=0;i<numBlocks;i++)
|
||||
{
|
||||
EncryptBlock(msg.m_data, (i * blockSize), m_tmp);
|
||||
Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
|
||||
}
|
||||
|
||||
// add true payload length last
|
||||
msg.Write((ushort)payloadBitLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt an incoming message encrypted with corresponding Encrypt
|
||||
/// </summary>
|
||||
/// <param name="msg">message to decrypt</param>
|
||||
/// <returns>true if successful; false if failed</returns>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
int numEncryptedBytes = msg.LengthBytes - 2; // last 2 bytes is true bit length
|
||||
int blockSize = BlockSize;
|
||||
int numBlocks = numEncryptedBytes / blockSize;
|
||||
if (numBlocks * blockSize != numEncryptedBytes)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < numBlocks; i++)
|
||||
{
|
||||
DecryptBlock(msg.m_data, (i * blockSize), m_tmp);
|
||||
Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
|
||||
}
|
||||
|
||||
// read 16 bits of true payload length
|
||||
uint realSize = NetBitWriter.ReadUInt32(msg.m_data, 16, (numEncryptedBytes * 8));
|
||||
msg.m_bitLength = (int)realSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract void EncryptBlock(byte[] source, int sourceOffset, byte[] destination);
|
||||
protected abstract void DecryptBlock(byte[] source, int sourceOffset, byte[] destination);
|
||||
}
|
||||
}
|
||||
46
Lidgren.Network/Encryption/NetXorEncryption.cs
Normal file
46
Lidgren.Network/Encryption/NetXorEncryption.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Example class; not very good encryption
|
||||
/// </summary>
|
||||
public class NetXorEncryption : INetEncryption
|
||||
{
|
||||
private byte[] m_key;
|
||||
|
||||
public NetXorEncryption(byte[] key)
|
||||
{
|
||||
m_key = key;
|
||||
}
|
||||
|
||||
public NetXorEncryption(string key)
|
||||
{
|
||||
m_key = Encoding.ASCII.GetBytes(key);
|
||||
}
|
||||
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
int numBytes = msg.LengthBytes;
|
||||
for (int i = 0; i < numBytes; i++)
|
||||
{
|
||||
int offset = i % m_key.Length;
|
||||
msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
int numBytes = msg.LengthBytes;
|
||||
for (int i = 0; i < numBytes; i++)
|
||||
{
|
||||
int offset = i % m_key.Length;
|
||||
msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
137
Lidgren.Network/Encryption/NetXteaEncryption.cs
Normal file
137
Lidgren.Network/Encryption/NetXteaEncryption.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
/* Copyright (c) 2010 Michael Lidgren
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
|
||||
the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Security;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Methods to encrypt and decrypt data using the XTEA algorithm
|
||||
/// </summary>
|
||||
public sealed class NetXtea : NetBlockEncryptionBase
|
||||
{
|
||||
private const int c_blockSize = 8;
|
||||
private const int c_keySize = 16;
|
||||
private const int c_delta = unchecked((int)0x9E3779B9);
|
||||
|
||||
private readonly int m_numRounds;
|
||||
private readonly uint[] m_sum0;
|
||||
private readonly uint[] m_sum1;
|
||||
|
||||
public override int BlockSize { get { return c_blockSize; } }
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte key
|
||||
/// </summary>
|
||||
public NetXtea(byte[] key, int rounds)
|
||||
{
|
||||
if (key.Length < c_keySize)
|
||||
throw new NetException("Key too short!");
|
||||
|
||||
m_numRounds = rounds;
|
||||
m_sum0 = new uint[m_numRounds];
|
||||
m_sum1 = new uint[m_numRounds];
|
||||
uint[] tmp = new uint[8];
|
||||
|
||||
int num2;
|
||||
int index = num2 = 0;
|
||||
while (index < 4)
|
||||
{
|
||||
tmp[index] = BitConverter.ToUInt32(key, num2);
|
||||
index++;
|
||||
num2 += 4;
|
||||
}
|
||||
for (index = num2 = 0; index < 32; index++)
|
||||
{
|
||||
m_sum0[index] = ((uint)num2) + tmp[num2 & 3];
|
||||
num2 += -1640531527;
|
||||
m_sum1[index] = ((uint)num2) + tmp[(num2 >> 11) & 3];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte key
|
||||
/// </summary>
|
||||
public NetXtea(byte[] key)
|
||||
: this(key, 32)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String to hash for key
|
||||
/// </summary>
|
||||
public NetXtea(string key)
|
||||
: this(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key)), 32)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void EncryptBlock(byte[] source, int sourceOffset, byte[] destination)
|
||||
{
|
||||
uint v0 = BytesToUInt(source, sourceOffset);
|
||||
uint v1 = BytesToUInt(source, sourceOffset + 4);
|
||||
|
||||
for (int i = 0; i != m_numRounds; i++)
|
||||
{
|
||||
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ m_sum0[i];
|
||||
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ m_sum1[i];
|
||||
}
|
||||
|
||||
UIntToBytes(v0, destination, 0);
|
||||
UIntToBytes(v1, destination, 0 + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void DecryptBlock(byte[] source, int sourceOffset, byte[] destination)
|
||||
{
|
||||
// Pack bytes into integers
|
||||
uint v0 = BytesToUInt(source, sourceOffset);
|
||||
uint v1 = BytesToUInt(source, sourceOffset + 4);
|
||||
|
||||
for (int i = m_numRounds - 1; i >= 0; i--)
|
||||
{
|
||||
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ m_sum1[i];
|
||||
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ m_sum0[i];
|
||||
}
|
||||
|
||||
UIntToBytes(v0, destination, 0);
|
||||
UIntToBytes(v1, destination, 0 + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private static uint BytesToUInt(byte[] bytes, int offset)
|
||||
{
|
||||
uint retval = (uint)(bytes[offset] << 24);
|
||||
retval |= (uint)(bytes[++offset] << 16);
|
||||
retval |= (uint)(bytes[++offset] << 8);
|
||||
return (retval | bytes[++offset]);
|
||||
}
|
||||
|
||||
private static void UIntToBytes(uint value, byte[] destination, int destinationOffset)
|
||||
{
|
||||
destination[destinationOffset++] = (byte)(value >> 24);
|
||||
destination[destinationOffset++] = (byte)(value >> 16);
|
||||
destination[destinationOffset++] = (byte)(value >> 8);
|
||||
destination[destinationOffset++] = (byte)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,10 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Encryption\INetEncryption.cs" />
|
||||
<Compile Include="Encryption\NetBlockEncryptionBase.cs" />
|
||||
<Compile Include="Encryption\NetXorEncryption.cs" />
|
||||
<Compile Include="Encryption\NetXteaEncryption.cs" />
|
||||
<Compile Include="NamespaceDoc.cs" />
|
||||
<Compile Include="NetBigInteger.cs">
|
||||
<SubType>Code</SubType>
|
||||
@@ -68,7 +72,6 @@
|
||||
<Compile Include="NetConnectionStatus.cs" />
|
||||
<Compile Include="NetConstants.cs" />
|
||||
<Compile Include="NetDeliveryMethod.cs" />
|
||||
<Compile Include="NetEncryption.cs" />
|
||||
<Compile Include="NetException.cs" />
|
||||
<Compile Include="NetConnection.MTU.cs" />
|
||||
<Compile Include="NetFragmentationHelper.cs" />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
|
||||
@@ -104,17 +104,9 @@ namespace Lidgren.Network
|
||||
m_isFragment = false;
|
||||
}
|
||||
|
||||
public void Decrypt(NetXtea tea)
|
||||
public bool Decrypt(INetEncryption encryption)
|
||||
{
|
||||
// requires blocks of 8 bytes
|
||||
int blocks = m_bitLength / 64;
|
||||
if (blocks * 64 != m_bitLength)
|
||||
throw new NetException("Wrong message length for XTEA decrypt! Length is " + m_bitLength + " bits");
|
||||
|
||||
byte[] result = new byte[m_data.Length];
|
||||
for (int i = 0; i < blocks; i++)
|
||||
tea.DecryptBlock(m_data, (i * 8), result, (i * 8));
|
||||
m_data = result;
|
||||
return encryption.Decrypt(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -115,22 +115,11 @@ namespace Lidgren.Network
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt this message using the XTEA algorithm; no more writing can be done before sending it
|
||||
/// Encrypt this message using the provided algorithm; no more writing can be done before sending it or the message will be corrupt!
|
||||
/// </summary>
|
||||
public void Encrypt(NetXtea tea)
|
||||
public bool Encrypt(INetEncryption encryption)
|
||||
{
|
||||
// need blocks of 8 bytes
|
||||
WritePadBits();
|
||||
int blocksNeeded = (m_bitLength + 63) / 64;
|
||||
int missingBits = (blocksNeeded * 64) - m_bitLength;
|
||||
int missingBytes = NetUtility.BytesToHoldBits(missingBits);
|
||||
for (int i = 0; i < missingBytes; i++)
|
||||
Write((byte)0);
|
||||
|
||||
byte[] result = new byte[m_data.Length];
|
||||
for (int i = 0; i < blocksNeeded; i++)
|
||||
tea.EncryptBlock(m_data, (i * 8), result, (i * 8));
|
||||
m_data = result;
|
||||
return encryption.Encrypt(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Lidgren.Network
|
||||
public static class NetSRP
|
||||
{
|
||||
private static readonly NetBigInteger N = new NetBigInteger("0115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16);
|
||||
private static readonly NetBigInteger g = new NetBigInteger("2");
|
||||
private static readonly NetBigInteger g = NetBigInteger.Two;
|
||||
private static readonly NetBigInteger k = ComputeMultiplier();
|
||||
|
||||
private static HashAlgorithm GetHashAlgorithm()
|
||||
|
||||
Binary file not shown.
@@ -3,6 +3,7 @@ using System.Text;
|
||||
|
||||
using Lidgren.Network;
|
||||
using System.Security;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnitTests
|
||||
{
|
||||
@@ -13,48 +14,42 @@ namespace UnitTests
|
||||
//
|
||||
// Test XTEA
|
||||
//
|
||||
NetXtea xtea = new NetXtea("TopSecret");
|
||||
List<INetEncryption> algos = new List<INetEncryption>();
|
||||
|
||||
byte[] original = new byte[16];
|
||||
NetRandom.Instance.NextBytes(original);
|
||||
algos.Add(new NetXorEncryption("TopSecret"));
|
||||
algos.Add(new NetXtea("TopSecret"));
|
||||
|
||||
byte[] encrypted = new byte[original.Length];
|
||||
xtea.EncryptBlock(original, 0, encrypted, 0);
|
||||
xtea.EncryptBlock(original, 8, encrypted, 8);
|
||||
foreach (var algo in algos)
|
||||
{
|
||||
NetOutgoingMessage om = peer.CreateMessage();
|
||||
om.Write("Hallon");
|
||||
om.Write(42);
|
||||
om.Write(5, 5);
|
||||
om.Write(true);
|
||||
om.Write("kokos");
|
||||
int trueLen = om.LengthBits;
|
||||
om.Encrypt(algo);
|
||||
|
||||
byte[] decrypted = new byte[original.Length];
|
||||
xtea.DecryptBlock(encrypted, 0, decrypted, 0);
|
||||
xtea.DecryptBlock(encrypted, 8, decrypted, 8);
|
||||
// convert to incoming message
|
||||
NetIncomingMessage im = Program.CreateIncomingMessage(om.PeekDataBuffer(), om.LengthBits);
|
||||
im.Decrypt(algo);
|
||||
|
||||
// compare!
|
||||
for (int i = 0; i < original.Length; i++)
|
||||
if (original[i] != decrypted[i])
|
||||
throw new NetException("XTEA fail!");
|
||||
if (im.LengthBits != trueLen)
|
||||
throw new NetException("Length fail");
|
||||
|
||||
Console.WriteLine("XTEA OK");
|
||||
if (im.ReadString() != "Hallon")
|
||||
throw new NetException("fail");
|
||||
if (im.ReadInt32() != 42)
|
||||
throw new NetException("fail");
|
||||
if (im.ReadInt32(5) != 5)
|
||||
throw new NetException("fail");
|
||||
if (im.ReadBoolean() != true)
|
||||
throw new NetException("fail");
|
||||
if (im.ReadString() != "kokos")
|
||||
throw new NetException("fail");
|
||||
|
||||
NetOutgoingMessage om = peer.CreateMessage();
|
||||
om.Write("Hallon");
|
||||
om.Write(42);
|
||||
om.Write(5, 5);
|
||||
om.Write(true);
|
||||
om.Write("kokos");
|
||||
om.Encrypt(xtea);
|
||||
|
||||
// convert to incoming message
|
||||
NetIncomingMessage im = Program.CreateIncomingMessage(om.PeekDataBuffer(), om.LengthBits);
|
||||
im.Decrypt(xtea);
|
||||
|
||||
if (im.ReadString() != "Hallon")
|
||||
throw new NetException("fail");
|
||||
if (im.ReadInt32() != 42)
|
||||
throw new NetException("fail");
|
||||
if (im.ReadInt32(5) != 5)
|
||||
throw new NetException("fail");
|
||||
if (im.ReadBoolean() != true)
|
||||
throw new NetException("fail");
|
||||
if (im.ReadString() != "kokos")
|
||||
throw new NetException("fail");
|
||||
Console.WriteLine(algo.GetType().Name + " encryption verified");
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user