Merge pull request #220 from coderb/master

verify RAR crc on header and file data
This commit is contained in:
Adam Hathcock
2017-04-07 11:56:06 +01:00
committed by GitHub
9 changed files with 146 additions and 16 deletions

View File

@@ -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<T>(MarkingBinaryReader reader)
protected virtual void ReadFromReader(MarkingBinaryReader reader) {
throw new NotImplementedException();
}
internal T PromoteHeader<T>(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
/// </summary>
protected long ReadBytes { get; private set; }
protected short HeadCRC { get; private set; }
protected ushort HeadCRC { get; private set; }
internal HeaderType HeaderType { get; private set; }

View File

@@ -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
}
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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<byte> data = new Queue<byte>();
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;
}

View File

@@ -19,6 +19,7 @@ namespace SharpCompress.Compressors.Rar
private long currentPartTotalReadBytes;
private long currentEntryTotalReadBytes;
private uint currentCrc;
internal MultiVolumeReadOnlyStream(IEnumerable<RarFilePart> 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();

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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<RarFilePart>(), this)));
}
}
}
}