Skip to content

Commit

Permalink
verify rar CRC on header and file data
Browse files Browse the repository at this point in the history
  • Loading branch information
coderb committed Apr 4, 2017
1 parent 356c977 commit 97d5e0a
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 16 deletions.
32 changes: 25 additions & 7 deletions src/SharpCompress/Common/Rar/Headers/RarHeader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using SharpCompress.IO;

namespace SharpCompress.Common.Rar.Headers
Expand All @@ -18,14 +19,14 @@ private void FillBase(RarHeader baseHeader)
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;
Expand All @@ -36,9 +37,10 @@ internal static RarHeader Create(MarkingBinaryReader reader)
}
}

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();
Expand All @@ -48,7 +50,11 @@ protected virtual void ReadFromReader(MarkingBinaryReader reader)
}
}

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();
Expand All @@ -65,9 +71,21 @@ internal T PromoteHeader<T>(MarkingBinaryReader reader)
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)
{
}
Expand All @@ -77,7 +95,7 @@ protected virtual void PostReadingBytes(MarkingBinaryReader reader)
/// </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; }

Expand Down
4 changes: 2 additions & 2 deletions src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ private RarHeader ReadNextHeader(Stream stream)
reader.InitializeAes(salt);
}
#else
var reader = new MarkingBinaryReader(stream);
var reader = new RarCrcBinaryReader(stream);

#endif

Expand Down Expand Up @@ -247,4 +247,4 @@ private RarHeader ReadNextHeader(Stream stream)
}
}
}
}
}
40 changes: 40 additions & 0 deletions src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
27 changes: 24 additions & 3 deletions src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,36 @@

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)
{
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; }
Expand All @@ -36,6 +53,7 @@ public override byte[] ReadBytes(int count)
{
return ReadAndDecryptBytes(count);
}
this.readCount += count;
return base.ReadBytes(count);
}

Expand All @@ -50,7 +68,7 @@ private byte[] ReadAndDecryptBytes(int count)
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);
Expand All @@ -63,8 +81,11 @@ private byte[] ReadAndDecryptBytes(int count)

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal class MultiVolumeReadOnlyStream : Stream

private long currentPartTotalReadBytes;
private long currentEntryTotalReadBytes;
private uint currentCrc;

internal MultiVolumeReadOnlyStream(IEnumerable<RarFilePart> parts, IExtractionListener streamListener)
{
Expand Down Expand Up @@ -59,6 +60,8 @@ private void InitializeNextFilePart()

currentPartTotalReadBytes = 0;

currentCrc = filePartEnumerator.Current.FileHeader.FileCRC;

streamListener.FireFilePartExtractionBegin(filePartEnumerator.Current.FilePartName,
filePartEnumerator.Current.FileHeader.CompressedSize,
filePartEnumerator.Current.FileHeader.UncompressedSize);
Expand Down Expand Up @@ -119,6 +122,8 @@ public override int Read(byte[] buffer, int offset, int count)

public override bool CanWrite { get { return false; } }

public uint CurrentCrc { get { return this.currentCrc; } }

public override void Flush()
{
throw new NotSupportedException();
Expand Down
4 changes: 4 additions & 0 deletions src/SharpCompress/Compressors/Rar/RarCRC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ internal static class RarCRC
{
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);
Expand Down
42 changes: 42 additions & 0 deletions src/SharpCompress/Compressors/Rar/RarCrcStream.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
4 changes: 2 additions & 2 deletions src/SharpCompress/IO/MarkingBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ public MarkingBinaryReader(Stream stream)
{
}

public long CurrentReadByteCount { get; private set; }
public virtual long CurrentReadByteCount { get; protected set; }

public void Mark()
public virtual void Mark()
{
CurrentReadByteCount = 0;
}
Expand Down
4 changes: 2 additions & 2 deletions src/SharpCompress/Readers/Rar/RarReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ protected virtual IEnumerable<FilePart> CreateFilePartEnumerableForCurrentEntry(

protected override EntryStream GetEntryStream()
{
return CreateEntryStream(new RarStream(pack, Entry.FileHeader,
return CreateEntryStream(new RarCrcStream(pack, Entry.FileHeader,
new MultiVolumeReadOnlyStream(
CreateFilePartEnumerableForCurrentEntry().Cast<RarFilePart>(), this)));
}
}
}
}

0 comments on commit 97d5e0a

Please sign in to comment.