From 97d5e0aac40334213c3a5619fee9470cebb01b0b Mon Sep 17 00:00:00 2001 From: Brien Oberstein Date: Tue, 4 Apr 2017 12:20:06 -0400 Subject: [PATCH] verify rar CRC on header and file data --- .../Common/Rar/Headers/RarHeader.cs | 32 ++++++++++---- .../Common/Rar/Headers/RarHeaderFactory.cs | 4 +- .../Common/Rar/RarCrcBinaryReader.cs | 40 ++++++++++++++++++ .../Common/Rar/RarCryptoBinaryReader.cs | 27 ++++++++++-- .../Rar/MultiVolumeReadOnlyStream.cs | 5 +++ src/SharpCompress/Compressors/Rar/RarCRC.cs | 4 ++ .../Compressors/Rar/RarCrcStream.cs | 42 +++++++++++++++++++ src/SharpCompress/IO/MarkingBinaryReader.cs | 4 +- src/SharpCompress/Readers/Rar/RarReader.cs | 4 +- 9 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs create mode 100644 src/SharpCompress/Compressors/Rar/RarCrcStream.cs diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index ecfaad65..02658389 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using SharpCompress.IO; namespace SharpCompress.Common.Rar.Headers @@ -18,14 +19,14 @@ namespace SharpCompress.Common.Rar.Headers ReadBytes = baseHeader.ReadBytes; } - internal static RarHeader Create(MarkingBinaryReader reader) + internal static RarHeader Create(RarCrcBinaryReader reader) { try { RarHeader header = new RarHeader(); reader.Mark(); - header.ReadFromReader(reader); + header.ReadStartFromReader(reader); header.ReadBytes += reader.CurrentReadByteCount; return header; @@ -36,9 +37,10 @@ namespace SharpCompress.Common.Rar.Headers } } - protected virtual void ReadFromReader(MarkingBinaryReader reader) + private void ReadStartFromReader(RarCrcBinaryReader reader) { - HeadCRC = reader.ReadInt16(); + HeadCRC = reader.ReadUInt16(); + reader.ResetCrc(); HeaderType = (HeaderType)(reader.ReadByte() & 0xff); Flags = reader.ReadInt16(); HeaderSize = reader.ReadInt16(); @@ -48,7 +50,11 @@ namespace SharpCompress.Common.Rar.Headers } } - internal T PromoteHeader(MarkingBinaryReader reader) + protected virtual void ReadFromReader(MarkingBinaryReader reader) { + throw new NotImplementedException(); + } + + internal T PromoteHeader(RarCrcBinaryReader reader) where T : RarHeader, new() { T header = new T(); @@ -65,9 +71,21 @@ namespace SharpCompress.Common.Rar.Headers reader.ReadBytes(headerSizeDiff); } + VerifyHeaderCrc(reader.GetCrc()); + return header; } + private void VerifyHeaderCrc(ushort crc) { + if (HeaderType != HeaderType.MarkHeader) + { + if (crc != HeadCRC) + { + throw new InvalidFormatException("rar header crc mismatch"); + } + } + } + protected virtual void PostReadingBytes(MarkingBinaryReader reader) { } @@ -77,7 +95,7 @@ namespace SharpCompress.Common.Rar.Headers /// protected long ReadBytes { get; private set; } - protected short HeadCRC { get; private set; } + protected ushort HeadCRC { get; private set; } internal HeaderType HeaderType { get; private set; } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 8d9f2412..13e02722 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -129,7 +129,7 @@ namespace SharpCompress.Common.Rar.Headers reader.InitializeAes(salt); } #else - var reader = new MarkingBinaryReader(stream); + var reader = new RarCrcBinaryReader(stream); #endif @@ -247,4 +247,4 @@ namespace SharpCompress.Common.Rar.Headers } } } -} \ No newline at end of file +} diff --git a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs new file mode 100644 index 00000000..fe15e517 --- /dev/null +++ b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs @@ -0,0 +1,40 @@ +using System.IO; +using SharpCompress.Compressors.Rar; +using SharpCompress.IO; + +namespace SharpCompress.Common.Rar { + internal class RarCrcBinaryReader : MarkingBinaryReader { + private uint currentCrc; + + public RarCrcBinaryReader(Stream stream) : base(stream) + { + } + + public ushort GetCrc() + { + return (ushort)~this.currentCrc; + } + + public void ResetCrc() + { + this.currentCrc = 0xffffffff; + } + + protected void UpdateCrc(byte b) + { + this.currentCrc = RarCRC.CheckCrc(this.currentCrc, b); + } + + protected byte[] ReadBytesNoCrc(int count) + { + return base.ReadBytes(count); + } + + public override byte[] ReadBytes(int count) + { + var result = base.ReadBytes(count); + this.currentCrc = RarCRC.CheckCrc(this.currentCrc, result, 0, result.Length); + return result; + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs index 40f64d19..9635375b 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs @@ -6,12 +6,13 @@ using SharpCompress.IO; namespace SharpCompress.Common.Rar { - internal class RarCryptoBinaryReader : MarkingBinaryReader + internal class RarCryptoBinaryReader : RarCrcBinaryReader { private RarRijndael rijndael; private byte[] salt; private readonly string password; private readonly Queue data = new Queue(); + private long readCount; public RarCryptoBinaryReader(Stream stream, string password ) : base(stream) @@ -19,6 +20,22 @@ namespace SharpCompress.Common.Rar this.password = password; } + // track read count ourselves rather than using the underlying stream since we buffer + public override long CurrentReadByteCount { + get + { + return this.readCount; + } + protected set + { + // ignore + } + } + + public override void Mark() { + this.readCount = 0; + } + protected bool UseEncryption { get { return salt != null; } @@ -36,6 +53,7 @@ namespace SharpCompress.Common.Rar { return ReadAndDecryptBytes(count); } + this.readCount += count; return base.ReadBytes(count); } @@ -50,7 +68,7 @@ namespace SharpCompress.Common.Rar for (int i = 0; i < alignedSize / 16; i++) { //long ax = System.currentTimeMillis(); - byte[] cipherText = base.ReadBytes(16); + byte[] cipherText = base.ReadBytesNoCrc(16); var readBytes = rijndael.ProcessBlock(cipherText); foreach (var readByte in readBytes) data.Enqueue(readByte); @@ -63,8 +81,11 @@ namespace SharpCompress.Common.Rar for (int i = 0; i < count; i++) { - decryptedBytes[i] = data.Dequeue(); + var b = data.Dequeue(); + decryptedBytes[i] = b; + UpdateCrc(b); } + this.readCount += count; return decryptedBytes; } diff --git a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs index 5c221a4f..90a823a6 100644 --- a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs @@ -19,6 +19,7 @@ namespace SharpCompress.Compressors.Rar private long currentPartTotalReadBytes; private long currentEntryTotalReadBytes; + private uint currentCrc; internal MultiVolumeReadOnlyStream(IEnumerable parts, IExtractionListener streamListener) { @@ -59,6 +60,8 @@ namespace SharpCompress.Compressors.Rar currentPartTotalReadBytes = 0; + currentCrc = filePartEnumerator.Current.FileHeader.FileCRC; + streamListener.FireFilePartExtractionBegin(filePartEnumerator.Current.FilePartName, filePartEnumerator.Current.FileHeader.CompressedSize, filePartEnumerator.Current.FileHeader.UncompressedSize); @@ -119,6 +122,8 @@ namespace SharpCompress.Compressors.Rar public override bool CanWrite { get { return false; } } + public uint CurrentCrc { get { return this.currentCrc; } } + public override void Flush() { throw new NotSupportedException(); diff --git a/src/SharpCompress/Compressors/Rar/RarCRC.cs b/src/SharpCompress/Compressors/Rar/RarCRC.cs index 0bf20009..077b2f46 100644 --- a/src/SharpCompress/Compressors/Rar/RarCRC.cs +++ b/src/SharpCompress/Compressors/Rar/RarCRC.cs @@ -6,6 +6,10 @@ namespace SharpCompress.Compressors.Rar { private static readonly uint[] crcTab; + public static uint CheckCrc(uint startCrc, byte b) { + return (crcTab[((int) ((int) startCrc ^ (int) b)) & 0xff] ^ (startCrc >> 8)); + } + public static uint CheckCrc(uint startCrc, byte[] data, int offset, int count) { int size = Math.Min(data.Length - offset, count); diff --git a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs new file mode 100644 index 00000000..9922d967 --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs @@ -0,0 +1,42 @@ +using System.IO; +using SharpCompress.Common; +using SharpCompress.Common.Rar.Headers; + +namespace SharpCompress.Compressors.Rar { + internal class RarCrcStream : RarStream { + private readonly MultiVolumeReadOnlyStream readStream; + private uint currentCrc; + + public RarCrcStream(Unpack unpack, FileHeader fileHeader, MultiVolumeReadOnlyStream readStream) : base(unpack, fileHeader, readStream) + { + this.readStream = readStream; + ResetCrc(); + } + + public uint GetCrc() + { + return ~this.currentCrc; + } + + public void ResetCrc() + { + this.currentCrc = 0xffffffff; + } + + + public override int Read(byte[] buffer, int offset, int count) + { + var result = base.Read(buffer, offset, count); + if (result != 0) + { + this.currentCrc = RarCRC.CheckCrc(this.currentCrc, buffer, offset, result); + } + else if (GetCrc() != this.readStream.CurrentCrc) + { + // NOTE: we use the last FileHeader in a multipart volume to check CRC + throw new InvalidFormatException("file crc mismatch"); + } + return result; + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/IO/MarkingBinaryReader.cs b/src/SharpCompress/IO/MarkingBinaryReader.cs index 6732b9c0..aa7c820b 100644 --- a/src/SharpCompress/IO/MarkingBinaryReader.cs +++ b/src/SharpCompress/IO/MarkingBinaryReader.cs @@ -12,9 +12,9 @@ namespace SharpCompress.IO { } - public long CurrentReadByteCount { get; private set; } + public virtual long CurrentReadByteCount { get; protected set; } - public void Mark() + public virtual void Mark() { CurrentReadByteCount = 0; } diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 5c1f59d0..5ac7d2e2 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -69,9 +69,9 @@ namespace SharpCompress.Readers.Rar protected override EntryStream GetEntryStream() { - return CreateEntryStream(new RarStream(pack, Entry.FileHeader, + return CreateEntryStream(new RarCrcStream(pack, Entry.FileHeader, new MultiVolumeReadOnlyStream( CreateFilePartEnumerableForCurrentEntry().Cast(), this))); } } -} \ No newline at end of file +}