From cd45ecd3779dae79f561c17090c90f5a9b13763d Mon Sep 17 00:00:00 2001 From: Mino Date: Wed, 25 Mar 2020 16:10:52 +0900 Subject: [PATCH] Rename to EncryptedArgument --- .../SqexArg/EncryptedArgument.cs | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 Dalamud.Bootstrap/SqexArg/EncryptedArgument.cs diff --git a/Dalamud.Bootstrap/SqexArg/EncryptedArgument.cs b/Dalamud.Bootstrap/SqexArg/EncryptedArgument.cs new file mode 100644 index 000000000..54b449d86 --- /dev/null +++ b/Dalamud.Bootstrap/SqexArg/EncryptedArgument.cs @@ -0,0 +1,148 @@ +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using Dalamud.Bootstrap.Crypto; + +namespace Dalamud.Bootstrap.SqexArg +{ + internal sealed class EncryptedArgument : IDisposable + { + private static char[] ChecksumTable = new char[] + { + 'f', 'X', '1', 'p', 'G', 't', 'd', 'S', + '5', 'C', 'A', 'P', '4', '_', 'V', 'L' + }; + + /// + /// Denotes that no checksum is encoded. + /// + private const char NoChecksumMarker = '!'; + + /// + /// A data that is not encrypted. + /// + private IMemoryOwner m_data; + + /// + /// Creates an object that can take (e.g. /T=1234) + /// + /// A data that is not encrypted. + /// + /// This takes the ownership of the data. + /// + public EncryptedArgument(IMemoryOwner data) + { + m_data = data; + } + + public EncryptedArgument(string argument) + { + var buffer = MemoryPool.Shared.Rent(Encoding.UTF8.GetByteCount(argument)); + Encoding.UTF8.GetBytes(argument, buffer.Memory.Span); + + m_data = buffer; + } + + public void Dispose() + { + m_data?.Dispose(); + m_data = null!; + } + + /// + /// + /// + /// + /// An encrypted payload encoded in url-safe base64 string extracted. The value is undefined if the function fails. + /// A checksum of the key extracted. The value is undefined if the function fails. + /// Returns true on success, false otherwise. + public static bool Extract(string argument, out string payload, out char checksum) + { + // must start with //**sqex0003, some characters, one checksum character and end with **// + var regex = new Regex(@"^\/\/\*\*sqex0003(?.+)(?.)\*\*\/\/$"); + + var match = regex.Match(argument); + if (!match.Success) + { + payload = ""; + checksum = '\0'; + return false; + } + + payload = match.Groups["payload"].Value; + checksum = match.Groups["checksum"].Value[0]; + return true; + } + + public static EncryptedArgument FromEncryptedData(string argument, uint key) + { + // Create the key + Span keyBytes = stackalloc byte[8]; + CreateKey(key, keyBytes); + + // Allocate the buffer to store decrypted data + var decryptedData = CreateAlignedBuffer(encryptedData.Length); + + // Decrypt the data with the key + try + { + var blowfish = new Blowfish(keyBytes); + blowfish.Decrypt(encryptedData, decryptedData.Memory.Span); + } + catch (Exception) + { + decryptedData?.Dispose(); // TODO: clean up this thing? + throw; + } + + return new EncryptedArgument(decryptedData); + } + + private static IMemoryOwner CreateAlignedBuffer(int minimumSize) + { + // align (by padding) to block size if needed + throw new NotImplementedException("TODO"); + } + + /// + /// + /// + /// + /// + private static bool CheckDecryptedData(ReadOnlySpan decryptedData) + { + // TODO + return false; + } + + /// + /// Formats the key. + /// + /// A secret key. + /// A buffer where formatted key will be stored. This must be larger than 8 bytes. + private static void CreateKey(uint key, Span destination) + { + if (!Utf8Formatter.TryFormat(key, destination, out var _, new StandardFormat('x', 8))) + { + var message = $"BUG: Could not create a key"; // This should not fail but.. + throw new InvalidOperationException(message); + } + } + + /// + /// Converts the url-safe variant of base64 string to bytes. + /// + /// A url-safe variant of base64 string. + private static byte[] DecodeUrlSafeBase64(string payload) + { + var base64Str = payload + .Replace('-', '+') + .Replace('_', '/'); + + return Convert.FromBase64String(base64Str); + } + } +}