diff --git a/FnePeer.cs b/FnePeer.cs
index 0787a70..bff5c91 100644
--- a/FnePeer.cs
+++ b/FnePeer.cs
@@ -8,6 +8,7 @@
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2022-2023 Bryan Biedenkapp, N2PLL
+* Copyright (C) 2024 Caleb, KO4UYJ
*
*/
@@ -117,7 +118,7 @@ namespace fnecore
///
///
///
- public FnePeer(string systemName, uint peerId, string address, int port) : this(systemName, peerId, new IPEndPoint(IPAddress.Parse(address), port))
+ public FnePeer(string systemName, uint peerId, string address, int port, string PresharedKey = null) : this(systemName, peerId, new IPEndPoint(IPAddress.Parse(address), port), PresharedKey)
{
/* stub */
}
@@ -128,11 +129,14 @@ namespace fnecore
///
///
///
- public FnePeer(string systemName, uint peerId, IPEndPoint endpoint) : base(systemName, peerId)
+ public FnePeer(string systemName, uint peerId, IPEndPoint endpoint, string PresharedKey = null) : base(systemName, peerId)
{
masterEndpoint = endpoint;
client = new UdpReceiver();
+ if (PresharedKey != null)
+ client.SetPresharedKey(FneUtils.ConvertHexStringToPresharedKey(PresharedKey));
+
info = new PeerInformation();
info.PeerID = peerId;
info.Connection = false;
diff --git a/FneUtils.cs b/FneUtils.cs
index 0e15e58..82b0e73 100644
--- a/FneUtils.cs
+++ b/FneUtils.cs
@@ -8,6 +8,7 @@
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2022-2024 Bryan Biedenkapp, N2PLL
+* Copyright (C) 2024 Caleb, KO4UYJ
*
*/
@@ -619,6 +620,62 @@ namespace fnecore
return new string(result);
}
+ ///
+ /// Converts a hexadecimal string to a byte array.
+ ///
+ /// The hexadecimal string.
+ /// The byte array.
+ public static byte[] HexStringToByteArray(string hexString)
+ {
+ // Remove any spaces or non-hex characters if necessary
+ hexString = hexString.Replace(" ", "").Replace("-", "");
+
+ // Ensure the string length is even
+ if (hexString.Length % 2 != 0)
+ throw new ArgumentException("Invalid hex string length.");
+
+ byte[] byteArray = new byte[hexString.Length / 2];
+
+ for (int i = 0; i < hexString.Length; i += 2)
+ {
+ byteArray[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
+ }
+
+ return byteArray;
+ }
+
+ ///
+ /// Converts a hexadecimal string to a byte array for preshared key.
+ ///
+ /// The hexadecimal string.
+ /// The expected key length in bytes.
+ /// The byte array.
+ public static byte[] ConvertHexStringToPresharedKey(string hexString)
+ {
+ if (hexString.Length == 32)
+ {
+ // Double the key if it's 32 characters (16 hex pairs)
+ hexString += hexString;
+ Console.WriteLine("Half-length network preshared encryption key detected, doubling key on itself.");
+ }
+
+ if (hexString.Length == 64)
+ {
+ if (hexString.All(c => "0123456789abcdefABCDEF".Contains(c)))
+ {
+ return HexStringToByteArray(hexString);
+ }
+ else
+ {
+ throw new ArgumentException("Invalid characters in the network preshared encryption key.");
+ }
+ }
+ else
+ {
+ throw new ArgumentException("Invalid network preshared encryption key length, key should be 32 hex pairs, or 64 characters.");
+ }
+ }
+
///
/// Helper to display the ASCII representation of a hex dump.
///
diff --git a/Udp.cs b/Udp.cs
index e776445..1b4f9e7 100644
--- a/Udp.cs
+++ b/Udp.cs
@@ -87,7 +87,6 @@ namespace fnecore
public async Task Receive()
{
UdpReceiveResult res = await client.ReceiveAsync();
-
byte[] buffer = res.Buffer;
// are we crypto wrapped?
@@ -100,32 +99,27 @@ namespace fnecore
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);
-
- // 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(res.Buffer, 0, cryptoBuffer, 0, res.Buffer.Length - 2);
- }
+ int cryptedLen = res.Buffer.Length - 2;
+ byte[] cryptoBuffer = new byte[cryptedLen];
+ Buffer.BlockCopy(res.Buffer, 2, cryptoBuffer, 0, cryptedLen);
// decrypt
byte[] decrypted = Decrypt(cryptoBuffer, presharedKey);
- // finalize, cleanup buffers and replace with new
+ // 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
+ }
}
return new UdpFrame()
@@ -174,7 +168,6 @@ namespace fnecore
///
protected static byte[] Decrypt(byte[] buffer, byte[] key)
{
- byte[] decrypted = null;
using (AesManaged aes = new AesManaged()
{
KeySize = 256,
@@ -186,20 +179,14 @@ namespace fnecore
})
{
ICryptoTransform decryptor = aes.CreateDecryptor();
- using (MemoryStream ms = new MemoryStream())
+ using (MemoryStream ms = new MemoryStream(buffer))
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
+ using (MemoryStream resultStream = new MemoryStream())
{
- 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);
- }
+ cs.CopyTo(resultStream);
+ return resultStream.ToArray();
}
}
-
- return decrypted;
}
} // public abstract class UDPBase
@@ -289,36 +276,32 @@ namespace fnecore
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);
+ // calculate the length of the encrypted data
+ int cryptedLen = buffer.Length;
- // do we need to pad the original buffer to be block aligned?
- if (cryptedLen % AES_BLOCK_SIZE != 0)
+ // calculate the padding needed to make the buffer a multiple of AES_BLOCK_SIZE
+ int paddingLen = AES_BLOCK_SIZE - (cryptedLen % AES_BLOCK_SIZE);
+ if (paddingLen == AES_BLOCK_SIZE)
{
- 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);
+ paddingLen = 0; // no padding needed if already a multiple of AES_BLOCK_SIZE
}
- // encrypt
+ byte[] cryptoBuffer = new byte[cryptedLen + paddingLen];
+ Buffer.BlockCopy(buffer, 0, cryptoBuffer, 0, buffer.Length);
+
+ // encrypt the buffer
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;
+ // create the final buffer with the magic number and encrypted data
+ buffer = new byte[crypted.Length + 2];
+ Buffer.BlockCopy(crypted, 0, buffer, 2, crypted.Length);
+ FneUtils.WriteBytes(AES_WRAPPED_PCKT_MAGIC, ref buffer, 0);
+
+ // set the length to the actual length of the buffer to be sent
+ frame.Message = buffer;
}
- client.Send(buffer, frame.Message.Length);
+ client.Send(frame.Message, frame.Message.Length);
}
} // public class UdpReceiver : UdpBase
} // namespace fnecore