示例#1
0
        public static StorageMetadata CreateStorageFile(Stream dataStream, uint archiveNumber, ulong?profileKey)
        {
            StorageMetadata metadata = new StorageMetadata();

            metadata.ArchiveNumber = archiveNumber + 2;

            byte[] data = null;
            using (BinaryReader reader = new BinaryReader(dataStream))
            {
                data = reader.ReadBytes((int)reader.BaseStream.Length);
            }

            Array.Resize(ref data, data.Length + 1); // 1 byte null padding

            // Generate Spooky hash of data for later
            SpookyHash sh = new SpookyHash(0x155af93ac304200, 0x8ac7230489e7ffff);

            sh.Update(new SHA256Managed().ComputeHash(data));
            sh.Update(data);
            sh.Final(out metadata.Key[0], out metadata.Key[1]);

            var compressedData = Compress(data);
            var key            = GenerateKey(metadata, profileKey, true);

            metadata.StorageRaw       = XXTEA.Encrypt(compressedData, key);
            metadata.CompressedSize   = (uint)compressedData.Length;
            metadata.DecompressedSize = (uint)data.Length;

            return(metadata);
        }
示例#2
0
        private static void ParseStorageFile(StorageMetadata metadata, Stream storageStream, ulong?profileKey = null)
        {
            byte[] data = null;
            using (BinaryReader reader = new BinaryReader(storageStream))
            {
                data = reader.ReadBytes((int)reader.BaseStream.Length);
            }

            var rawSha256 = new SHA256Managed().ComputeHash(data);
            var sha256    = BitConverter.ToString(rawSha256).Replace("-", "");

            if (sha256 != metadata.SHA256)
            {
                throw new InvalidDataException("Invalid storage file. Corrupt or wrong file?");
            }

            if (metadata.StorageVersion == STORAGE_VERSION_V1)
            {
                var key           = GenerateKey(metadata, profileKey);
                var decryptedData = XXTEA.Decrypt(data, key);
                var output        = Decompress(metadata, decryptedData);

                // Verify data
                SpookyHash sh = new SpookyHash(0x155af93ac304200, 0x8ac7230489e7ffff);
                sh.Update(new SHA256Managed().ComputeHash(output));
                sh.Update(output);

                ulong hash1, hash2;
                sh.Final(out hash1, out hash2);

                ulong orig_hash1 = metadata.Key[0];
                ulong orig_hash2 = metadata.Key[1];

                if (orig_hash1 != hash1 || orig_hash2 != hash2)
                {
                    throw new InvalidDataException("Invalid decrypted data. Wrong key?");
                }

                metadata.StorageRaw = output;
            }
            else
            {
                SpookyHash sh = new SpookyHash(0x155af93ac304200, 0x8ac7230489e7ffff);
                sh.Update(rawSha256);
                sh.Update(data);

                ulong hash1, hash2;
                sh.Final(out hash1, out hash2);

                ulong orig_hash1 = metadata.Key[0];
                ulong orig_hash2 = metadata.Key[1];

                if (orig_hash1 != hash1 || orig_hash2 != hash2)
                {
                    throw new InvalidDataException("Invalid decrypted data. Wrong key?");
                }

                metadata.StorageRaw = data;
            }
        }
示例#3
0
        private static byte[] GenerateKey(StorageMetadata metadata, ulong?profileKey, bool storeProfileKey = false)
        {
            uint[] keydata_ints = { 0, 0, 0, 0 };

            uint k1_h = (uint)metadata.Key[0] & 0xffff;
            uint k1_d = (uint)(metadata.Key[0] >> 5) & 0x7fff800;
            uint k2_h = (uint)(metadata.Key[0] >> 32) & 0xffff;
            uint k2_d = (uint)((metadata.Key[0] >> 32) >> 5) & 0x7fff800;
            uint k3_h = (uint)metadata.Key[1] & 0xffff;
            uint k3_d = (uint)(metadata.Key[1] >> 5) & 0x7fff800;
            uint k4_h = (uint)(metadata.Key[1] >> 32) & 0xffff;
            uint k4_d = (uint)((metadata.Key[1] >> 32) >> 5) & 0x7fff800;

            // More murmurhash-like code
            uint temp = 0;

            // Byte 1
            temp            = F1(0x85EBCA6B, k1_h, k1_d, k2_h, k2_d);
            temp            = F2(((temp >> 6) + temp), metadata.ArchiveNumber);
            keydata_ints[0] = temp;

            // Byte 2
            temp            = F1(0xCC9E2D51, k3_h, k3_d, k4_h, k4_d);
            temp           += (temp >> 6);
            keydata_ints[1] = temp;

            // Byte 3
            temp            = F1(0x1b873593, k1_h, k1_d, k2_h, k2_d);
            temp           += (temp >> 6);
            keydata_ints[2] = temp;

            // Byte 4
            temp            = F1(0x85EBCA6B, k3_h, k3_d, k4_h, k4_d);
            temp           += (temp >> 6);
            keydata_ints[3] = temp;

            if (metadata.ProfileHash.HasValue || (profileKey.HasValue && storeProfileKey))
            {
                if (profileKey.HasValue)
                {
                    // Decrypt using Steam/GOG ID key
                    uint profile_key_h  = (uint)(profileKey.Value & 0xffff);
                    uint profile_key_d  = (uint)((profileKey.Value >> 5) & 0x7fff800);
                    uint profile_key_h2 = (uint)((profileKey.Value >> 0x20) & 0xffff);
                    uint profile_key_d2 = (uint)(((profileKey.Value >> 0x20) >> 5) & 0x7fff800);

                    temp  = F1(0x1b873593, profile_key_h, profile_key_d, profile_key_h2, profile_key_d2);
                    temp += (temp >> 6);

                    uint hashCheck = temp & 0xffff;

                    if (storeProfileKey)
                    {
                        metadata.ProfileHash = hashCheck;
                    }

                    keydata_ints[1] = F2(keydata_ints[1], temp);

                    if (hashCheck != metadata.ProfileHash)
                    {
                        throw new InvalidDataException("Invalid data: profile hash did not check out");
                    }
                }
                else
                {
                    throw new ArgumentException("Must provide profile key to decrypt this data");
                }
            }

            return(keydata_ints.SelectMany(BitConverter.GetBytes).ToArray());
        }
示例#4
0
        // Creation code
        public static void CreateMetadataFile(StorageMetadata metadata)
        {
            var sha256 = new SHA256Managed().ComputeHash(metadata.StorageRaw);

            // Murmurhash-like
            uint k1 = metadata.ArchiveNumber ^ 0x1422cb8c;
            uint h1 = RotateLeft(k1, 13) * 5 + 0xe6546b64;

            var input_key = Encoding.ASCII.GetBytes("NAESEVADNAYRTNRG");

            for (int i = 0; i < 4; i++)
            {
                input_key[i] = (byte)(h1 >> (i * 8));
            }

            List <uint> key = new List <uint>();

            for (int i = 0; i < input_key.Length; i += 4)
            {
                key.Add(BitConverter.ToUInt32(input_key, i));
            }

            var metadataBuffer = new byte[0x68];

            using (BinaryWriter writer = new BinaryWriter(new MemoryStream(metadataBuffer)))
            {
                writer.Write(0xeeeeeebe);
                writer.Write(0x7d0);

                writer.Write(metadata.Key[0]);
                writer.Write(metadata.Key[1]);

                writer.Write(sha256);
                writer.Write(metadata.DecompressedSize);
                writer.Write(metadata.CompressedSize);

                if (metadata.ProfileHash.HasValue)
                {
                    writer.Write(metadata.ProfileHash.Value);
                }
            }

            List <uint> data = new List <uint>();

            using (BinaryReader reader = new BinaryReader(new MemoryStream(metadataBuffer)))
            {
                while (reader.BaseStream.Position < reader.BaseStream.Length)
                {
                    data.Add(reader.ReadUInt32());
                }
            }

            uint hash = 0;
            uint cur  = 0;

            for (int itr1 = 0; itr1 < 8; itr1++)
            {
                hash += 0x9E3779B9;

                int key_idx = (int)((hash >> 2) & 3);

                int  idx = 0;
                uint tmp = 0;
                for (int itr2 = 0; itr2 < 0x19; itr2++, idx++)
                {
                    tmp        = ((data[idx + 1] >> 3) ^ (cur << 4)) + ((data[idx + 1] * 4) ^ (cur >> 5));
                    tmp       ^= (cur ^ key[(itr2 & 3) ^ key_idx]) + (data[idx + 1] ^ hash);
                    data[idx] += tmp;
                    cur        = data[idx];
                }

                tmp  = ((data[0] >> 3) ^ (cur << 4)) + ((data[0] * 4) ^ (cur >> 5));
                tmp ^= (cur ^ key[key_idx ^ 1]) + (data[0] ^ hash);
                data[data.Count - 1] += tmp;
                cur = data[data.Count - 1];
            }

            metadata.MetadataRaw = data.SelectMany(BitConverter.GetBytes).ToArray();
        }
示例#5
0
        private static byte[] Decompress(StorageMetadata metadata, byte[] data)
        {
            List <byte> output = new List <byte>();

            int i      = 0;
            int window = 0;

            while (i < metadata.CompressedSize)
            {
                var c = data[i++];

                for (int x = 1; x >= 0; x--)
                {
                    int size = ((c >> (0x04 * x)) & 0xf);

                    if (output.Count >= metadata.DecompressedSize)
                    {
                        break;
                    }

                    if (size == 0x0f)
                    {
                        do
                        {
                            size += data[i++];
                        } while (data[i - 1] == 0x0ff);
                    }

                    if (x == 1)
                    {
                        byte[] temp_data = new byte[size];
                        Array.Copy(data, i, temp_data, 0, size);
                        output.AddRange(temp_data);
                        i += size;

                        if (output.Count >= metadata.DecompressedSize)
                        {
                            break;
                        }

                        window = output.Count - BitConverter.ToUInt16(data, i);
                        i     += 2;
                    }
                    else
                    {
                        size += 4;

                        byte[] temp_data = new byte[size];
                        int    copySize  = size > (output.Count - window) ? (output.Count - window) : size;

                        for (int idx = 0; idx < copySize; idx++)
                        {
                            temp_data[idx] = output[window + idx];
                        }

                        int remaining = size - copySize;
                        int offset    = copySize;
                        while (remaining > 0)
                        {
                            copySize = remaining > offset ? offset : remaining;
                            Array.Copy(temp_data, 0, temp_data, offset, copySize);
                            remaining -= copySize;
                            offset    += copySize;
                        }

                        output.AddRange(temp_data);
                    }
                }
            }

            return(output.ToArray());
        }
示例#6
0
        // Loading code
        private static StorageMetadata ParseMetadataFile(Stream input, uint archiveNumber)
        {
            archiveNumber += 2;

            List <uint> data = new List <uint>();

            using (BinaryReader reader = new BinaryReader(input))
            {
                while (reader.BaseStream.Position < reader.BaseStream.Length)
                {
                    data.Add(reader.ReadUInt32());
                }
            }

            if (data.Count * 4 != 0x68)
            {
                throw new InvalidDataException("Invalid metadata file. Expected a file of size 0x68");
            }

            // Murmurhash-like
            uint k1 = archiveNumber ^ 0x1422cb8c;
            uint h1 = RotateLeft(k1, 13) * 5 + 0xe6546b64;

            var input_key = Encoding.ASCII.GetBytes("NAESEVADNAYRTNRG");

            for (int i = 0; i < 4; i++)
            {
                input_key[i] = (byte)(h1 >> (i * 8));
            }

            List <uint> key = new List <uint>();

            for (int i = 0; i < input_key.Length; i += 4)
            {
                key.Add(BitConverter.ToUInt32(input_key, i));
            }

            uint hash = 0xf1bbcdc8;

            for (int itr1 = 0; itr1 < 8; itr1++)
            {
                int key_idx = (int)((hash >> 2) & 3);

                uint cur = data[0];
                int  idx = data.Count - 1;
                uint tmp = 0;
                for (int itr2 = 0x19; itr2 > 0; itr2--, idx--)
                {
                    tmp  = ((cur >> 3) ^ (data[idx - 1] << 4)) + ((cur * 4) ^ (data[idx - 1] >> 5));
                    tmp ^= (data[idx - 1] ^ key[(itr2 & 3) ^ key_idx]) + (cur ^ hash);

                    data[idx] -= tmp;
                    cur        = data[idx];
                }

                idx      = data.Count - 1;
                tmp      = (((cur >> 3) ^ (data[idx] << 4)) + ((cur * 4) ^ (data[idx] >> 5))) & 0xffffffff;
                tmp     ^= (((data[idx] ^ key[key_idx]) + (cur ^ hash)) & 0xffffffff);
                data[0] -= tmp;
                hash    += 0x61c88647;
            }

            // Check fixed header values to make sure decryption worked
            if (data[0] != 0xeeeeeebe || data[1] != 0x7d0)
            {
                throw new InvalidDataException("Invalid metadata archive");
            }

            // Get SHA-256 hash from data
            var sha256 = BitConverter.ToString(data.Skip(6).Take(8).SelectMany(BitConverter.GetBytes).ToArray()).Replace("-", "");

            // Get encryption key for data
            var dataKey = data.Skip(2).Take(4).ToArray();

            StorageMetadata metadata = new StorageMetadata();

            metadata.Key              = new ulong[] { ((ulong)dataKey[1] << 32) | dataKey[0], ((ulong)dataKey[3] << 32) | dataKey[2] };
            metadata.SHA256           = sha256;
            metadata.DecompressedSize = data[14];
            metadata.CompressedSize   = data[15];
            metadata.ArchiveNumber    = archiveNumber;

            if (data[16] != 0)
            {
                metadata.ProfileHash = data[16];
            }

            return(metadata);
        }