mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-22 23:15:20 +00:00
Merge pull request #220 from coderb/master
verify RAR crc on header and file data
This commit is contained in:
@@ -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; }
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs
Normal file
40
src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
42
src/SharpCompress/Compressors/Rar/RarCrcStream.cs
Normal file
42
src/SharpCompress/Compressors/Rar/RarCrcStream.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user