Refactorings: * BinaryCollector -> BitWriter * BinaryDistributor -> BitReader Additions: * Output class for making serverside output pretty and more readable * Better RSA keys (private keys withheld) Changes: * Minor changes to all views and their rendering * Added corrective resizing to resize listener to prevent errant window sizes * Removed "default" language in favour of a purely priority-based system * NetContext now attempts to verify server identity before continuing to next context * Simplified common operations in Context * Minor updates to some layouts * Completed translations for english and swedish * Promise system now supports internal processing before notifying original caller * Bank interactor methods are now async * Added support for multiple accounts per user (separate repositories for money) * Removed test code from client program * Updated Database to support multiple accounts * Reimplemented RSA on the server side purely as an identity verification system on top of the networking layer (rather than part of the layer) * Added Account management endpoints * Added full support for System-sourced transactions * Added Account availability endpoint * Added verbose error responses
93 lines
4.2 KiB
C#
93 lines
4.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Tofvesson.Common;
|
|
using Tofvesson.Crypto;
|
|
|
|
namespace Common
|
|
{
|
|
// Helper methods. WithHeader() should really just be in Support.cs
|
|
public static class NetSupport
|
|
{
|
|
public enum Compression { int16, int32, int64 }
|
|
public static byte[] WithHeader(string message) => WithHeader(Encoding.UTF8.GetBytes(message));
|
|
public static byte[] WithHeader(byte[] message)
|
|
{
|
|
int i = BinaryHelpers.VarIntSize(message.Length);
|
|
byte[] nmsg = new byte[message.Length + i];
|
|
//Support.WriteToArray(nmsg, message.Length, 0);
|
|
BinaryHelpers.WriteVarInt(nmsg, 0, message.Length);
|
|
Array.Copy(message, 0, nmsg, i, message.Length);
|
|
Debug.WriteLine($"Compression: {nmsg.Length}/{Compress(nmsg, Compression.int16).Length}/{Compress(nmsg, Compression.int32).Length}/{Compress(nmsg, Compression.int64).Length}");
|
|
Debug.WriteLine($"Matches: {Support.ArraysEqual(nmsg, Decompress(Compress(nmsg)))}");
|
|
return nmsg;
|
|
}
|
|
|
|
public static byte[] Decompress(byte[] cmpMessage, Compression method = Compression.int32)
|
|
{
|
|
BitReader reader = new BitReader(cmpMessage);
|
|
byte[] decomp = new byte[reader.ReadUInt()];
|
|
int size = method == Compression.int16 ? 2 : method == Compression.int32 ? 4 : 8;
|
|
int count = (decomp.Length / size) + (decomp.Length % size == 0 ? 0 : 1);
|
|
for(int i = 0; i<count; ++i)
|
|
{
|
|
dynamic value = size == 2 ? reader.ReadUShort() : size == 4 ? reader.ReadUInt() : reader.ReadULong();
|
|
for (int j = Math.Min(size, decomp.Length - (i * size)) - 1; j >= 0; --j) decomp[(i * size) + j] = (byte)((int)(value >> (8 * j)) & 0xFF);
|
|
}
|
|
return decomp;
|
|
}
|
|
|
|
public static byte[] FromHeaded(byte[] msg, int offset) => msg.SubArray(offset + 4, offset + 4 + Support.ReadInt(msg, offset));
|
|
|
|
internal static void DoStateCheck(bool state, bool target)
|
|
{
|
|
if (state != target) throw new InvalidOperationException("Bad state!");
|
|
}
|
|
|
|
private delegate void WriteFunc(BitWriter writer, byte[] data, int index);
|
|
private static WriteFunc
|
|
func16 = (w, d, i) => w.WriteUShort(ReadUShort(d, i * 2)),
|
|
func32 = (w, d, i) => w.WriteUInt(ReadUInt(d, i * 4)),
|
|
func64 = (w, d, i) => w.WriteULong(ReadULong(d, i * 8));
|
|
private static byte[] Compress(byte[] data, Compression method = Compression.int32)
|
|
{
|
|
int size = method == Compression.int16 ? 2 : method == Compression.int32 ? 4 : 8;
|
|
int count = (data.Length / size) + (data.Length % size == 0 ? 0 : 1);
|
|
WriteFunc func = size == 2 ? func16 : size == 4 ? func32 : func64;
|
|
using (BitWriter writer = new BitWriter())
|
|
{
|
|
writer.WriteUInt((uint)data.Length);
|
|
for (int i = 0; i < count; ++i)
|
|
func(writer, data, i);
|
|
return writer.Finalize();
|
|
}
|
|
}
|
|
|
|
private static ushort ReadUShort(byte[] b, int offset) =>
|
|
(ushort)((ushort)TryReadByte(b, offset) |
|
|
(ushort)((ushort)TryReadByte(b, offset + 1) << 8));
|
|
|
|
private static uint ReadUInt(byte[] b, int offset) =>
|
|
(uint)TryReadByte(b, offset) |
|
|
((uint)TryReadByte(b, offset + 1) << 8) |
|
|
((uint)TryReadByte(b, offset + 2) << 16) |
|
|
((uint)TryReadByte(b, offset + 3) << 24);
|
|
|
|
private static ulong ReadULong(byte[] b, int offset) =>
|
|
(ulong)TryReadByte(b, offset) |
|
|
((ulong)TryReadByte(b, offset + 1) << 8) |
|
|
((ulong)TryReadByte(b, offset + 2) << 16) |
|
|
((ulong)TryReadByte(b, offset + 3) << 24) |
|
|
((ulong)TryReadByte(b, offset + 4) << 32) |
|
|
((ulong)TryReadByte(b, offset + 5) << 40) |
|
|
((ulong)TryReadByte(b, offset + 6) << 48) |
|
|
((ulong)TryReadByte(b, offset + 7) << 56);
|
|
|
|
private static byte TryReadByte(byte[] b, int idx) => idx >= b.Length ? (byte) 0 : b[idx];
|
|
}
|
|
}
|