mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-22 23:15:20 +00:00
Merge pull request #1 from hrasyid/master
Added support for decryption of RAR files
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,4 +6,5 @@ bin/
|
||||
*.user
|
||||
TestArchives/Scratch/
|
||||
TestArchives/Scratch2/
|
||||
TestResults/
|
||||
*.nupkg
|
||||
|
||||
@@ -79,6 +79,38 @@ namespace SharpCompress.Test
|
||||
Read("Rar.rar", CompressionType.Rar);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Rar_EncryptedFileAndHeader_Reader()
|
||||
{
|
||||
ReadRar("Rar.encrypted_filesAndHeader.rar", "test");
|
||||
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Rar_EncryptedFileOnly_Reader()
|
||||
{
|
||||
ReadRar("Rar.encrypted_filesOnly.rar", "test");
|
||||
|
||||
}
|
||||
|
||||
private void ReadRar(string testArchive, string password)
|
||||
{
|
||||
ResetScratch();
|
||||
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testArchive)))
|
||||
using (var reader = RarReader.Open(stream, password))
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
Assert.AreEqual(reader.Entry.CompressionType, CompressionType.Rar);
|
||||
reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, ExtractOptions.ExtractFullPath | ExtractOptions.Overwrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Rar_Entry_Stream()
|
||||
{
|
||||
|
||||
65
SharpCompress.Test/Rar/Unit/RarHeaderFactoryTest.cs
Normal file
65
SharpCompress.Test/Rar/Unit/RarHeaderFactoryTest.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Security;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Test.Rar.Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for RarFactoryReaderTest
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class RarHeaderFactoryTest : TestBase
|
||||
{
|
||||
private RarHeaderFactory rarHeaderFactory;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
ResetScratch();
|
||||
rarHeaderFactory = new RarHeaderFactory(StreamingMode.Seekable, Options.KeepStreamsOpen);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void ReadHeaders_RecognizeEncryptedFlag()
|
||||
{
|
||||
|
||||
ReadEncryptedFlag("Rar.Encrypted_filesAndHeader.rar", true);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void ReadEncryptedFlag(string testArchive, bool isEncrypted)
|
||||
{
|
||||
using (var stream = GetReaderStream(testArchive))
|
||||
foreach (var header in rarHeaderFactory.ReadHeaders(stream))
|
||||
{
|
||||
if (header.HeaderType == HeaderType.ArchiveHeader)
|
||||
{
|
||||
Assert.AreEqual(isEncrypted, rarHeaderFactory.IsEncrypted);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadHeaders_RecognizeNoEncryptedFlag()
|
||||
{
|
||||
ReadEncryptedFlag("Rar.rar", false);
|
||||
}
|
||||
|
||||
private FileStream GetReaderStream(string testArchive)
|
||||
{
|
||||
return new FileStream(Path.Combine(TEST_ARCHIVES_PATH, testArchive),
|
||||
FileMode.Open);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@
|
||||
<Compile Include="ArchiveTests.cs" />
|
||||
<Compile Include="GZip\GZipWriterTests.cs" />
|
||||
<Compile Include="GZip\GZipArchiveTests.cs" />
|
||||
<Compile Include="Rar\Unit\RarHeaderFactoryTest.cs" />
|
||||
<Compile Include="SevenZip\SevenZipArchiveTests.cs" />
|
||||
<Compile Include="Streams\StreamTests.cs" />
|
||||
<Compile Include="Tar\TarWriterTests.cs" />
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace SharpCompress.Test
|
||||
{
|
||||
public class TestBase
|
||||
{
|
||||
protected const string TEST_BASE_PATH = @"C:\Git\sharpcompress";
|
||||
protected const string TEST_BASE_PATH = @"D:\Codes\sharpcompress";
|
||||
protected static readonly string TEST_ARCHIVES_PATH = Path.Combine(TEST_BASE_PATH, "TestArchives", "Archives");
|
||||
protected static readonly string ORIGINAL_FILES_PATH = Path.Combine(TEST_BASE_PATH, "TestArchives", "Original");
|
||||
protected static readonly string MISC_TEST_FILES_PATH = Path.Combine(TEST_BASE_PATH, "TestArchives", "MiscTest");
|
||||
@@ -122,14 +122,14 @@ namespace SharpCompress.Test
|
||||
using (var file2Stream = File.OpenRead(file2))
|
||||
{
|
||||
Assert.AreEqual(file1Stream.Length, file2Stream.Length);
|
||||
|
||||
int byte1 = 0;
|
||||
int byte2 = 0;
|
||||
while (byte1 != -1)
|
||||
for (int counter = 0; byte1 != -1; counter++ )
|
||||
{
|
||||
byte1 = file1Stream.ReadByte();
|
||||
byte2 = file2Stream.ReadByte();
|
||||
Assert.AreEqual(byte1, byte2);
|
||||
if (byte1 != byte2) Assert.AreEqual(byte1, byte2, string.Format("Byte {0} differ between {1} and {2}",
|
||||
counter, file1, file2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,5 +27,10 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
internal int PosAv { get; private set; }
|
||||
|
||||
internal byte EncryptionVersion { get; private set; }
|
||||
|
||||
public bool HasPassword
|
||||
{
|
||||
get { return ArchiveHeaderFlags.HasFlag(ArchiveFlags.PASSWORD); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,11 +35,10 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ReadFromReader(MarkingBinaryReader reader)
|
||||
{
|
||||
HeadCRC = reader.ReadInt16();
|
||||
HeaderType = (HeaderType) (int) (reader.ReadByte() & 0xff);
|
||||
HeaderType = (HeaderType)(int)(reader.ReadByte() & 0xff);
|
||||
Flags = reader.ReadInt16();
|
||||
HeaderSize = reader.ReadInt16();
|
||||
if (FlagUtility.HasFlag(Flags, LONG_BLOCK))
|
||||
@@ -58,7 +57,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
header.ReadFromReader(reader);
|
||||
header.ReadBytes += reader.CurrentReadByteCount;
|
||||
|
||||
int headerSizeDiff = header.HeaderSize - (int) header.ReadBytes;
|
||||
int headerSizeDiff = header.HeaderSize - (int)header.ReadBytes;
|
||||
|
||||
if (headerSizeDiff > 0)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Compressor.Rar;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Rar.Headers
|
||||
@@ -9,22 +10,26 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
{
|
||||
private int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136
|
||||
|
||||
internal RarHeaderFactory(StreamingMode mode, Options options)
|
||||
internal RarHeaderFactory(StreamingMode mode, Options options, string password = null)
|
||||
{
|
||||
StreamingMode = mode;
|
||||
Options = options;
|
||||
Password = password;
|
||||
}
|
||||
|
||||
private Options Options { get; set; }
|
||||
|
||||
public string Password { get; private set; }
|
||||
internal StreamingMode StreamingMode { get; private set; }
|
||||
|
||||
internal bool IsEncrypted { get; set; }
|
||||
|
||||
internal IEnumerable<RarHeader> ReadHeaders(Stream stream)
|
||||
{
|
||||
if (Options.HasFlag(Options.LookForHeader))
|
||||
{
|
||||
stream = CheckSFX(stream);
|
||||
}
|
||||
|
||||
RarHeader header;
|
||||
while ((header = ReadNextHeader(stream)) != null)
|
||||
{
|
||||
@@ -107,9 +112,19 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
return rewindableStream;
|
||||
}
|
||||
|
||||
|
||||
private RarHeader ReadNextHeader(Stream stream)
|
||||
{
|
||||
MarkingBinaryReader reader = new MarkingBinaryReader(stream);
|
||||
MarkingBinaryReader reader = new MarkingBinaryReader(stream, Password);
|
||||
|
||||
if (IsEncrypted)
|
||||
{
|
||||
reader.Salt = null;
|
||||
reader.SkipQueue();
|
||||
byte[] salt = reader.ReadBytes(8);
|
||||
reader.Salt = salt;
|
||||
|
||||
}
|
||||
RarHeader header = RarHeader.Create(reader);
|
||||
if (header == null)
|
||||
{
|
||||
@@ -119,7 +134,9 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
{
|
||||
case HeaderType.ArchiveHeader:
|
||||
{
|
||||
return header.PromoteHeader<ArchiveHeader>(reader);
|
||||
var ah = header.PromoteHeader<ArchiveHeader>(reader);
|
||||
IsEncrypted = ah.HasPassword;
|
||||
return ah;
|
||||
}
|
||||
case HeaderType.MarkHeader:
|
||||
{
|
||||
@@ -164,7 +181,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
{
|
||||
ReadOnlySubStream ms
|
||||
= new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize);
|
||||
fh.PackedStream = ms;
|
||||
fh.PackedStream = fh.Salt == null? (Stream) ms : new RarCryptoWrapper(ms, Password) { Salt = fh.Salt};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
116
SharpCompress/Common/Rar/RarCryptoWrapper.cs
Normal file
116
SharpCompress/Common/Rar/RarCryptoWrapper.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Common.Rar
|
||||
{
|
||||
internal class RarCryptoWrapper : Stream
|
||||
{
|
||||
private readonly Stream actualStream;
|
||||
private byte[] salt;
|
||||
private RarRijndael rijndael;
|
||||
private readonly string password;
|
||||
private readonly Queue<byte> data = new Queue<byte>();
|
||||
|
||||
public RarCryptoWrapper(Stream actualStream, string password)
|
||||
{
|
||||
this.actualStream = actualStream;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
internal byte[] Salt
|
||||
{
|
||||
get { return salt; }
|
||||
set
|
||||
{
|
||||
salt = value;
|
||||
if (value != null) InitializeAes();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAes()
|
||||
{
|
||||
rijndael = RarRijndael.InitializeFrom(password, salt);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (Salt == null) return actualStream.Read(buffer, offset, count);
|
||||
return ReadAndDecrypt(buffer, offset, count);
|
||||
}
|
||||
|
||||
public int ReadAndDecrypt(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int queueSize = data.Count;
|
||||
int sizeToRead = count - queueSize;
|
||||
|
||||
if (sizeToRead > 0)
|
||||
{
|
||||
int alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf);
|
||||
for (int i = 0; i < alignedSize/16; i++)
|
||||
{
|
||||
//long ax = System.currentTimeMillis();
|
||||
byte[] cipherText = new byte[RarRijndael.CryptoBlockSize];
|
||||
actualStream.Read(cipherText, 0, RarRijndael.CryptoBlockSize);
|
||||
|
||||
var readBytes = rijndael.ProcessBlock(cipherText);
|
||||
foreach(var readByte in readBytes)
|
||||
data.Enqueue(readByte);
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
buffer[offset+i] = data.Dequeue();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override long Position { get; set; }
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(rijndael != null) rijndael.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
113
SharpCompress/Common/Rar/RarRijndael.cs
Normal file
113
SharpCompress/Common/Rar/RarRijndael.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpCompress.Common.Rar
|
||||
{
|
||||
internal class RarRijndael : IDisposable
|
||||
{
|
||||
private readonly string password;
|
||||
private readonly byte[] salt;
|
||||
private byte[] aesInitializationVector;
|
||||
private Rijndael rijndael;
|
||||
|
||||
private RarRijndael(string password, byte[] salt)
|
||||
{
|
||||
this.password = password;
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
internal const int CryptoBlockSize = 16;
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
|
||||
rijndael = new RijndaelManaged() { Padding = PaddingMode.None };
|
||||
aesInitializationVector = new byte[CryptoBlockSize];
|
||||
int rawLength = 2 * password.Length;
|
||||
byte[] rawPassword = new byte[rawLength + 8];
|
||||
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
|
||||
for (int i = 0; i < password.Length; i++)
|
||||
{
|
||||
rawPassword[i * 2] = passwordBytes[i];
|
||||
rawPassword[i * 2 + 1] = 0;
|
||||
}
|
||||
for (int i = 0; i < salt.Length; i++)
|
||||
{
|
||||
rawPassword[i + rawLength] = salt[i];
|
||||
}
|
||||
|
||||
SHA1 sha = new SHA1CryptoServiceProvider();
|
||||
|
||||
const int noOfRounds = (1 << 18);
|
||||
IList<byte> bytes = new List<byte>();
|
||||
byte[] digest;
|
||||
|
||||
//TODO slow code below, find ways to optimize
|
||||
for (int i = 0; i < noOfRounds; i++)
|
||||
{
|
||||
bytes.AddRange(rawPassword);
|
||||
|
||||
bytes.AddRange(new[] { (byte)i, (byte)(i >> 8), (byte)(i >> CryptoBlockSize) });
|
||||
if (i % (noOfRounds / CryptoBlockSize) == 0)
|
||||
{
|
||||
digest = sha.ComputeHash(bytes.ToArray());
|
||||
aesInitializationVector[i / (noOfRounds / CryptoBlockSize)] = digest[19];
|
||||
}
|
||||
}
|
||||
|
||||
digest = sha.ComputeHash(bytes.ToArray());
|
||||
//slow code ends
|
||||
|
||||
byte[] aesKey = new byte[CryptoBlockSize];
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int j = 0; j < 4; j++)
|
||||
aesKey[i * 4 + j] = (byte)
|
||||
(((digest[i * 4] * 0x1000000) & 0xff000000 |
|
||||
(uint)((digest[i * 4 + 1] * 0x10000) & 0xff0000) |
|
||||
(uint)((digest[i * 4 + 2] * 0x100) & 0xff00) |
|
||||
(uint)(digest[i * 4 + 3] & 0xff)) >> (j * 8));
|
||||
|
||||
rijndael.IV = new byte[CryptoBlockSize];
|
||||
rijndael.Key = aesKey;
|
||||
rijndael.BlockSize = CryptoBlockSize * 8;
|
||||
|
||||
}
|
||||
|
||||
public static RarRijndael InitializeFrom(string password, byte[] salt)
|
||||
{
|
||||
var rijndael = new RarRijndael(password, salt);
|
||||
rijndael.Initialize();
|
||||
return rijndael;
|
||||
}
|
||||
|
||||
public byte[] ProcessBlock(byte[] cipherText)
|
||||
{
|
||||
var plainText = new byte[CryptoBlockSize];
|
||||
var decryptedBytes = new List<byte>();
|
||||
var decryptor = rijndael.CreateDecryptor();
|
||||
using (var msDecrypt = new MemoryStream(cipherText))
|
||||
{
|
||||
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
|
||||
{
|
||||
csDecrypt.ReadFully(plainText);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < plainText.Length; j++)
|
||||
decryptedBytes.Add((byte)(plainText[j] ^ aesInitializationVector[j % 16])); //32:114, 33:101
|
||||
|
||||
for (int j = 0; j < aesInitializationVector.Length; j++)
|
||||
aesInitializationVector[j] = cipherText[j];
|
||||
return decryptedBytes.ToArray();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
rijndael.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,18 @@ namespace SharpCompress.Common.Rar
|
||||
public abstract class RarVolume : Volume
|
||||
{
|
||||
private readonly RarHeaderFactory headerFactory;
|
||||
public string Password { get; set; }
|
||||
|
||||
internal RarVolume(StreamingMode mode, Stream stream, Options options)
|
||||
internal RarVolume(StreamingMode mode, Stream stream, Options options)
|
||||
: this(mode, stream, null, options)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal RarVolume(StreamingMode mode, Stream stream, string password, Options options)
|
||||
: base(stream, options)
|
||||
{
|
||||
headerFactory = new RarHeaderFactory(mode, options);
|
||||
headerFactory = new RarHeaderFactory(mode, options, password);
|
||||
}
|
||||
|
||||
internal StreamingMode Mode
|
||||
|
||||
@@ -1,17 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.Rar;
|
||||
|
||||
namespace SharpCompress.IO
|
||||
{
|
||||
internal class MarkingBinaryReader : BinaryReader
|
||||
{
|
||||
public MarkingBinaryReader(Stream stream)
|
||||
private byte[] _salt;
|
||||
private readonly string _password;
|
||||
private RarRijndael _rijndael;
|
||||
private Queue<byte> _data = new Queue<byte>();
|
||||
|
||||
public MarkingBinaryReader(Stream stream, string password = null)
|
||||
: base(stream)
|
||||
{
|
||||
_password = password;
|
||||
}
|
||||
|
||||
public long CurrentReadByteCount { get; private set; }
|
||||
|
||||
internal byte[] Salt
|
||||
{
|
||||
get { return _salt; }
|
||||
set
|
||||
{
|
||||
_salt = value;
|
||||
if (value != null) InitializeAes();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAes()
|
||||
{
|
||||
_rijndael = RarRijndael.InitializeFrom(_password, _salt);
|
||||
}
|
||||
|
||||
|
||||
public void Mark()
|
||||
{
|
||||
CurrentReadByteCount = 0;
|
||||
@@ -42,14 +68,49 @@ namespace SharpCompress.IO
|
||||
|
||||
public override byte ReadByte()
|
||||
{
|
||||
CurrentReadByteCount++;
|
||||
return base.ReadByte();
|
||||
return ReadBytes(1).Single();
|
||||
}
|
||||
|
||||
public override byte[] ReadBytes(int count)
|
||||
{
|
||||
CurrentReadByteCount += count;
|
||||
return base.ReadBytes(count);
|
||||
return UseEncryption ?
|
||||
ReadAndDecryptBytes(count)
|
||||
: base.ReadBytes(count);
|
||||
}
|
||||
|
||||
protected bool UseEncryption
|
||||
{
|
||||
get { return Salt != null; }
|
||||
}
|
||||
|
||||
private byte[] ReadAndDecryptBytes(int count)
|
||||
{
|
||||
int queueSize = _data.Count;
|
||||
int sizeToRead = count - queueSize;
|
||||
|
||||
if (sizeToRead > 0)
|
||||
{
|
||||
int alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf);
|
||||
for (int i = 0; i < alignedSize / 16; i++)
|
||||
{
|
||||
//long ax = System.currentTimeMillis();
|
||||
byte[] cipherText = base.ReadBytes(16);
|
||||
var readBytes = _rijndael.ProcessBlock(cipherText);
|
||||
foreach (var readByte in readBytes)
|
||||
_data.Enqueue(readByte);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var decryptedBytes = new byte[count];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
decryptedBytes[i] = _data.Dequeue();
|
||||
}
|
||||
return decryptedBytes;
|
||||
}
|
||||
|
||||
public override char ReadChar()
|
||||
@@ -65,45 +126,49 @@ namespace SharpCompress.IO
|
||||
#if !PORTABLE
|
||||
public override decimal ReadDecimal()
|
||||
{
|
||||
CurrentReadByteCount += 16;
|
||||
return base.ReadDecimal();
|
||||
return ByteArrayToDecimal(ReadBytes(16), 0);
|
||||
}
|
||||
|
||||
private decimal ByteArrayToDecimal(byte[] src, int offset)
|
||||
{
|
||||
//http://stackoverflow.com/a/16984356/385387
|
||||
var i1 = BitConverter.ToInt32(src, offset);
|
||||
var i2 = BitConverter.ToInt32(src, offset + 4);
|
||||
var i3 = BitConverter.ToInt32(src, offset + 8);
|
||||
var i4 = BitConverter.ToInt32(src, offset + 12);
|
||||
|
||||
return new decimal(new[] { i1, i2, i3, i4 });
|
||||
}
|
||||
#endif
|
||||
|
||||
public override double ReadDouble()
|
||||
{
|
||||
CurrentReadByteCount += 8;
|
||||
return base.ReadDouble();
|
||||
return BitConverter.ToDouble(ReadBytes(8), 0);
|
||||
}
|
||||
|
||||
public override short ReadInt16()
|
||||
{
|
||||
CurrentReadByteCount += 2;
|
||||
return base.ReadInt16();
|
||||
return BitConverter.ToInt16(ReadBytes(2), 0);
|
||||
}
|
||||
|
||||
public override int ReadInt32()
|
||||
{
|
||||
CurrentReadByteCount += 4;
|
||||
return base.ReadInt32();
|
||||
return BitConverter.ToInt32(ReadBytes(4), 0);
|
||||
}
|
||||
|
||||
public override long ReadInt64()
|
||||
{
|
||||
CurrentReadByteCount += 8;
|
||||
return base.ReadInt64();
|
||||
return BitConverter.ToInt64(ReadBytes(8), 0);
|
||||
}
|
||||
|
||||
public override sbyte ReadSByte()
|
||||
{
|
||||
CurrentReadByteCount++;
|
||||
return base.ReadSByte();
|
||||
return (sbyte)ReadByte();
|
||||
}
|
||||
|
||||
public override float ReadSingle()
|
||||
{
|
||||
CurrentReadByteCount += 4;
|
||||
return base.ReadSingle();
|
||||
return BitConverter.ToSingle(ReadBytes(4), 0);
|
||||
}
|
||||
|
||||
public override string ReadString()
|
||||
@@ -113,20 +178,29 @@ namespace SharpCompress.IO
|
||||
|
||||
public override ushort ReadUInt16()
|
||||
{
|
||||
CurrentReadByteCount += 2;
|
||||
return base.ReadUInt16();
|
||||
return BitConverter.ToUInt16(ReadBytes(2), 0);
|
||||
}
|
||||
|
||||
public override uint ReadUInt32()
|
||||
{
|
||||
CurrentReadByteCount += 4;
|
||||
return base.ReadUInt32();
|
||||
return BitConverter.ToUInt32(ReadBytes(4), 0);
|
||||
}
|
||||
|
||||
public override ulong ReadUInt64()
|
||||
{
|
||||
CurrentReadByteCount += 8;
|
||||
return base.ReadUInt64();
|
||||
return BitConverter.ToUInt64(ReadBytes(8), 0);
|
||||
}
|
||||
|
||||
public void ClearQueue()
|
||||
{
|
||||
_data.Clear();
|
||||
}
|
||||
|
||||
public void SkipQueue()
|
||||
{
|
||||
var position = BaseStream.Position;
|
||||
BaseStream.Position = position + _data.Count;
|
||||
ClearQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,7 @@ namespace SharpCompress.IO
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
//Stream.Dispose();
|
||||
}
|
||||
//don't dispose anything
|
||||
}
|
||||
|
||||
public Stream Stream { get; private set; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
@@ -12,6 +13,7 @@ namespace SharpCompress.Reader.Rar
|
||||
/// </summary>
|
||||
public abstract class RarReader : AbstractReader<RarReaderEntry, RarVolume>
|
||||
{
|
||||
public string Password { get; set; }
|
||||
private RarVolume volume;
|
||||
private readonly Unpack pack = new Unpack();
|
||||
|
||||
@@ -37,8 +39,7 @@ namespace SharpCompress.Reader.Rar
|
||||
/// <returns></returns>
|
||||
public static RarReader Open(Stream stream, Options options = Options.KeepStreamsOpen)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new SingleVolumeRarReader(stream, options);
|
||||
return Open(stream, null, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,7 +58,7 @@ namespace SharpCompress.Reader.Rar
|
||||
|
||||
internal override IEnumerable<RarReaderEntry> GetEntries(Stream stream)
|
||||
{
|
||||
volume = new RarReaderVolume(stream, Options);
|
||||
volume = new RarReaderVolume(stream, Password, Options);
|
||||
foreach (RarFilePart fp in volume.ReadFileParts())
|
||||
{
|
||||
ValidateArchive(volume);
|
||||
@@ -76,5 +77,11 @@ namespace SharpCompress.Reader.Rar
|
||||
new MultiVolumeReadOnlyStream(
|
||||
CreateFilePartEnumerableForCurrentEntry().Cast<RarFilePart>(), this)));
|
||||
}
|
||||
|
||||
public static RarReader Open(Stream stream, string password, Options options = Options.KeepStreamsOpen)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new SingleVolumeRarReader(stream, password, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,10 @@ namespace SharpCompress.Reader.Rar
|
||||
{
|
||||
public class RarReaderVolume : RarVolume
|
||||
{
|
||||
internal RarReaderVolume(Stream stream, Options options)
|
||||
: base(StreamingMode.Streaming, stream, options)
|
||||
|
||||
|
||||
internal RarReaderVolume(Stream stream, string password, Options options)
|
||||
: base(StreamingMode.Streaming, stream, password, options)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ namespace SharpCompress.Reader.Rar
|
||||
{
|
||||
internal class SingleVolumeRarReader : RarReader
|
||||
{
|
||||
|
||||
private readonly Stream stream;
|
||||
|
||||
internal SingleVolumeRarReader(Stream stream, Options options)
|
||||
internal SingleVolumeRarReader(Stream stream, string password, Options options)
|
||||
: base(options)
|
||||
{
|
||||
Password = password;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
<Compile Include="Common\Rar\Headers\SignHeader.cs" />
|
||||
<Compile Include="Common\Rar\RarEntry.cs" />
|
||||
<Compile Include="Common\Rar\RarFilePart.cs" />
|
||||
<Compile Include="Common\Rar\RarRijndael.cs" />
|
||||
<Compile Include="Common\Rar\RarVolume.cs" />
|
||||
<Compile Include="Common\SevenZip\ArchiveDatabase.cs" />
|
||||
<Compile Include="Common\SevenZip\ArchiveReader.cs" />
|
||||
|
||||
@@ -126,6 +126,8 @@
|
||||
<Compile Include="Common\Entry.cs" />
|
||||
<Compile Include="Common\IEntry.cs" />
|
||||
<Compile Include="Common\IVolume.cs" />
|
||||
<Compile Include="Common\Rar\RarCryptoWrapper.cs" />
|
||||
<Compile Include="Common\Rar\RarRijndael.cs" />
|
||||
<Compile Include="Common\SevenZip\CBindPair.cs" />
|
||||
<Compile Include="Common\SevenZip\CCoderInfo.cs" />
|
||||
<Compile Include="Common\SevenZip\CFileItem.cs" />
|
||||
|
||||
BIN
TestArchives/Archives/Rar.encrypted_filesAndHeader.rar
Normal file
BIN
TestArchives/Archives/Rar.encrypted_filesAndHeader.rar
Normal file
Binary file not shown.
BIN
TestArchives/Archives/Rar.encrypted_filesOnly.rar
Normal file
BIN
TestArchives/Archives/Rar.encrypted_filesOnly.rar
Normal file
Binary file not shown.
Reference in New Issue
Block a user