public void TestEntryDecrypt() { var e = SaveReader.GetSaveFileEntry((byte[])Entry000_0.Clone(), 0, 0); e.Magic.Should().Be(0x9B7A0B7C); e.AdlerChecksum.Should().Be(Adler32.ComputeChecksum(e.Data)); e.Crc32Checksum.Should().Be(Crc32.ComputeChecksum(e.Data)); e.DataSize.Should().Be(e.Data.Length); e.DataSizeDuplicate.Should().Be(e.Data.Length); }
public void TestDecryptFull() { const string dir = @"E:\snap\save\"; for (var index = 0; index < 16; ++index) { var name = SaveReader.SaveFileNames[index]; var file = Path.Combine(dir, name); if (!File.Exists(file)) { continue; } var encsave = File.ReadAllBytes(file); var h = SaveReader.GetSaveFileHeader(new ReadOnlySpan <byte>(encsave, 0, 0x30), index); var decsave = new byte[0x20 + (h.EntryCount * h.DecryptedEntrySize)]; var encHeader = encsave.AsSpan(..0x30); SaveReader.DecryptHeader(encHeader, SaveReader.SaveFileKeys[index], SaveReader.HeaderKey).CopyTo(decsave, 0); for (var i = 0; i < h.EntryCount; ++i) { var start = 0x30 + (i * h.EncryptedEntrySize); if (BitConverter.ToUInt64(encsave, start) == 0) { continue; } var encEntry = encsave.AsSpan(start, h.EncryptedEntrySize); var e = SaveReader.GetSaveFileEntry(encEntry, index, i); var region = e.Data.AsSpan(..h.DecryptedEntrySize); e.AdlerChecksum.Should().Be(Adler32.ComputeChecksum(region)); e.Crc32Checksum.Should().Be(Crc32.ComputeChecksum(region)); e.Magic.Should().Be(0x9B7A0B7C); e.DataSize.Should().BeLessOrEqualTo(e.Data.Length); e.DataSizeDuplicate.Should().BeLessOrEqualTo(e.Data.Length); e.Data.Length.Should().Be((h.DecryptedEntrySize + 15) & ~15); var decDest = decsave.AsSpan(0x20 + (i * h.DecryptedEntrySize)); region.CopyTo(decDest); } var dest = Path.Combine(dir, $"{name}.dec"); File.WriteAllBytes(dest, decsave); } }
/// <summary> /// Verifies the integrity of a file sector using an adler32 checksum. /// </summary> /// <returns><c>true</c>, if the sector integrity is not compromised, <c>false</c> otherwise.</returns> /// <param name="sector">Sector data.</param> /// <param name="checksum">Sector checksum.</param> public static bool VerifySectorChecksum([NotNull] byte[] sector, uint checksum) { using var ms = new MemoryStream(sector); if (checksum == 0) { return(true); } var sectorChecksum = (uint)Adler32.ComputeChecksum(ms); if (sectorChecksum == 0) { // We can't handle a 0 checksum. sectorChecksum = uint.MaxValue; } return(sectorChecksum == checksum); }
/// <summary> /// Reads and decodes a window, returning whether or not there was /// any more data to read. /// </summary> /// <returns> /// Whether or not the delta stream had reached the end of its data. /// </returns> private bool DecodeWindow() { int windowIndicator = delta.ReadByte(); // Have we finished? if (windowIndicator == -1) { return(false); } // The stream to load source data from for this window, if any Stream sourceStream; // Where to reposition the source stream to after reading from it, if anywhere int sourceStreamPostReadSeek = -1; // xdelta3 uses an undocumented extra bit which indicates that there are an extra // 4 bytes at the end of the encoding for the window bool hasAdler32Checksum = ((windowIndicator & 4) == 4); // Get rid of the checksum bit for the rest windowIndicator &= 0xfb; // Work out what the source data is, and detect invalid window indicators switch (windowIndicator & 3) { // No source data used in this window case 0: sourceStream = null; break; // Source data comes from the original stream case 1: if (original == null) { throw new VcdiffFormatException ("Source stream requested by delta but not provided by caller."); } sourceStream = original; break; case 2: sourceStream = output; sourceStreamPostReadSeek = (int)output.Position; break; case 3: throw new VcdiffFormatException ("Invalid window indicator - bits 0 and 1 both set."); default: throw new VcdiffFormatException("Invalid window indicator - bits 3-7 not all zero."); } // Read the source data, if any byte[] sourceData = null; int sourceLength = 0; if (sourceStream != null) { sourceLength = IOHelper.ReadBigEndian7BitEncodedInt(delta); int sourcePosition = IOHelper.ReadBigEndian7BitEncodedInt(delta); sourceStream.Position = sourcePosition; sourceData = IOHelper.CheckedReadBytes(sourceStream, sourceLength); // Reposition the source stream if appropriate if (sourceStreamPostReadSeek != -1) { sourceStream.Position = sourceStreamPostReadSeek; } } // Read how long the delta encoding is - then ignore it IOHelper.ReadBigEndian7BitEncodedInt(delta); // Read how long the target window is int targetLength = IOHelper.ReadBigEndian7BitEncodedInt(delta); byte[] targetData = new byte[targetLength]; MemoryStream targetDataStream = new MemoryStream(targetData, true); // Read the indicator and the lengths of the different data sections byte deltaIndicator = IOHelper.CheckedReadByte(delta); if (deltaIndicator != 0) { throw new VcdiffFormatException("VcdiffDecoder is unable to handle compressed delta sections."); } int addRunDataLength = IOHelper.ReadBigEndian7BitEncodedInt(delta); int instructionsLength = IOHelper.ReadBigEndian7BitEncodedInt(delta); int addressesLength = IOHelper.ReadBigEndian7BitEncodedInt(delta); // If we've been given a checksum, we have to read it and we might as well // use it to check the data. int checksumInFile = 0; if (hasAdler32Checksum) { byte[] checksumBytes = IOHelper.CheckedReadBytes(delta, 4); checksumInFile = (checksumBytes[0] << 24) | (checksumBytes[1] << 16) | (checksumBytes[2] << 8) | checksumBytes[3]; } // Read all the data for this window byte[] addRunData = IOHelper.CheckedReadBytes(delta, addRunDataLength); byte[] instructions = IOHelper.CheckedReadBytes(delta, instructionsLength); byte[] addresses = IOHelper.CheckedReadBytes(delta, addressesLength); int addRunDataIndex = 0; MemoryStream instructionStream = new MemoryStream(instructions, false); _cache.Reset(addresses); while (true) { int instructionIndex = instructionStream.ReadByte(); if (instructionIndex == -1) { break; } for (int i = 0; i < 2; i++) { Instruction instruction = _codeTable[instructionIndex, i]; int size = instruction.Size; if (size == 0 && instruction.Type != InstructionType.NoOp) { size = IOHelper.ReadBigEndian7BitEncodedInt(instructionStream); } switch (instruction.Type) { case InstructionType.NoOp: break; case InstructionType.Add: targetDataStream.Write(addRunData, addRunDataIndex, size); addRunDataIndex += size; break; case InstructionType.Copy: int addr = _cache.DecodeAddress((int)targetDataStream.Position + sourceLength, instruction.Mode); if (sourceData != null && addr < sourceData.Length) { targetDataStream.Write(sourceData, addr, size); } else // Data is in target data { // Get rid of the offset addr -= sourceLength; // Can we just ignore overlap issues? if (addr + size < targetDataStream.Position) { targetDataStream.Write(targetData, addr, size); } else { for (int j = 0; j < size; j++) { targetDataStream.WriteByte(targetData[addr++]); } } } break; case InstructionType.Run: byte data = addRunData[addRunDataIndex++]; for (int j = 0; j < size; j++) { targetDataStream.WriteByte(data); } break; default: throw new VcdiffFormatException("Invalid instruction type found."); } } } output.Write(targetData, 0, targetLength); if (hasAdler32Checksum) { int actualChecksum = Adler32.ComputeChecksum(1, targetData); if (actualChecksum != checksumInFile) { throw new VcdiffFormatException("Invalid checksum after decoding window"); } } return(true); }