forked from aly/qstbak
111 lines
3.8 KiB
C#
111 lines
3.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Linq;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Questionable.Model.IO;
|
|
|
|
internal static class PathBundle
|
|
{
|
|
private const int NonceSize = 12;
|
|
|
|
private const int TagSize = 16;
|
|
|
|
private static readonly byte[] Magic = "QPB1"u8.ToArray();
|
|
|
|
internal static byte[] Encode(IReadOnlyList<KeyValuePair<string, byte[]>> entries, byte[] key, byte[] nonce)
|
|
{
|
|
using MemoryStream memoryStream = new MemoryStream();
|
|
using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
|
|
{
|
|
binaryWriter.Write(Magic);
|
|
binaryWriter.Write(entries.Count);
|
|
foreach (KeyValuePair<string, byte[]> entry in entries)
|
|
{
|
|
entry.Deconstruct(out var key2, out var value);
|
|
string s = key2;
|
|
byte[] array = value;
|
|
byte[] bytes = Encoding.UTF8.GetBytes(s);
|
|
binaryWriter.Write(checked((byte)bytes.Length));
|
|
binaryWriter.Write(bytes);
|
|
binaryWriter.Write(array.Length);
|
|
binaryWriter.Write(array);
|
|
}
|
|
}
|
|
using MemoryStream memoryStream2 = new MemoryStream();
|
|
using (DeflateStream deflateStream = new DeflateStream(memoryStream2, CompressionLevel.Optimal, leaveOpen: true))
|
|
{
|
|
deflateStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
|
|
}
|
|
byte[] array2 = memoryStream2.ToArray();
|
|
byte[] array3 = new byte[28 + array2.Length];
|
|
nonce.CopyTo(array3, 0);
|
|
using AesGcm aesGcm = new AesGcm(key, 16);
|
|
aesGcm.Encrypt(nonce, array2, array3.AsSpan(28), array3.AsSpan(12, 16));
|
|
return array3;
|
|
}
|
|
|
|
internal static List<KeyValuePair<string, T>> Deserialize<T>(Stream stream, byte[] key)
|
|
{
|
|
byte[] container = Decode(stream, key);
|
|
using MemoryStream memoryStream = new MemoryStream(container);
|
|
using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8);
|
|
if (!((ReadOnlySpan<byte>)binaryReader.ReadBytes(Magic.Length).AsSpan()).SequenceEqual((ReadOnlySpan<byte>)Magic))
|
|
{
|
|
throw new InvalidDataException("not a path bundle");
|
|
}
|
|
int num = binaryReader.ReadInt32();
|
|
(string Id, int Offset, int Length)[] entries = new(string, int, int)[num];
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
int count = binaryReader.ReadByte();
|
|
string item = Encoding.UTF8.GetString(binaryReader.ReadBytes(count));
|
|
int num2 = binaryReader.ReadInt32();
|
|
entries[i] = (Id: item, Offset: (int)memoryStream.Position, Length: num2);
|
|
memoryStream.Seek(num2, SeekOrigin.Current);
|
|
}
|
|
KeyValuePair<string, T>[] result = new KeyValuePair<string, T>[num];
|
|
Parallel.For(0, num, delegate(int num3)
|
|
{
|
|
(string Id, int Offset, int Length) tuple = entries[num3];
|
|
string item2 = tuple.Id;
|
|
int item3 = tuple.Offset;
|
|
int item4 = tuple.Length;
|
|
T val = JsonSerializer.Deserialize<T>(new ReadOnlySpan<byte>(container, item3, item4));
|
|
if (val == null)
|
|
{
|
|
throw new InvalidDataException("null payload for id " + item2);
|
|
}
|
|
T value = val;
|
|
result[num3] = new KeyValuePair<string, T>(item2, value);
|
|
});
|
|
return result.ToList();
|
|
}
|
|
|
|
private static byte[] Decode(Stream stream, byte[] key)
|
|
{
|
|
using MemoryStream memoryStream = new MemoryStream();
|
|
stream.CopyTo(memoryStream);
|
|
byte[] array = memoryStream.ToArray();
|
|
if (array.Length < 28)
|
|
{
|
|
throw new InvalidDataException("bundle truncated");
|
|
}
|
|
byte[] array2 = new byte[array.Length - 12 - 16];
|
|
using (AesGcm aesGcm = new AesGcm(key, 16))
|
|
{
|
|
aesGcm.Decrypt(array.AsSpan(0, 12), array.AsSpan(28), array.AsSpan(12, 16), array2);
|
|
}
|
|
using MemoryStream memoryStream2 = new MemoryStream();
|
|
using (DeflateStream deflateStream = new DeflateStream(new MemoryStream(array2), CompressionMode.Decompress))
|
|
{
|
|
deflateStream.CopyTo(memoryStream2);
|
|
}
|
|
return memoryStream2.ToArray();
|
|
}
|
|
}
|