using System; using System.Runtime.InteropServices; namespace NHSE.Core; public static class Encryption { private const int BlockSize = 16; private static void GetParam(ReadOnlySpan data, int index, Span result) { var rand = new XorShift128(data[(int)data[index] & 0x7F]); var prms = data[(int)(data[index + 1] & 0x7F)] & 0x7F; var rndRollCount = (prms & 0xF) + 1; for (var i = 0; i < rndRollCount; i++) rand.Next64(); for (var i = 0; i < result.Length; i++) result[i] = (byte)(rand.Next() >> 24); } /// /// Decrypts the using the in place. /// /// Header Data (at least 0x300 bytes) /// Encrypted SaveData (modified in place) public static void Decrypt(Span headerData, Span encData) { // First 256 bytes go unused; important data starts at offset 0x100 var sourceSpan = headerData.Slice(0x100, 0x200); ReadOnlySpan importantData = MemoryMarshal.Cast(sourceSpan); // Set up Key and Counter Span key = stackalloc byte[BlockSize]; Span counter = stackalloc byte[BlockSize]; GetParam(importantData, 0, key); GetParam(importantData, 2, counter); // Decrypt in place using AES-CTR AesCtr.Crypt(encData, key, counter); } private static CryptoFile GenerateHeaderFile(uint seed, ReadOnlySpan versionData) { // Generate 128 Random uints which will be used for params var random = new XorShift128(seed); Span encryptData = stackalloc uint[128]; for (var i = 0; i < encryptData.Length; i++) encryptData[i] = random.Next(); var headerData = new byte[0x300]; var key = new byte[BlockSize]; var ctr = new byte[BlockSize]; versionData[..0x100].CopyTo(headerData); MemoryMarshal.AsBytes(encryptData).CopyTo(headerData.AsSpan(0x100)); GetParam(encryptData, 0, key); GetParam(encryptData, 2, ctr); return new CryptoFile(headerData, key, ctr); } /// /// Encrypts the (savedata) using the provided . /// /// SaveData to encrypt /// Seed to encrypt with /// Version data to encrypt with /// Encrypted SaveData, and associated headerData public static EncryptedSaveFile Encrypt(ReadOnlySpan data, uint seed, ReadOnlySpan versionData) { // Generate header file and get key and counter var header = GenerateHeaderFile(seed, versionData); // Encrypt using AES-CTR var encData = AesCtr.Encrypt(data, header.Key.Span, header.Ctr.Span); return new EncryptedSaveFile(encData, header.Data); } }