diff --git a/Udp.cs b/Udp.cs
index e0a4a84..e776445 100644
--- a/Udp.cs
+++ b/Udp.cs
@@ -7,13 +7,16 @@
* @package DVM / Fixed Network Equipment Core Library
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
-* Copyright (C) 2022 Bryan Biedenkapp, N2PLL
+* Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL
*
*/
using System;
+using System.IO;
using System.Net;
using System.Net.Sockets;
+using System.Runtime.Intrinsics.X86;
+using System.Security.Cryptography;
using System.Threading.Tasks;
namespace fnecore
@@ -39,8 +42,14 @@ namespace fnecore
///
public abstract class UdpBase
{
+ protected const ushort AES_WRAPPED_PCKT_MAGIC = 0xC0FE;
+ protected const int AES_BLOCK_SIZE = 128;
+
protected UdpClient client;
+ protected bool isCryptoWrapped;
+ protected byte[] presharedKey;
+
/*
** Methods
*/
@@ -51,6 +60,24 @@ namespace fnecore
protected UdpBase()
{
client = new UdpClient();
+
+ isCryptoWrapped = false;
+ presharedKey = null;
+ }
+
+ ///
+ /// Sets the preshared encryption key.
+ ///
+ ///
+ public void SetPresharedKey(byte[] presharedKey)
+ {
+ if (presharedKey != null) {
+ this.presharedKey = presharedKey;
+ isCryptoWrapped = true;
+ } else {
+ this.presharedKey = null;
+ isCryptoWrapped = false;
+ }
}
///
@@ -60,72 +87,121 @@ namespace fnecore
public async Task Receive()
{
UdpReceiveResult res = await client.ReceiveAsync();
- return new UdpFrame()
+
+ byte[] buffer = res.Buffer;
+
+ // are we crypto wrapped?
+ if (isCryptoWrapped)
{
- Message = res.Buffer,
- Endpoint = res.RemoteEndPoint
- };
- }
- } // public abstract class UDPBase
+ if (presharedKey == null)
+ throw new InvalidOperationException("tried to read datagram encrypted with no key? this shouldn't happen BUGBUG");
- ///
- /// Class implementing a UDP listener (server).
- ///
- public class UdpListener : UdpBase
- {
- private IPEndPoint listen;
+ // does the network packet contain the appropriate magic leader?
+ ushort magic = FneUtils.ToUInt16(res.Buffer, 0);
+ if (magic == AES_WRAPPED_PCKT_MAGIC)
+ {
+ int cryptedLen = (res.Buffer.Length - 2) * sizeof(byte);
+ byte[] cryptoBuffer = new byte[res.Buffer.Length - 2];
+ Buffer.BlockCopy(res.Buffer, 0, cryptoBuffer, 0, res.Buffer.Length - 2);
- /*
- ** Properties
- */
+ // do we need to pad the original buffer to be block aligned?
+ if (cryptedLen % AES_BLOCK_SIZE != 0)
+ {
+ int alignment = AES_BLOCK_SIZE - (cryptedLen % AES_BLOCK_SIZE);
+ cryptedLen += alignment;
- ///
- /// Gets the for this .
- ///
- public IPEndPoint EndPoint => listen;
+ // reallocate buffer and copy
+ cryptoBuffer = new byte[cryptedLen];
+ Buffer.BlockCopy(res.Buffer, 0, cryptoBuffer, 0, res.Buffer.Length - 2);
+ }
- /*
- ** Methods
- */
+ // decrypt
+ byte[] decrypted = Decrypt(cryptoBuffer, presharedKey);
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- ///
- public UdpListener(string address, int port) : this(new IPEndPoint(IPAddress.Parse(address), port))
- {
- /* stub */
- }
+ // finalize, cleanup buffers and replace with new
+ if (decrypted != null)
+ buffer = decrypted;
+ else
+ buffer = new byte[0];
+ }
+ else
+ buffer = new byte[0]; // this will effectively discard packets without the packet magic
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- public UdpListener(IPEndPoint endpoint)
- {
- listen = endpoint;
- client = new UdpClient(listen);
+ return new UdpFrame()
+ {
+ Message = buffer,
+ Endpoint = res.RemoteEndPoint
+ };
}
///
///
///
- ///
- public void Send(UdpFrame frame)
+ ///
+ ///
+ ///
+ protected static byte[] Encrypt(byte[] buffer, byte[] key)
{
- client.Send(frame.Message, frame.Message.Length, frame.Endpoint);
+ byte[] encrypted = null;
+ using (AesManaged aes = new AesManaged()
+ {
+ KeySize = 256,
+ Key = key,
+ BlockSize = AES_BLOCK_SIZE,
+ Mode = CipherMode.ECB,
+ Padding = PaddingMode.Zeros,
+ IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+ })
+ {
+ ICryptoTransform encryptor = aes.CreateEncryptor();
+ using (MemoryStream ms = new MemoryStream())
+ using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
+ {
+ cs.Write(buffer, 0, buffer.Length);
+ encrypted = ms.ToArray();
+ }
+ }
+
+ return encrypted;
}
///
///
///
- ///
- public async Task SendAsync(UdpFrame frame)
+ ///
+ ///
+ ///
+ protected static byte[] Decrypt(byte[] buffer, byte[] key)
{
- return await client.SendAsync(frame.Message, frame.Message.Length, frame.Endpoint);
+ byte[] decrypted = null;
+ using (AesManaged aes = new AesManaged()
+ {
+ KeySize = 256,
+ Key = key,
+ BlockSize = 128,
+ Mode = CipherMode.ECB,
+ Padding = PaddingMode.Zeros,
+ IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+ })
+ {
+ ICryptoTransform decryptor = aes.CreateDecryptor();
+ using (MemoryStream ms = new MemoryStream())
+ using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
+ {
+ byte[] outBuf = new byte[16384];
+ int len = cs.Read(outBuf, 0, outBuf.Length);
+ if (len > 0)
+ {
+ decrypted = new byte[len];
+ Buffer.BlockCopy(outBuf, 0, decrypted, 0, len);
+ }
+ }
+ }
+
+ return decrypted;
}
- } // public class UdpListener : UdpBase
+ } // public abstract class UDPBase
///
///
@@ -205,7 +281,44 @@ namespace fnecore
///
public void Send(UdpFrame frame)
{
- client.Send(frame.Message, frame.Message.Length);
+ byte[] buffer = frame.Message;
+
+ // are we crypto wrapped?
+ if (isCryptoWrapped)
+ {
+ if (presharedKey == null)
+ throw new InvalidOperationException("tried to read datagram encrypted with no key? this shouldn't happen BUGBUG");
+
+ int cryptedLen = buffer.Length * sizeof(byte);
+ byte[] cryptoBuffer = new byte[buffer.Length];
+ Buffer.BlockCopy(buffer, 0, cryptoBuffer, 0, buffer.Length);
+
+ // do we need to pad the original buffer to be block aligned?
+ if (cryptedLen % AES_BLOCK_SIZE != 0)
+ {
+ int alignment = AES_BLOCK_SIZE - (cryptedLen % AES_BLOCK_SIZE);
+ cryptedLen += alignment;
+
+ // reallocate buffer and copy
+ cryptoBuffer = new byte[cryptedLen];
+ Buffer.BlockCopy(buffer, 0, cryptoBuffer, 0, buffer.Length);
+ }
+
+ // encrypt
+ byte[] crypted = Encrypt(cryptoBuffer, presharedKey);
+
+ // finalize, cleanup buffers and replace with new
+ buffer = new byte[cryptedLen + 2];
+ if (crypted != null)
+ {
+ Buffer.BlockCopy(crypted, 0, buffer, 2, cryptedLen);
+ FneUtils.WriteBytes(AES_WRAPPED_PCKT_MAGIC, ref buffer, 0);
+ }
+ else
+ return;
+ }
+
+ client.Send(buffer, frame.Message.Length);
}
} // public class UdpReceiver : UdpBase
} // namespace fnecore