Compare commits

...

44 Commits

Author SHA1 Message Date
Adam Hathcock
f515ff36b6 Mark for 0.36.0 2024-01-15 08:21:36 +00:00
Adam Hathcock
ed57cfd2f9 Merge pull request #803 from DannyBoyk/802_Add_UnixTimeExtraField_Support_Zips
Add support for the UnixTimeExtraField in Zip files
2024-01-15 08:19:29 +00:00
Daniel Nash
d69559e9c7 Add support for the UnixTimeExtraField in Zip files
Fixes #802
2024-01-12 09:34:13 -05:00
Adam Hathcock
396717efd1 Merge pull request #799 from Erior/feature/DataDescriptorStream-fix-report-size-position
Fix reporting size / position
2024-01-08 09:18:34 +00:00
Adam Hathcock
284fa24464 Merge pull request #800 from Erior/feature/Expose-file-attributes-for-rar-entries
Expose file attributes for rar
2024-01-08 09:17:53 +00:00
Adam Hathcock
0a20b9179a Merge pull request #798 from Erior/feature/Fix-crash-when-not-setting-password-for-rar5
Set Empty string for Rar5 password as default
2024-01-08 09:17:07 +00:00
Adam Hathcock
a0d5037885 Merge pull request #801 from Erior/feature/771
Issue 771, remove throw on flush for readonly streams
2024-01-08 09:13:39 +00:00
Lars Vahlenberg
4477833b1d Issue 771, remove throw on flush for readonly streams 2024-01-06 00:14:34 +01:00
Lars Vahlenberg
e0a5ed4bdb Expose file attributes 2024-01-05 09:50:58 +01:00
Lars Vahlenberg
46d4b26eba Fix testing under Linux 2024-01-05 00:35:24 +01:00
Lars Vahlenberg
f7c6edf849 Fix reporting size / position 2024-01-04 23:33:39 +01:00
Lars Vahlenberg
6c157def4b set empty string if password not set 2024-01-04 21:16:07 +01:00
Adam Hathcock
741712f89f Merge pull request #794 from Erior/feature/rar5-blake2
Feature/rar5 blake2
2024-01-03 08:34:54 +00:00
Lars Vahlenberg
4f749da628 Merge branch 'develop' into feature/rar5-blake2 2024-01-02 21:26:51 +01:00
Lars Vahlenberg
8b02795d69 CSharpier 2024-01-02 21:25:40 +01:00
Lars Vahlenberg
f8a0069a5d Calc checksum when encrypted is not working for RAR5, disable for now 2024-01-02 21:18:49 +01:00
Lars Vahlenberg
388bbe047e Blake2 Archive test OK 2024-01-02 20:46:55 +01:00
Adam Hathcock
2d4ce30e58 Merge pull request #792 from DannyBoyk/791_Correct_EOCD_ZipWriter
ZipWriter: Write correct EOCD record when more than 65,535 files
2023-12-27 08:55:02 +00:00
Daniel Nash
d4fb17cf66 ZipWriter: Write correct EOCD record when more than 65,535 files
0xFFFF will be written to the EOCD to signal to use the ZIP64
CentralDirectory record when the number of files is 65,535 or more.
Fixes #791
2023-12-22 11:26:01 -05:00
Adam Hathcock
372a2c8375 Mark for 0.35.0 2023-12-18 09:59:46 +00:00
Adam Hathcock
8f27121f21 Merge pull request #789 from adamhathcock/dotnet8
Dotnet8
2023-12-18 09:57:50 +00:00
Adam Hathcock
b986bf675f just remove readme 2023-12-18 09:31:33 +00:00
Adam Hathcock
80718a461b fix readme? 2023-12-18 09:24:00 +00:00
Adam Hathcock
2d14ecf58b add readme 2023-12-18 09:20:44 +00:00
Adam Hathcock
32aa9877c0 remove caching 2023-12-18 09:16:02 +00:00
Adam Hathcock
cee3a9c11d Revert "add lock files"
This reverts commit 30a31de45b.
2023-12-18 09:15:26 +00:00
Adam Hathcock
b78643f2d8 update upload artifact 2023-12-18 09:15:15 +00:00
Adam Hathcock
30a31de45b add lock files 2023-12-18 09:13:14 +00:00
Adam Hathcock
e4c4db534c build for dotnet 8 2023-12-18 09:09:31 +00:00
Adam Hathcock
4f7a0d3ad0 CI to dotnet 8 2023-12-18 09:08:06 +00:00
Adam Hathcock
ea3a96eead update and rerun csharpier 2023-12-18 09:04:04 +00:00
Adam Hathcock
c0e01ac132 Use dotnet 8 and update deps 2023-12-18 09:01:54 +00:00
Adam Hathcock
28ea50bca4 Merge pull request #788 from Erior/develop
RAR5 decryption support
2023-12-18 08:51:25 +00:00
Lars Vahlenberg
619e44b30f CSharpier fixes 2023-12-16 03:08:51 +01:00
Lars Vahlenberg
d678275dee Implement RAR5 decryption 2023-12-16 02:53:09 +01:00
Adam Hathcock
08eed53595 Merge pull request #787 from adamhathcock/dependabot/github_actions/actions/setup-dotnet-4
Bump actions/setup-dotnet from 3 to 4
2023-12-11 10:09:28 +00:00
dependabot[bot]
ff40f7d262 Bump actions/setup-dotnet from 3 to 4
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3 to 4.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 09:35:30 +00:00
Adam Hathcock
3c1ae51dae Merge pull request #786 from Erior/feature/Issue-774
LZMA EOS marker detection
2023-12-11 08:46:20 +00:00
Adam Hathcock
8a59fc9aaf Merge pull request #785 from Erior/feature/Issue-782
Handle tar files generated with tar -H oldgnu that has large uid/gid values
2023-12-11 08:44:54 +00:00
Adam Hathcock
b7ea9dd841 Merge pull request #784 from Erior/feature/rar-comment
Dont crash on reading rar5 comment #783
2023-12-11 08:44:01 +00:00
Lars Vahlenberg
0320db6b4a LZMA EOS marker detection 2023-12-09 13:41:35 +01:00
Lars Vahlenberg
18c7f58093 Handle tar files generated with tar -H oldgnu that has large uid/gid values 2023-12-04 22:35:11 +01:00
Lars Vahlenberg
7f6f7b1436 Resharpier fix 2023-12-04 20:28:16 +01:00
Lars Vahlenberg
ca49176b97 Dont crash on reading rar5 comment 2023-12-04 20:19:11 +01:00
69 changed files with 1197 additions and 2299 deletions

View File

@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"csharpier": {
"version": "0.26.1",
"version": "0.26.7",
"commands": [
"dotnet-csharpier"
]

View File

@@ -15,18 +15,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v3
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 7.0.x
- name: NuGet Caching
uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('packages.lock.json', '*/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-
dotnet-version: 8.0.x
- run: dotnet run --project build/build.csproj
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}-sharpcompress.nupkg
path: artifacts/*

View File

@@ -36,7 +36,7 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un
## TODOs (always lots)
* RAR 5 decryption support
* RAR 5 decryption crc check support
* 7Zip writing
* Zip64 (Need writing and extend Reading)
* Multi-volume Zip support.

View File

@@ -61,7 +61,7 @@ Target(
Target(
Test,
DependsOn(Build),
ForEach("net7.0", "net462"),
ForEach("net8.0", "net462"),
framework =>
{
IEnumerable<string> GetFiles(string d)

View File

@@ -1,6 +1,6 @@
{
"sdk": {
"version": "7.0.101",
"version": "8.0.100",
"rollForward": "latestFeature"
}
}

View File

@@ -26,8 +26,7 @@ public static class ArchiveFactory
public static IWritableArchive Create(ArchiveType type)
{
var factory = Factory
.Factories
.OfType<IWriteableArchiveFactory>()
.Factories.OfType<IWriteableArchiveFactory>()
.FirstOrDefault(item => item.KnownArchiveType == type);
if (factory != null)

View File

@@ -40,9 +40,9 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
streams[1].Position = 0;
SrcStream.Position = 0;
return srcStream
.Streams
.Select(a => new StreamRarArchiveVolume(a, ReaderOptions, idx++));
return srcStream.Streams.Select(
a => new StreamRarArchiveVolume(a, ReaderOptions, idx++)
);
}
else //split mode or single file
{

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -40,7 +41,10 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
get
{
CheckIncomplete();
return parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc;
return BitConverter.ToUInt32(
parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc,
0
);
}
}

View File

@@ -25,10 +25,19 @@ internal class SeekableFilePart : RarFilePart
internal override Stream GetCompressedStream()
{
stream.Position = FileHeader.DataStartPosition;
if (FileHeader.R4Salt != null)
{
return new RarCryptoWrapper(stream, password!, FileHeader.R4Salt);
var cryptKey = new CryptKey3(password!);
return new RarCryptoWrapper(stream, FileHeader.R4Salt, cryptKey);
}
if (FileHeader.Rar5CryptoInfo != null)
{
var cryptKey = new CryptKey5(password!, FileHeader.Rar5CryptoInfo);
return new RarCryptoWrapper(stream, FileHeader.Rar5CryptoInfo.Salt, cryptKey);
}
return stream;
}

View File

@@ -236,13 +236,11 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
}
else
{
currentStream = archive
.database
.GetFolderStream(
stream,
currentFolder,
new PasswordProvider(Options.Password)
);
currentStream = archive.database.GetFolderStream(
stream,
currentFolder,
new PasswordProvider(Options.Password)
);
}
foreach (var entry in group)
{

View File

@@ -0,0 +1,87 @@
#nullable disable
using System.Security.Cryptography;
using System.Text;
using SharpCompress.Common.Rar.Headers;
namespace SharpCompress.Common.Rar;
internal class CryptKey3 : ICryptKey
{
const int AES_128 = 128;
private string _password;
public CryptKey3(string password)
{
_password = password ?? "";
}
public ICryptoTransform Transformer(byte[] salt)
{
var aesIV = new byte[EncryptionConstV5.SIZE_INITV];
var rawLength = 2 * _password.Length;
var rawPassword = new byte[rawLength + EncryptionConstV5.SIZE_SALT30];
var passwordBytes = Encoding.UTF8.GetBytes(_password);
for (var i = 0; i < _password.Length; i++)
{
rawPassword[i * 2] = passwordBytes[i];
rawPassword[(i * 2) + 1] = 0;
}
for (var i = 0; i < salt.Length; i++)
{
rawPassword[i + rawLength] = salt[i];
}
var msgDigest = SHA1.Create();
const int noOfRounds = (1 << 18);
const int iblock = 3;
byte[] digest;
var data = new byte[(rawPassword.Length + iblock) * noOfRounds];
//TODO slow code below, find ways to optimize
for (var i = 0; i < noOfRounds; i++)
{
rawPassword.CopyTo(data, i * (rawPassword.Length + iblock));
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 0] = (byte)i;
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 1] = (byte)(i >> 8);
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 2] = (byte)(i >> 16);
if (i % (noOfRounds / EncryptionConstV5.SIZE_INITV) == 0)
{
digest = msgDigest.ComputeHash(data, 0, (i + 1) * (rawPassword.Length + iblock));
aesIV[i / (noOfRounds / EncryptionConstV5.SIZE_INITV)] = digest[19];
}
}
digest = msgDigest.ComputeHash(data);
//slow code ends
var aesKey = new byte[EncryptionConstV5.SIZE_INITV];
for (var i = 0; i < 4; i++)
{
for (var 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)
);
}
}
var aes = Aes.Create();
aes.KeySize = AES_128;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
aes.Key = aesKey;
aes.IV = aesIV;
return aes.CreateDecryptor();
}
}

View File

@@ -0,0 +1,97 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using SharpCompress.Common.Rar.Headers;
namespace SharpCompress.Common.Rar;
internal class CryptKey5 : ICryptKey
{
const int AES_256 = 256;
const int DERIVED_KEY_LENGTH = 0x10;
const int SHA256_DIGEST_SIZE = 32;
private string _password;
private Rar5CryptoInfo _cryptoInfo;
private byte[] _pswCheck = { };
private byte[] _hashKey = { };
public CryptKey5(string password, Rar5CryptoInfo rar5CryptoInfo)
{
_password = password ?? "";
_cryptoInfo = rar5CryptoInfo;
}
public byte[] PswCheck => _pswCheck;
public byte[] HashKey => _hashKey;
private static List<byte[]> GenerateRarPBKDF2Key(
string password,
byte[] salt,
int iterations,
int keyLength
)
{
using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(password)))
{
byte[] block = hmac.ComputeHash(salt);
byte[] finalHash = (byte[])block.Clone();
var loop = new int[] { iterations, 17, 17 };
var res = new List<byte[]> { };
for (int x = 0; x < 3; x++)
{
for (int i = 1; i < loop[x]; i++)
{
block = hmac.ComputeHash(block);
for (int j = 0; j < finalHash.Length; j++)
{
finalHash[j] ^= block[j];
}
}
res.Add((byte[])finalHash.Clone());
}
return res;
}
}
public ICryptoTransform Transformer(byte[] salt)
{
int iterations = (1 << _cryptoInfo.LG2Count); // Adjust the number of iterations as needed
var salt_rar5 = salt.Concat(new byte[] { 0, 0, 0, 1 });
var derivedKey = GenerateRarPBKDF2Key(
_password,
salt_rar5.ToArray(),
iterations,
DERIVED_KEY_LENGTH
);
_hashKey = derivedKey[1];
_pswCheck = new byte[EncryptionConstV5.SIZE_PSWCHECK];
for (int i = 0; i < SHA256_DIGEST_SIZE; i++)
{
_pswCheck[i % EncryptionConstV5.SIZE_PSWCHECK] ^= derivedKey[2][i];
}
if (_cryptoInfo.UsePswCheck && !_cryptoInfo.PswCheck.SequenceEqual(_pswCheck))
{
throw new CryptographicException("The password did not match.");
}
var aes = Aes.Create();
aes.KeySize = AES_256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
aes.Key = derivedKey[0];
aes.IV = _cryptoInfo.InitV;
return aes.CreateDecryptor();
}
}

View File

@@ -1,50 +1,21 @@
#nullable disable
using System;
using System.Security.Cryptography;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.IO;
namespace SharpCompress.Common.Rar.Headers;
internal class ArchiveCryptHeader : RarHeader
{
private const int CRYPT_VERSION = 0; // Supported encryption version.
private const int SIZE_SALT50 = 16;
private const int SIZE_PSWCHECK = 8;
private const int SIZE_PSWCHECK_CSUM = 4;
private const int CRYPT5_KDF_LG2_COUNT_MAX = 24; // LOG2 of maximum accepted iteration count.
private bool _usePswCheck;
private uint _lg2Count; // Log2 of PBKDF2 repetition count.
private byte[] _salt;
private byte[] _pswCheck;
private byte[] _pswCheckCsm;
public ArchiveCryptHeader(RarHeader header, RarCrcBinaryReader reader)
: base(header, reader, HeaderType.Crypt) { }
public Rar5CryptoInfo CryptInfo = new Rar5CryptoInfo();
protected override void ReadFinish(MarkingBinaryReader reader)
{
var cryptVersion = reader.ReadRarVIntUInt32();
if (cryptVersion > CRYPT_VERSION)
{
//error?
return;
}
var encryptionFlags = reader.ReadRarVIntUInt32();
_usePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK);
_lg2Count = reader.ReadRarVIntByte(1);
//UsePswCheck = HasHeaderFlag(EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK);
if (_lg2Count > CRYPT5_KDF_LG2_COUNT_MAX)
{
//error?
return;
}
_salt = reader.ReadBytes(SIZE_SALT50);
if (_usePswCheck)
{
_pswCheck = reader.ReadBytes(SIZE_PSWCHECK);
_pswCheckCsm = reader.ReadBytes(SIZE_PSWCHECK_CSUM);
}
CryptInfo = new Rar5CryptoInfo(reader, false);
}
}

View File

@@ -2,6 +2,8 @@
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using SharpCompress.IO;
#if !Rar2017_64bit
@@ -16,7 +18,7 @@ namespace SharpCompress.Common.Rar.Headers;
internal class FileHeader : RarHeader
{
private uint _fileCrc;
private byte[] _hash;
public FileHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType)
: base(header, reader, headerType) { }
@@ -51,7 +53,7 @@ internal class FileHeader : RarHeader
if (HasFlag(FileFlagsV5.HAS_CRC32))
{
FileCrc = reader.ReadUInt32();
FileCrc = reader.ReadBytes(4);
}
var compressionInfo = reader.ReadRarVIntUInt16();
@@ -103,7 +105,13 @@ internal class FileHeader : RarHeader
throw new InvalidFormatException("rar5 header size / extra size inconsistency");
}
isEncryptedRar5 = false;
const ushort FHEXTRA_CRYPT = 0x01;
const ushort FHEXTRA_HASH = 0x02;
const ushort FHEXTRA_HTIME = 0x03;
// const ushort FHEXTRA_VERSION = 0x04;
// const ushort FHEXTRA_REDIR = 0x05;
// const ushort FHEXTRA_UOWNER = 0x06;
// const ushort FHEXTRA_SUBDATA = 0x07;
while (RemainingHeaderBytes(reader) > 0)
{
@@ -113,21 +121,32 @@ internal class FileHeader : RarHeader
switch (type)
{
//TODO
case 1: // file encryption
case FHEXTRA_CRYPT: // file encryption
{
isEncryptedRar5 = true;
Rar5CryptoInfo = new Rar5CryptoInfo(reader, true);
//var version = reader.ReadRarVIntByte();
//if (version != 0) throw new InvalidFormatException("unknown encryption algorithm " + version);
if (Rar5CryptoInfo.PswCheck.All(singleByte => singleByte == 0))
{
Rar5CryptoInfo = null;
}
}
break;
// case 2: // file hash
// {
//
// }
// break;
case 3: // file time
case FHEXTRA_HASH:
{
const uint FHEXTRA_HASH_BLAKE2 = 0x0;
// const uint HASH_BLAKE2 = 0x03;
const int BLAKE2_DIGEST_SIZE = 0x20;
if ((uint)reader.ReadRarVInt() == FHEXTRA_HASH_BLAKE2)
{
// var hash = HASH_BLAKE2;
_hash = reader.ReadBytes(BLAKE2_DIGEST_SIZE);
}
// enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
}
break;
case FHEXTRA_HTIME: // file time
{
var flags = reader.ReadRarVIntUInt16();
@@ -147,22 +166,22 @@ internal class FileHeader : RarHeader
}
break;
//TODO
// case 4: // file version
// case FHEXTRA_VERSION: // file version
// {
//
// }
// break;
// case 5: // file system redirection
// case FHEXTRA_REDIR: // file system redirection
// {
//
// }
// break;
// case 6: // unix owner
// case FHEXTRA_UOWNER: // unix owner
// {
//
// }
// break;
// case 7: // service data
// case FHEXTRA_SUBDATA: // service data
// {
//
// }
@@ -221,7 +240,7 @@ internal class FileHeader : RarHeader
HostOs = reader.ReadByte();
FileCrc = reader.ReadUInt32();
FileCrc = reader.ReadBytes(4);
FileLastModifiedTime = Utility.DosDateToDateTime(reader.ReadUInt32());
@@ -254,7 +273,6 @@ internal class FileHeader : RarHeader
var fileNameBytes = reader.ReadBytes(nameSize);
const int saltSize = 8;
const int newLhdSize = 32;
switch (HeaderCode)
@@ -292,7 +310,7 @@ internal class FileHeader : RarHeader
var datasize = HeaderSize - newLhdSize - nameSize;
if (HasFlag(FileFlagsV4.SALT))
{
datasize -= saltSize;
datasize -= EncryptionConstV5.SIZE_SALT30;
}
if (datasize > 0)
{
@@ -313,7 +331,7 @@ internal class FileHeader : RarHeader
if (HasFlag(FileFlagsV4.SALT))
{
R4Salt = reader.ReadBytes(saltSize);
R4Salt = reader.ReadBytes(EncryptionConstV5.SIZE_SALT30);
}
if (HasFlag(FileFlagsV4.EXT_TIME))
{
@@ -394,18 +412,10 @@ internal class FileHeader : RarHeader
private bool HasFlag(ushort flag) => (Flags & flag) == flag;
internal uint FileCrc
internal byte[] FileCrc
{
get
{
if (IsRar5 && !HasFlag(FileFlagsV5.HAS_CRC32))
{
//!!! rar5:
throw new InvalidOperationException("TODO rar5");
}
return _fileCrc;
}
private set => _fileCrc = value;
get => _hash;
private set => _hash = value;
}
// 0 - storing
@@ -431,7 +441,7 @@ internal class FileHeader : RarHeader
internal size_t WindowSize { get; private set; }
internal byte[] R4Salt { get; private set; }
internal Rar5CryptoInfo Rar5CryptoInfo { get; private set; }
private byte HostOs { get; set; }
internal uint FileAttributes { get; private set; }
internal long CompressedSize { get; private set; }
@@ -449,8 +459,7 @@ internal class FileHeader : RarHeader
public bool IsDirectory => HasFlag(IsRar5 ? FileFlagsV5.DIRECTORY : FileFlagsV4.DIRECTORY);
private bool isEncryptedRar5 = false;
public bool IsEncrypted => IsRar5 ? isEncryptedRar5 : HasFlag(FileFlagsV4.PASSWORD);
public bool IsEncrypted => IsRar5 ? Rar5CryptoInfo != null : HasFlag(FileFlagsV4.PASSWORD);
internal DateTime? FileLastModifiedTime { get; private set; }

View File

@@ -50,6 +50,17 @@ internal static class EncryptionFlagsV5
public const uint FHEXTRA_CRYPT_HASHMAC = 0x02;
}
internal static class EncryptionConstV5
{
public const int VERSION = 0;
public const uint CRYPT5_KDF_LG2_COUNT_MAX = 0x24;
public const int SIZE_SALT30 = 0x08;
public const int SIZE_SALT50 = 0x10;
public const int SIZE_INITV = 0x10;
public const int SIZE_PSWCHECK = 0x08;
public const int SIZE_PSWCHECK_CSUM = 0x04;
}
internal static class HeaderFlagsV5
{
public const ushort HAS_EXTRA = 0x0001;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using SharpCompress.IO;

View File

@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Common.Rar;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -9,6 +11,8 @@ public class RarHeaderFactory
{
private bool _isRar5;
private Rar5CryptoInfo? _cryptInfo;
public RarHeaderFactory(StreamingMode mode, ReaderOptions options)
{
StreamingMode = mode;
@@ -53,7 +57,19 @@ public class RarHeaderFactory
"Encrypted Rar archive has no password specified."
);
}
reader = new RarCryptoBinaryReader(stream, Options.Password);
if (_isRar5 && _cryptInfo != null)
{
_cryptInfo.ReadInitV(new MarkingBinaryReader(stream));
var _headerKey = new CryptKey5(Options.Password!, _cryptInfo);
reader = new RarCryptoBinaryReader(stream, _headerKey, _cryptInfo.Salt);
}
else
{
var key = new CryptKey3(Options.Password);
reader = new RarCryptoBinaryReader(stream, key);
}
}
var header = RarHeader.TryReadBase(reader, _isRar5, Options.ArchiveEncoding);
@@ -105,7 +121,14 @@ public class RarHeaderFactory
case HeaderCodeV.RAR5_SERVICE_HEADER:
{
var fh = new FileHeader(header, reader, HeaderType.Service);
SkipData(fh, reader);
if (fh.FileName == "CMT")
{
fh.PackedStream = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize);
}
else
{
SkipData(fh, reader);
}
return fh;
}
@@ -133,7 +156,7 @@ public class RarHeaderFactory
{
var ms = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize);
if (fh.R4Salt is null)
if (fh.R4Salt is null && fh.Rar5CryptoInfo is null)
{
fh.PackedStream = ms;
}
@@ -141,8 +164,10 @@ public class RarHeaderFactory
{
fh.PackedStream = new RarCryptoWrapper(
ms,
Options.Password!,
fh.R4Salt
fh.R4Salt is null ? fh.Rar5CryptoInfo.Salt : fh.R4Salt,
fh.R4Salt is null
? new CryptKey5(Options.Password!, fh.Rar5CryptoInfo)
: new CryptKey3(Options.Password!)
);
}
}
@@ -161,9 +186,11 @@ public class RarHeaderFactory
}
case HeaderCodeV.RAR5_ARCHIVE_ENCRYPTION_HEADER:
{
var ch = new ArchiveCryptHeader(header, reader);
var cryptoHeader = new ArchiveCryptHeader(header, reader);
IsEncrypted = true;
return ch;
_cryptInfo = cryptoHeader.CryptInfo;
return cryptoHeader;
}
default:
{

View File

@@ -0,0 +1,8 @@
using System.Security.Cryptography;
namespace SharpCompress.Common.Rar;
internal interface ICryptKey
{
ICryptoTransform Transformer(byte[] salt);
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Security.Cryptography;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.IO;
namespace SharpCompress.Common.Rar;
internal class Rar5CryptoInfo
{
public Rar5CryptoInfo() { }
public Rar5CryptoInfo(MarkingBinaryReader reader, bool readInitV)
{
var cryptVersion = reader.ReadRarVIntUInt32();
if (cryptVersion > EncryptionConstV5.VERSION)
{
throw new CryptographicException($"Unsupported crypto version of {cryptVersion}");
}
var encryptionFlags = reader.ReadRarVIntUInt32();
UsePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK);
LG2Count = reader.ReadRarVIntByte(1);
if (LG2Count > EncryptionConstV5.CRYPT5_KDF_LG2_COUNT_MAX)
{
throw new CryptographicException($"Unsupported LG2 count of {LG2Count}.");
}
Salt = reader.ReadBytes(EncryptionConstV5.SIZE_SALT50);
if (readInitV) // File header needs to read IV here
{
ReadInitV(reader);
}
if (UsePswCheck)
{
PswCheck = reader.ReadBytes(EncryptionConstV5.SIZE_PSWCHECK);
var _pswCheckCsm = reader.ReadBytes(EncryptionConstV5.SIZE_PSWCHECK_CSUM);
var sha = SHA256.Create();
UsePswCheck = sha.ComputeHash(PswCheck).AsSpan().StartsWith(_pswCheckCsm.AsSpan());
}
}
public void ReadInitV(MarkingBinaryReader reader)
{
InitV = reader.ReadBytes(EncryptionConstV5.SIZE_INITV);
}
public bool UsePswCheck = false;
public int LG2Count = 0;
public byte[] InitV = { };
public byte[] Salt = { };
public byte[] PswCheck = { };
}

View File

@@ -1,27 +1,30 @@
using System.Collections.Generic;
#nullable disable
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.Crypto;
namespace SharpCompress.Common.Rar;
internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader
{
private RarRijndael _rijndael;
private byte[] _salt;
private readonly string _password;
private BlockTransformer _rijndael;
private readonly Queue<byte> _data = new Queue<byte>();
private long _readCount;
public RarCryptoBinaryReader(Stream stream, string password)
public RarCryptoBinaryReader(Stream stream, ICryptKey cryptKey)
: base(stream)
{
_password = password;
var salt = base.ReadBytes(EncryptionConstV5.SIZE_SALT30);
_readCount += EncryptionConstV5.SIZE_SALT30;
_rijndael = new BlockTransformer(cryptKey.Transformer(salt));
}
// coderb: not sure why this was being done at this logical point
//SkipQueue();
var salt = ReadBytes(8);
_salt = salt;
_rijndael = RarRijndael.InitializeFrom(_password, salt);
public RarCryptoBinaryReader(Stream stream, ICryptKey cryptKey, byte[] salt)
: base(stream)
{
_rijndael = new BlockTransformer(cryptKey.Transformer(salt));
}
// track read count ourselves rather than using the underlying stream since we buffer
@@ -36,28 +39,14 @@ internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader
public override void Mark() => _readCount = 0;
private bool UseEncryption => _salt != null;
public override byte ReadByte()
{
if (UseEncryption)
{
return ReadAndDecryptBytes(1)[0];
}
_readCount++;
return base.ReadByte();
return ReadAndDecryptBytes(1)[0];
}
public override byte[] ReadBytes(int count)
{
if (UseEncryption)
{
return ReadAndDecryptBytes(count);
}
_readCount += count;
return base.ReadBytes(count);
return ReadAndDecryptBytes(count);
}
private byte[] ReadAndDecryptBytes(int count)

View File

@@ -1,24 +1,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Crypto;
namespace SharpCompress.Common.Rar;
internal sealed class RarCryptoWrapper : Stream
{
private readonly Stream _actualStream;
private readonly byte[] _salt;
private RarRijndael _rijndael;
private BlockTransformer _rijndael;
private readonly Queue<byte> _data = new Queue<byte>();
public RarCryptoWrapper(Stream actualStream, string password, byte[] salt)
public RarCryptoWrapper(Stream actualStream, byte[] salt, ICryptKey key)
{
_actualStream = actualStream;
_salt = salt;
_rijndael = RarRijndael.InitializeFrom(password ?? "", salt);
_rijndael = new BlockTransformer(key.Transformer(salt));
}
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
@@ -26,10 +25,6 @@ internal sealed class RarCryptoWrapper : Stream
public override int Read(byte[] buffer, int offset, int count)
{
if (_salt is null)
{
return _actualStream.Read(buffer, offset, count);
}
return ReadAndDecrypt(buffer, offset, count);
}
@@ -41,7 +36,7 @@ internal sealed class RarCryptoWrapper : Stream
if (sizeToRead > 0)
{
var alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf);
Span<byte> cipherText = stackalloc byte[RarRijndael.CRYPTO_BLOCK_SIZE];
Span<byte> cipherText = stackalloc byte[16];
for (var i = 0; i < alignedSize / 16; i++)
{
//long ax = System.currentTimeMillis();

View File

@@ -20,7 +20,7 @@ public abstract class RarEntry : Entry
/// <summary>
/// The File's 32 bit CRC Hash
/// </summary>
public override long Crc => FileHeader.FileCrc;
public override long Crc => BitConverter.ToUInt32(FileHeader.FileCrc, 0);
/// <summary>
/// The path of the file internal to the Rar Archive.
@@ -55,7 +55,12 @@ public abstract class RarEntry : Entry
public override bool IsEncrypted => FileHeader.IsEncrypted;
/// <summary>
/// Entry is password protected and encrypted and cannot be extracted.
/// Entry Windows file attributes
/// </summary>
public override int? Attrib => (int)FileHeader.FileAttributes;
/// <summary>
/// Entry is a directory
/// </summary>
public override bool IsDirectory => FileHeader.IsDirectory;

View File

@@ -1,114 +0,0 @@
#nullable disable
using System;
using System.Security.Cryptography;
using System.Text;
using SharpCompress.Crypto;
namespace SharpCompress.Common.Rar;
internal class RarRijndael : IDisposable
{
internal const int CRYPTO_BLOCK_SIZE = 16;
private readonly string _password;
private readonly byte[] _salt;
private byte[] _aesInitializationVector;
private RijndaelEngine _rijndael;
private RarRijndael(string password, byte[] salt)
{
_password = password;
_salt = salt;
}
private void Initialize()
{
_rijndael = new RijndaelEngine();
_aesInitializationVector = new byte[CRYPTO_BLOCK_SIZE];
var rawLength = 2 * _password.Length;
var rawPassword = new byte[rawLength + 8];
var passwordBytes = Encoding.UTF8.GetBytes(_password);
for (var i = 0; i < _password.Length; i++)
{
rawPassword[i * 2] = passwordBytes[i];
rawPassword[(i * 2) + 1] = 0;
}
for (var i = 0; i < _salt.Length; i++)
{
rawPassword[i + rawLength] = _salt[i];
}
const int noOfRounds = (1 << 18);
const int iblock = 3;
byte[] digest;
var data = new byte[(rawPassword.Length + iblock) * noOfRounds];
//TODO slow code below, find ways to optimize
for (var i = 0; i < noOfRounds; i++)
{
rawPassword.CopyTo(data, i * (rawPassword.Length + iblock));
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 0] = (byte)i;
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 1] = (byte)(i >> 8);
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 2] = (byte)(
i >> CRYPTO_BLOCK_SIZE
);
if (i % (noOfRounds / CRYPTO_BLOCK_SIZE) == 0)
{
digest = SHA1.Create()
.ComputeHash(data, 0, (i + 1) * (rawPassword.Length + iblock));
_aesInitializationVector[i / (noOfRounds / CRYPTO_BLOCK_SIZE)] = digest[19];
}
}
digest = SHA1.Create().ComputeHash(data);
//slow code ends
var aesKey = new byte[CRYPTO_BLOCK_SIZE];
for (var i = 0; i < 4; i++)
{
for (var 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.Init(false, new KeyParameter(aesKey));
}
public static RarRijndael InitializeFrom(string password, byte[] salt)
{
var rijndael = new RarRijndael(password, salt);
rijndael.Initialize();
return rijndael;
}
public byte[] ProcessBlock(ReadOnlySpan<byte> cipherText)
{
Span<byte> plainText = stackalloc byte[CRYPTO_BLOCK_SIZE]; // 16 bytes
var decryptedBytes = new byte[CRYPTO_BLOCK_SIZE];
_rijndael.ProcessBlock(cipherText, plainText);
for (var j = 0; j < CRYPTO_BLOCK_SIZE; j++)
{
decryptedBytes[j] = (byte)(plainText[j] ^ _aesInitializationVector[j % 16]); //32:114, 33:101
}
for (var j = 0; j < _aesInitializationVector.Length; j++)
{
_aesInitializationVector[j] = cipherText[j];
}
return decryptedBytes;
}
public void Dispose() { }
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -70,11 +71,7 @@ public abstract class RarVolume : Volume
var part = CreateFilePart(lastMarkHeader!, fh);
var buffer = new byte[fh.CompressedSize];
part.GetCompressedStream().Read(buffer, 0, buffer.Length);
Comment = System
.Text
.Encoding
.UTF8
.GetString(buffer, 0, buffer.Length - 1);
Comment = Encoding.UTF8.GetString(buffer, 0, buffer.Length - 1);
}
}
break;

View File

@@ -1393,7 +1393,7 @@ internal class ArchiveReader
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => throw new NotSupportedException();

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using System;
using System.Buffers.Binary;
@@ -132,8 +132,8 @@ internal sealed class TarHeader
Mode |= 0b1_000_000_000;
}
UserId = ReadAsciiInt64Base8(buffer, 108, 7);
GroupId = ReadAsciiInt64Base8(buffer, 116, 7);
UserId = ReadAsciiInt64Base8oldGnu(buffer, 108, 7);
GroupId = ReadAsciiInt64Base8oldGnu(buffer, 116, 7);
var unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11);
LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime();
@@ -249,6 +249,24 @@ internal sealed class TarHeader
return Convert.ToInt64(s, 8);
}
private static long ReadAsciiInt64Base8oldGnu(byte[] buffer, int offset, int count)
{
if (buffer[offset] == 0x80 && buffer[offset + 1] == 0x00)
{
return buffer[offset + 4] << 24
| buffer[offset + 5] << 16
| buffer[offset + 6] << 8
| buffer[offset + 7];
}
var s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls();
if (string.IsNullOrEmpty(s))
{
return 0;
}
return Convert.ToInt64(s, 8);
}
private static long ReadAsciiInt64(byte[] buffer, int offset, int count)
{
var s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls();

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using SharpCompress.IO;
@@ -47,7 +47,7 @@ internal class TarReadOnlySubStream : NonDisposingStream
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => throw new NotSupportedException();

View File

@@ -85,6 +85,36 @@ internal class DirectoryEntryHeader : ZipFileEntry
RelativeOffsetOfEntryHeader = zip64ExtraData.RelativeOffsetOfEntryHeader;
}
}
var unixTimeExtra = Extra.FirstOrDefault(u => u.Type == ExtraDataType.UnixTimeExtraField);
if (unixTimeExtra is not null)
{
// Tuple order is last modified time, last access time, and creation time.
var unixTimeTuple = ((UnixTimeExtraField)unixTimeExtra).UnicodeTimes;
if (unixTimeTuple.Item1.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item1.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
else if (unixTimeTuple.Item2.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item2.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
else if (unixTimeTuple.Item3.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item3.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
}
}
internal ushort Version { get; private set; }

View File

@@ -64,6 +64,36 @@ internal class LocalEntryHeader : ZipFileEntry
UncompressedSize = zip64ExtraData.UncompressedSize;
}
}
var unixTimeExtra = Extra.FirstOrDefault(u => u.Type == ExtraDataType.UnixTimeExtraField);
if (unixTimeExtra is not null)
{
// Tuple order is last modified time, last access time, and creation time.
var unixTimeTuple = ((UnixTimeExtraField)unixTimeExtra).UnicodeTimes;
if (unixTimeTuple.Item1.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item1.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
else if (unixTimeTuple.Item2.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item2.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
else if (unixTimeTuple.Item3.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item3.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
}
}
internal ushort Version { get; private set; }

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Buffers.Binary;
using System.Text;
@@ -13,7 +13,8 @@ internal enum ExtraDataType : ushort
// Third Party Mappings
// -Info-ZIP Unicode Path Extra Field
UnicodePathExtraField = 0x7075,
Zip64ExtendedInformationExtraField = 0x0001
Zip64ExtendedInformationExtraField = 0x0001,
UnixTimeExtraField = 0x5455
}
internal class ExtraData
@@ -145,6 +146,84 @@ internal sealed class Zip64ExtendedInformationExtraField : ExtraData
public uint VolumeNumber { get; private set; }
}
internal sealed class UnixTimeExtraField : ExtraData
{
public UnixTimeExtraField(ExtraDataType type, ushort length, byte[] dataBytes)
: base(type, length, dataBytes) { }
/// <summary>
/// The unix modified time, last access time, and creation time, if set.
/// </summary>
/// <remarks>Must return Tuple explicitly due to net462 support.</remarks>
internal Tuple<DateTime?, DateTime?, DateTime?> UnicodeTimes
{
get
{
// There has to be at least 5 byte for there to be a timestamp.
// 1 byte for flags and 4 bytes for a timestamp.
if (DataBytes is null || DataBytes.Length < 5)
{
return Tuple.Create<DateTime?, DateTime?, DateTime?>(null, null, null);
}
var flags = DataBytes[0];
var isModifiedTimeSpecified = (flags & 0x01) == 1;
var isLastAccessTimeSpecified = (flags & 0x02) == 1;
var isCreationTimeSpecified = (flags & 0x04) == 1;
var currentIndex = 1;
DateTime? modifiedTime = null;
DateTime? lastAccessTime = null;
DateTime? creationTime = null;
if (isModifiedTimeSpecified)
{
var modifiedEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
DataBytes.AsSpan(currentIndex, 4)
);
currentIndex += 4;
modifiedTime = DateTimeOffset.FromUnixTimeSeconds(modifiedEpochTime).UtcDateTime;
}
if (isLastAccessTimeSpecified)
{
if (currentIndex + 4 > DataBytes.Length)
{
throw new ArchiveException("Invalid UnicodeExtraTime field");
}
var lastAccessEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
DataBytes.AsSpan(currentIndex, 4)
);
currentIndex += 4;
lastAccessTime = DateTimeOffset
.FromUnixTimeSeconds(lastAccessEpochTime)
.UtcDateTime;
}
if (isCreationTimeSpecified)
{
if (currentIndex + 4 > DataBytes.Length)
{
throw new ArchiveException("Invalid UnicodeExtraTime field");
}
var creationTimeEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
DataBytes.AsSpan(currentIndex, 4)
);
currentIndex += 4;
creationTime = DateTimeOffset
.FromUnixTimeSeconds(creationTimeEpochTime)
.UtcDateTime;
}
return Tuple.Create(modifiedTime, lastAccessTime, creationTime);
}
}
}
internal static class LocalEntryHeaderExtraFactory
{
internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extraData) =>
@@ -154,6 +233,7 @@ internal static class LocalEntryHeaderExtraFactory
=> new ExtraUnicodePathExtraField(type, length, extraData),
ExtraDataType.Zip64ExtendedInformationExtraField
=> new Zip64ExtendedInformationExtraField(type, length, extraData),
ExtraDataType.UnixTimeExtraField => new UnixTimeExtraField(type, length, extraData),
_ => new ExtraData(type, length, extraData)
};
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
namespace SharpCompress.Common.Zip;
@@ -87,10 +87,7 @@ internal class PkwareTraditionalCryptoStream : Stream
_stream.Write(encrypted, 0, encrypted.Length);
}
public override void Flush()
{
//throw new NotSupportedException();
}
public override void Flush() { }
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();

View File

@@ -73,7 +73,7 @@ internal class WinzipAesCryptoStream : Stream
}
}
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count)
{

View File

@@ -48,15 +48,15 @@ internal class WinzipAesEncryptionData
private void Initialize()
{
#if NET7_0
#if NETFRAMEWORK || NETSTANDARD2_0
var rfc2898 = new Rfc2898DeriveBytes(_password, _salt, RFC2898_ITERATIONS);
#else
var rfc2898 = new Rfc2898DeriveBytes(
_password,
_salt,
RFC2898_ITERATIONS,
HashAlgorithmName.SHA1
);
#else
var rfc2898 = new Rfc2898DeriveBytes(_password, _salt, RFC2898_ITERATIONS);
#endif
KeyBytes = rfc2898.GetBytes(KeySizeInBytes); // 16 or 24 or 32 ???

View File

@@ -142,9 +142,9 @@ internal class ZipHeaderFactory
if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes)
{
var data = entryHeader
.Extra
.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes);
var data = entryHeader.Extra.SingleOrDefault(
x => x.Type == ExtraDataType.WinZipAes
);
if (data != null)
{
var keySize = (WinzipAesKeySize)data.DataBytes[4];

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
namespace SharpCompress.Compressors.Filters;
@@ -79,7 +79,7 @@ internal class BCJ2Filter : Stream
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => _baseStream.Length + _data1.Length + _data2.Length;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
namespace SharpCompress.Compressors.Filters;
@@ -40,7 +40,7 @@ internal abstract class Filter : Stream
public override bool CanWrite => _isEncoder;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => _baseStream.Length;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using SharpCompress.Common.SevenZip;
using SharpCompress.Compressors.LZMA.Utilites;
@@ -14,7 +14,7 @@ internal abstract class DecoderStream2 : Stream
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => throw new NotSupportedException();

View File

@@ -207,14 +207,23 @@ public class LzmaStream : Stream
if (_availableBytes == 0 && !_uncompressedChunk)
{
_rangeDecoder.ReleaseStream();
// Check range corruption scenario
if (
!_rangeDecoder.IsFinished
|| (_rangeDecoderLimit >= 0 && _rangeDecoder._total != _rangeDecoderLimit)
)
{
throw new DataErrorException();
// Stream might have End Of Stream marker
_outWindow.SetLimit(toProcess + 1);
if (!_decoder.Code(_dictionarySize, _outWindow, _rangeDecoder))
{
_rangeDecoder.ReleaseStream();
throw new DataErrorException();
}
}
_rangeDecoder.ReleaseStream();
_inputPosition += _rangeDecoder._total;
if (_outWindow.HasPending)
{

View File

@@ -128,9 +128,9 @@ internal sealed class MultiVolumeReadOnlyStream : Stream
public override bool CanWrite => false;
public uint CurrentCrc { get; private set; }
public byte[] CurrentCrc { get; private set; }
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => throw new NotSupportedException();

View File

@@ -0,0 +1,305 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using SharpCompress.Common;
using SharpCompress.Common.Rar.Headers;
namespace SharpCompress.Compressors.Rar;
internal class RarBLAKE2spStream : RarStream
{
private readonly MultiVolumeReadOnlyStream readStream;
private readonly bool disableCRCCheck;
const uint BLAKE2S_NUM_ROUNDS = 10;
const uint BLAKE2S_FINAL_FLAG = (~(uint)0);
const int BLAKE2S_BLOCK_SIZE = 64;
const int BLAKE2S_DIGEST_SIZE = 32;
const int BLAKE2SP_PARALLEL_DEGREE = 8;
const uint BLAKE2S_INIT_IV_SIZE = 8;
static readonly UInt32[] k_BLAKE2S_IV =
{
0x6A09E667U,
0xBB67AE85U,
0x3C6EF372U,
0xA54FF53AU,
0x510E527FU,
0x9B05688CU,
0x1F83D9ABU,
0x5BE0CD19U
};
static readonly byte[][] k_BLAKE2S_Sigma =
{
new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
new byte[] { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
new byte[] { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
new byte[] { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
new byte[] { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
new byte[] { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
new byte[] { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
new byte[] { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
new byte[] { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
new byte[] { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
};
internal class BLAKE2S
{
internal UInt32[] h;
internal UInt32[] t;
internal UInt32[] f;
internal byte[] b;
internal int bufferPosition;
internal UInt32 lastNodeFlag;
UInt32[] dummy;
public BLAKE2S()
{
h = new uint[BLAKE2S_INIT_IV_SIZE];
t = new uint[2];
f = new uint[2];
b = new byte[BLAKE2S_BLOCK_SIZE];
dummy = new uint[2];
}
};
internal class BLAKE2SP
{
internal BLAKE2S[] S;
internal int bufferPosition;
public BLAKE2SP()
{
S = new BLAKE2S[BLAKE2SP_PARALLEL_DEGREE];
for (int i = 0; i < S.Length; i++)
{
S[i] = new BLAKE2S();
}
}
};
BLAKE2SP _blake2sp;
byte[] _hash = { };
public RarBLAKE2spStream(
IRarUnpack unpack,
FileHeader fileHeader,
MultiVolumeReadOnlyStream readStream
)
: base(unpack, fileHeader, readStream)
{
this.readStream = readStream;
disableCRCCheck = fileHeader.IsEncrypted;
_hash = fileHeader.FileCrc;
_blake2sp = new BLAKE2SP();
ResetCrc();
}
public byte[] GetCrc() => _hash;
internal void ResetCrc(BLAKE2S hash)
{
for (UInt32 j = 0; j < BLAKE2S_INIT_IV_SIZE; j++)
{
hash.h[j] = k_BLAKE2S_IV[j];
}
hash.t[0] = 0;
hash.t[1] = 0;
hash.f[0] = 0;
hash.f[1] = 0;
hash.bufferPosition = 0;
hash.lastNodeFlag = 0;
}
internal void G(
ref UInt32[] m,
ref byte[] sigma,
int i,
ref UInt32 a,
ref UInt32 b,
ref UInt32 c,
ref UInt32 d
)
{
a += b + m[sigma[2 * i]];
d ^= a;
d = (d >> 16) | (d << 16);
c += d;
b ^= c;
b = (b >> 12) | (b << 20);
a += b + m[sigma[2 * i + 1]];
d ^= a;
d = (d >> 8) | (d << 24);
c += d;
b ^= c;
b = (b >> 7) | (b << 25);
}
internal void Compress(BLAKE2S hash)
{
UInt32[] m = new UInt32[16];
UInt32[] v = new UInt32[16];
for (int i = 0; i < 16; i++)
{
m[i] = BitConverter.ToUInt32(hash.b, i * 4);
}
for (int i = 0; i < 8; i++)
{
v[i] = hash.h[i];
}
v[8] = k_BLAKE2S_IV[0];
v[9] = k_BLAKE2S_IV[1];
v[10] = k_BLAKE2S_IV[2];
v[11] = k_BLAKE2S_IV[3];
v[12] = hash.t[0] ^ k_BLAKE2S_IV[4];
v[13] = hash.t[1] ^ k_BLAKE2S_IV[5];
v[14] = hash.f[0] ^ k_BLAKE2S_IV[6];
v[15] = hash.f[1] ^ k_BLAKE2S_IV[7];
for (int r = 0; r < BLAKE2S_NUM_ROUNDS; r++)
{
ref byte[] sigma = ref k_BLAKE2S_Sigma[r];
G(ref m, ref sigma, 0, ref v[0], ref v[4], ref v[8], ref v[12]);
G(ref m, ref sigma, 1, ref v[1], ref v[5], ref v[9], ref v[13]);
G(ref m, ref sigma, 2, ref v[2], ref v[6], ref v[10], ref v[14]);
G(ref m, ref sigma, 3, ref v[3], ref v[7], ref v[11], ref v[15]);
G(ref m, ref sigma, 4, ref v[0], ref v[5], ref v[10], ref v[15]);
G(ref m, ref sigma, 5, ref v[1], ref v[6], ref v[11], ref v[12]);
G(ref m, ref sigma, 6, ref v[2], ref v[7], ref v[8], ref v[13]);
G(ref m, ref sigma, 7, ref v[3], ref v[4], ref v[9], ref v[14]);
}
for (int i = 0; i < 8; i++)
{
hash.h[i] ^= v[i] ^ v[i + 8];
}
}
internal void Update(BLAKE2S hash, ReadOnlySpan<byte> data, int size)
{
int i = 0;
while (size != 0)
{
var pos = hash.bufferPosition;
var reminder = BLAKE2S_BLOCK_SIZE - pos;
if (size <= reminder)
{
data.Slice(i, size).CopyTo(new Span<byte>(hash.b, pos, size));
hash.bufferPosition += size;
return;
}
data.Slice(i, reminder).CopyTo(new Span<byte>(hash.b, pos, reminder));
hash.t[0] += BLAKE2S_BLOCK_SIZE;
hash.t[1] += hash.t[0] < BLAKE2S_BLOCK_SIZE ? 1U : 0U;
Compress(hash);
hash.bufferPosition = 0;
i += reminder;
size -= reminder;
}
}
internal byte[] Final(BLAKE2S hash)
{
hash.t[0] += (uint)hash.bufferPosition;
hash.t[1] += hash.t[0] < hash.bufferPosition ? 1U : 0U;
hash.f[0] = BLAKE2S_FINAL_FLAG;
hash.f[1] = hash.lastNodeFlag;
Array.Clear(hash.b, hash.bufferPosition, BLAKE2S_BLOCK_SIZE - hash.bufferPosition);
Compress(hash);
var mem = new MemoryStream();
for (int i = 0; i < 8; i++)
{
mem.Write(BitConverter.GetBytes(hash.h[i]), 0, 4);
}
return mem.ToArray();
}
public void ResetCrc()
{
_blake2sp.bufferPosition = 0;
for (UInt32 i = 0; i < BLAKE2SP_PARALLEL_DEGREE; i++)
{
_blake2sp.S[i].bufferPosition = 0;
ResetCrc(_blake2sp.S[i]);
_blake2sp.S[i].h[0] ^= (BLAKE2S_DIGEST_SIZE | BLAKE2SP_PARALLEL_DEGREE << 16 | 2 << 24);
_blake2sp.S[i].h[2] ^= i;
_blake2sp.S[i].h[3] ^= (BLAKE2S_DIGEST_SIZE << 24);
}
_blake2sp.S[BLAKE2SP_PARALLEL_DEGREE - 1].lastNodeFlag = BLAKE2S_FINAL_FLAG;
}
internal void Update(BLAKE2SP hash, ReadOnlySpan<byte> data, int size)
{
int i = 0;
var pos = hash.bufferPosition;
while (size != 0)
{
var index = pos / BLAKE2S_BLOCK_SIZE;
var reminder = BLAKE2S_BLOCK_SIZE - (pos & (BLAKE2S_BLOCK_SIZE - 1));
if (reminder > size)
{
reminder = size;
}
// Update(hash.S[index], data, size);
Update(hash.S[index], data.Slice(i, reminder), reminder);
size -= reminder;
i += reminder;
pos += reminder;
pos &= (BLAKE2S_BLOCK_SIZE * (BLAKE2SP_PARALLEL_DEGREE - 1));
}
hash.bufferPosition = pos;
}
internal byte[] Final(BLAKE2SP hash)
{
var h = new BLAKE2S();
ResetCrc(h);
h.h[0] ^= (BLAKE2S_DIGEST_SIZE | BLAKE2SP_PARALLEL_DEGREE << 16 | 2 << 24);
h.h[3] ^= (1 << 16 | BLAKE2S_DIGEST_SIZE << 24);
h.lastNodeFlag = BLAKE2S_FINAL_FLAG;
for (int i = 0; i < BLAKE2SP_PARALLEL_DEGREE; i++)
{
var digest = Final(_blake2sp.S[i]);
Update(h, digest, BLAKE2S_DIGEST_SIZE);
}
return Final(h);
}
public override int Read(byte[] buffer, int offset, int count)
{
var result = base.Read(buffer, offset, count);
if (result != 0)
{
Update(_blake2sp, new ReadOnlySpan<byte>(buffer, offset, result), result);
}
else
{
_hash = Final(_blake2sp);
if (!disableCRCCheck && !(GetCrc().SequenceEqual(readStream.CurrentCrc)) && count != 0)
{
// NOTE: we use the last FileHeader in a multipart volume to check CRC
throw new InvalidFormatException("file crc mismatch");
}
}
return result;
}
}

View File

@@ -1,3 +1,4 @@
using System;
using SharpCompress.Common;
using SharpCompress.Common.Rar.Headers;
@@ -7,6 +8,7 @@ internal class RarCrcStream : RarStream
{
private readonly MultiVolumeReadOnlyStream readStream;
private uint currentCrc;
private readonly bool disableCRC;
public RarCrcStream(
IRarUnpack unpack,
@@ -16,6 +18,7 @@ internal class RarCrcStream : RarStream
: base(unpack, fileHeader, readStream)
{
this.readStream = readStream;
disableCRC = fileHeader.IsEncrypted;
ResetCrc();
}
@@ -30,7 +33,11 @@ internal class RarCrcStream : RarStream
{
currentCrc = RarCRC.CheckCrc(currentCrc, buffer, offset, result);
}
else if (GetCrc() != readStream.CurrentCrc && count != 0)
else if (
!disableCRC
&& GetCrc() != BitConverter.ToUInt32(readStream.CurrentCrc, 0)
&& count != 0
)
{
// NOTE: we use the last FileHeader in a multipart volume to check CRC
throw new InvalidFormatException("file crc mismatch");

View File

@@ -1269,10 +1269,9 @@ internal sealed partial class Unpack : BitInput, IRarUnpack
if (CurSize < DataSize + RarVM.VM_FIXEDGLOBALSIZE)
{
// StackFilter->Prg.GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE-CurSize);
StackFilter
.Program
.GlobalData
.SetSize(DataSize + RarVM.VM_FIXEDGLOBALSIZE - CurSize);
StackFilter.Program.GlobalData.SetSize(
DataSize + RarVM.VM_FIXEDGLOBALSIZE - CurSize
);
}
var offset = RarVM.VM_FIXEDGLOBALSIZE;
globalData = StackFilter.Program.GlobalData;

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using System;
using System.IO;
@@ -23,7 +23,7 @@ public abstract class ReadOnlyStream : Stream
set => throw new NotSupportedException();
}
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();

View File

@@ -0,0 +1,32 @@
#nullable disable
using System;
using System.Security.Cryptography;
namespace SharpCompress.Crypto;
internal class BlockTransformer : IDisposable
{
private ICryptoTransform _transform;
public BlockTransformer(ICryptoTransform transformer)
{
_transform = transformer;
}
public byte[] ProcessBlock(ReadOnlySpan<byte> cipherText)
{
var decryptedBytes = new byte[cipherText.Length];
var bytes = _transform.TransformBlock(
cipherText.ToArray(),
0,
cipherText.Length,
decryptedBytes,
0
);
return decryptedBytes;
}
public void Dispose() { }
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
namespace SharpCompress.IO;
@@ -26,7 +26,7 @@ internal class BufferedSubStream : NonDisposingStream
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => BytesLeftToRead;

View File

@@ -45,13 +45,13 @@ public class DataDescriptorStream : Stream
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => _stream.Length;
public override long Position
{
get => _stream.Position;
get => _stream.Position - _start;
set => _stream.Position = value;
}

View File

@@ -29,7 +29,7 @@ internal class ReadOnlySubStream : NonDisposingStream
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => throw new NotSupportedException();

View File

@@ -75,7 +75,7 @@ public class RewindableStream : Stream
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => stream.Length;

View File

@@ -72,6 +72,14 @@ public abstract class RarReader : AbstractReader<RarReaderEntry, RarVolume>
{
return CreateEntryStream(new RarCrcStream(UnpackV1.Value, Entry.FileHeader, stream));
}
if (Entry.FileHeader.FileCrc.Length > 5)
{
return CreateEntryStream(
new RarBLAKE2spStream(UnpackV2017.Value, Entry.FileHeader, stream)
);
}
return CreateEntryStream(new RarCrcStream(UnpackV2017.Value, Entry.FileHeader, stream));
}
}

View File

@@ -2,11 +2,11 @@
<PropertyGroup>
<AssemblyTitle>SharpCompress - Pure C# Decompression/Compression</AssemblyTitle>
<NeutralLanguage>en-US</NeutralLanguage>
<VersionPrefix>0.34.2</VersionPrefix>
<AssemblyVersion>0.34.2</AssemblyVersion>
<FileVersion>0.34.2</FileVersion>
<VersionPrefix>0.36.0</VersionPrefix>
<AssemblyVersion>0.36.0</AssemblyVersion>
<FileVersion>0.36.0</FileVersion>
<Authors>Adam Hathcock</Authors>
<TargetFrameworks>net462;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>net462;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0</TargetFrameworks>
<AssemblyName>SharpCompress</AssemblyName>
<AssemblyOriginatorKeyFile>../../SharpCompress.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
@@ -27,18 +27,18 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="ZstdSharp.Port" Version="0.7.2" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="ZstdSharp.Port" Version="0.7.4" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>
<ItemGroup Condition=" '$(VersionlessImplicitFrameworkDefine)' == 'NETFRAMEWORK' ">
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>
</Project>

View File

@@ -10,9 +10,7 @@ public static class WriterFactory
public static IWriter Open(Stream stream, ArchiveType archiveType, WriterOptions writerOptions)
{
var factory = Factories
.Factory
.Factories
.OfType<IWriterFactory>()
.Factory.Factories.OfType<IWriterFactory>()
.FirstOrDefault(item => item.KnownArchiveType == archiveType);
if (factory != null)

View File

@@ -304,7 +304,10 @@ public class ZipWriter : AbstractWriter
// Write normal end of central directory record
OutputStream.Write(stackalloc byte[] { 80, 75, 5, 6, 0, 0, 0, 0 });
BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)entries.Count);
BinaryPrimitives.WriteUInt16LittleEndian(
intBuf,
(ushort)(entries.Count < 0xFFFF ? entries.Count : 0xFFFF)
);
OutputStream.Write(intBuf.Slice(0, 2));
OutputStream.Write(intBuf.Slice(0, 2));
BinaryPrimitives.WriteUInt32LittleEndian(intBuf, sizevalue);

View File

@@ -258,6 +258,29 @@ public class ArchiveTests : ReaderTests
VerifyFiles();
}
protected void ArchiveFileSkip(
string testArchive,
string fileOrder,
ReaderOptions? readerOptions = null
)
{
#if !NETFRAMEWORK
if (!OperatingSystem.IsWindows())
{
fileOrder = fileOrder.Replace('\\', '/');
}
#endif
var expected = new Stack<string>(fileOrder.Split(' '));
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
using (var archive = ArchiveFactory.Open(testArchive, readerOptions))
{
foreach (var entry in archive.Entries)
{
Assert.Equal(expected.Pop(), entry.Key);
}
}
}
/// <summary>
/// Demonstrate the ExtractionOptions.PreserveFileTime and ExtractionOptions.PreserveAttributes extract options
/// </summary>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
namespace SharpCompress.Test.Mocks;
@@ -27,7 +27,7 @@ public class FlushOnDisposeStream : Stream, IDisposable
set => inner.Position = value;
}
public override void Flush() => throw new NotImplementedException();
public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count) =>
inner.Read(buffer, offset, count);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
namespace SharpCompress.Test.Mocks;
@@ -29,7 +29,7 @@ public class ForwardOnlyStream : Stream
public override bool CanSeek => false;
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => throw new NotSupportedException();

View File

@@ -22,11 +22,16 @@ public class RarArchiveTests : ArchiveTests
() => ReadRarPassword("Rar.encrypted_filesAndHeader.rar", null)
);
/*[Fact]
public void Rar5_EncryptedFileAndHeader_Archive()
{
[Fact]
public void Rar5_EncryptedFileAndHeader_Archive() =>
ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test");
}*/
[Fact]
public void Rar5_EncryptedFileAndHeader_Archive_Err() =>
Assert.Throws(
typeof(CryptographicException),
() => ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "failed")
);
[Fact]
public void Rar5_EncryptedFileAndHeader_NoPasswordExceptionTest() =>
@@ -39,20 +44,23 @@ public class RarArchiveTests : ArchiveTests
public void Rar_EncryptedFileOnly_Archive() =>
ReadRarPassword("Rar.encrypted_filesOnly.rar", "test");
/*[Fact]
public void Rar5_EncryptedFileOnly_Archive()
{
[Fact]
public void Rar_EncryptedFileOnly_Archive_Err() =>
Assert.Throws(
typeof(CryptographicException),
() => ReadRarPassword("Rar5.encrypted_filesOnly.rar", "failed")
);
[Fact]
public void Rar5_EncryptedFileOnly_Archive() =>
ReadRarPassword("Rar5.encrypted_filesOnly.rar", "test");
}*/
[Fact]
public void Rar_Encrypted_Archive() => ReadRarPassword("Rar.Encrypted.rar", "test");
/*[Fact]
public void Rar5_Encrypted_Archive()
{
[Fact]
public void Rar5_Encrypted_Archive() =>
ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test");
}*/
private void ReadRarPassword(string testArchive, string? password)
{
@@ -600,4 +608,37 @@ public class RarArchiveTests : ArchiveTests
Assert.True(archive.IsMultipartVolume());
Assert.False(archive.IsFirstVolume());
}
[Fact]
public void Rar5_CRC_Blake2_Archive() => ArchiveFileRead("Rar5.crc_blake2.rar");
[Fact]
void Rar_Iterate_Archive() =>
ArchiveFileSkip("Rar.rar", "Failure jpg exe Empty jpg\\test.jpg exe\\test.exe тест.txt");
[Fact]
public void Rar2_Iterate_Archive() =>
ArchiveFileSkip("Rar2.rar", "Failure Empty тест.txt jpg\\test.jpg exe\\test.exe jpg exe");
[Fact]
public void Rar4_Iterate_Archive() =>
ArchiveFileSkip("Rar4.rar", "Failure Empty jpg exe тест.txt jpg\\test.jpg exe\\test.exe");
[Fact]
public void Rar5_Iterate_Archive() =>
ArchiveFileSkip("Rar5.rar", "Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe");
[Fact]
public void Rar_Encrypted_Iterate_Archive() =>
ArchiveFileSkip(
"Rar.encrypted_filesOnly.rar",
"Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe"
);
[Fact]
public void Rar5_Encrypted_Iterate_Archive() =>
ArchiveFileSkip(
"Rar5.encrypted_filesOnly.rar",
"Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe"
);
}

View File

@@ -171,33 +171,28 @@ public class RarReaderTests : ReaderTests
[Fact]
public void Rar5_Reader() => Read("Rar5.rar", CompressionType.Rar);
[Fact]
public void Rar5_CRC_Blake2_Reader() => Read("Rar5.crc_blake2.rar", CompressionType.Rar);
[Fact]
public void Rar_EncryptedFileAndHeader_Reader() =>
ReadRar("Rar.encrypted_filesAndHeader.rar", "test");
/*[Fact]
public void Rar5_EncryptedFileAndHeader_Reader()
{
[Fact]
public void Rar5_EncryptedFileAndHeader_Reader() =>
ReadRar("Rar5.encrypted_filesAndHeader.rar", "test");
}*/
[Fact]
public void Rar_EncryptedFileOnly_Reader() => ReadRar("Rar.encrypted_filesOnly.rar", "test");
/*[Fact]
public void Rar5_EncryptedFileOnly_Reader()
{
ReadRar("Rar5.encrypted_filesOnly.rar", "test");
}*/
[Fact]
public void Rar5_EncryptedFileOnly_Reader() => ReadRar("Rar5.encrypted_filesOnly.rar", "test");
[Fact]
public void Rar_Encrypted_Reader() => ReadRar("Rar.Encrypted.rar", "test");
/*[Fact]
public void Rar5_Encrypted_Reader()
{
ReadRar("Rar5.encrypted_filesOnly.rar", "test");
}*/
[Fact]
public void Rar5_Encrypted_Reader() => ReadRar("Rar5.encrypted_filesOnly.rar", "test");
private void ReadRar(string testArchive, string password) =>
Read(testArchive, CompressionType.Rar, new ReaderOptions { Password = password });
@@ -284,6 +279,12 @@ public class RarReaderTests : ReaderTests
[Fact]
public void Rar_Solid_Reader() => Read("Rar.solid.rar", CompressionType.Rar);
[Fact]
public void Rar_Comment_Reader() => Read("Rar.comment.rar", CompressionType.Rar);
[Fact]
public void Rar5_Comment_Reader() => Read("Rar5.comment.rar", CompressionType.Rar);
[Fact]
public void Rar5_Solid_Reader() => Read("Rar5.solid.rar", CompressionType.Rar);
@@ -377,4 +378,55 @@ public class RarReaderTests : ReaderTests
}
}
}
[Fact]
public void Rar_Iterate_Reader() =>
Iterate(
"Rar.rar",
"Failure jpg exe Empty jpg\\test.jpg exe\\test.exe тест.txt",
CompressionType.Rar
);
[Fact]
public void Rar2_Iterate_Archive() =>
Iterate(
"Rar2.rar",
"Failure Empty тест.txt jpg\\test.jpg exe\\test.exe jpg exe",
CompressionType.Rar
);
[Fact]
public void Rar4_Iterate_Archive() =>
Iterate(
"Rar4.rar",
"Failure Empty jpg exe тест.txt jpg\\test.jpg exe\\test.exe",
CompressionType.Rar
);
[Fact]
public void Rar5_Iterate_Archive() =>
Iterate(
"Rar5.rar",
"Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe",
CompressionType.Rar
);
[Fact]
public void Rar_Encrypted_Iterate_Archive() =>
Iterate(
"Rar.encrypted_filesOnly.rar",
"Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe",
CompressionType.Rar
);
[Fact]
public void Rar5_Encrypted_Iterate_Archive() =>
Assert.Throws<CryptographicException>(
() =>
Iterate(
"Rar5.encrypted_filesOnly.rar",
"Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe",
CompressionType.Rar
)
);
}

View File

@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common;
using SharpCompress.IO;
@@ -67,4 +69,32 @@ public abstract class ReaderTests : TestBase
}
}
}
protected void Iterate(
string testArchive,
string fileOrder,
CompressionType expectedCompression,
ReaderOptions? options = null
)
{
#if !NETFRAMEWORK
if (!OperatingSystem.IsWindows())
{
fileOrder = fileOrder.Replace('\\', '/');
}
#endif
var expected = new Stack<string>(fileOrder.Split(' '));
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
using var file = File.OpenRead(testArchive);
using var forward = new ForwardOnlyStream(file);
using (var reader = ReaderFactory.Open(forward, options))
{
while (reader.MoveToNextEntry())
{
Assert.Equal(expectedCompression, reader.Entry.CompressionType);
Assert.Equal(expected.Pop(), reader.Entry.Key);
}
}
}
}

View File

@@ -135,6 +135,9 @@ public class SevenZipArchiveTests : ArchiveTests
)
);
[Fact]
public void SevenZipArchive_EOS_FileRead() => ArchiveFileRead("7Zip.eos.7z");
[Fact]
public void SevenZipArchive_Delta_FileRead() => ArchiveFileRead("7Zip.delta.7z");

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net462</TargetFrameworks>
<TargetFrameworks>net8.0;net462</TargetFrameworks>
<AssemblyName>SharpCompress.Test</AssemblyName>
<PackageId>SharpCompress.Test</PackageId>
</PropertyGroup>
@@ -8,13 +8,13 @@
<ProjectReference Include="..\..\src\SharpCompress\SharpCompress.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.10.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit" Version="2.6.3" />
<PackageReference Include="Xunit.SkippableFact" Version="1.4.13" />
</ItemGroup>
<ItemGroup Condition=" '$(VersionlessImplicitFrameworkDefine)' != 'NETFRAMEWORK' ">

View File

@@ -198,9 +198,9 @@ public class TarArchiveTests : ArchiveTests
using (var archive = TarArchive.Open(unmodified))
{
var entry = archive
.Entries
.Single(x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase));
var entry = archive.Entries.Single(
x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
);
archive.RemoveEntry(entry);
archive.SaveTo(scratchPath, CompressionType.None);
}

View File

@@ -56,6 +56,9 @@ public class TarReaderTests : ReaderTests
[Fact]
public void Tar_Xz_Reader() => Read("Tar.tar.xz", CompressionType.Xz);
[Fact]
public void Tar_GZip_OldGnu_Reader() => Read("Tar.oldgnu.tar.gz", CompressionType.GZip);
[Fact]
public void Tar_BZip2_Entry_Stream()
{
@@ -206,11 +209,9 @@ public class TarReaderTests : ReaderTests
[Fact]
public void Tar_GZip_With_Symlink_Entries()
{
var isWindows = System
.Runtime
.InteropServices
.RuntimeInformation
.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows);
var isWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(
System.Runtime.InteropServices.OSPlatform.Windows
);
using (
Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "TarWithSymlink.tar.gz"))
)
@@ -253,15 +254,12 @@ public class TarReaderTests : ReaderTests
{
// need to convert the link to an absolute path for comparison
var target = reader.Entry.LinkTarget;
var realTarget = System
.IO
.Path
.GetFullPath(
System
.IO
.Path
.Combine($"{System.IO.Path.GetDirectoryName(path)}", target)
);
var realTarget = System.IO.Path.GetFullPath(
System.IO.Path.Combine(
$"{System.IO.Path.GetDirectoryName(path)}",
target
)
);
Assert.Equal(realTarget, link.GetContents().ToString());
}

View File

@@ -20,10 +20,10 @@ public class TestBase : IDisposable
public TestBase()
{
var index = AppDomain
.CurrentDomain
.BaseDirectory
.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase);
var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf(
"SharpCompress.Test",
StringComparison.OrdinalIgnoreCase
);
var path = AppDomain.CurrentDomain.BaseDirectory.Substring(0, index);
SOLUTION_BASE_PATH = Path.GetDirectoryName(path) ?? throw new ArgumentNullException();

View File

@@ -184,9 +184,9 @@ public class ZipArchiveTests : ArchiveTests
using (var archive = ZipArchive.Open(unmodified))
{
var entry = archive
.Entries
.Single(x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase));
var entry = archive.Entries.Single(
x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
);
archive.RemoveEntry(entry);
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate);
@@ -252,9 +252,9 @@ public class ZipArchiveTests : ArchiveTests
)
);
Assert.Null(
((IArchive)vfs)
.Entries
.FirstOrDefault(v => v.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase))
((IArchive)vfs).Entries.FirstOrDefault(
v => v.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
)
);
}
}
@@ -391,14 +391,14 @@ public class ZipArchiveTests : ArchiveTests
{
archive.AddAllFromDirectory(SCRATCH_FILES_PATH);
archive.RemoveEntry(
archive
.Entries
.Single(x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase))
archive.Entries.Single(
x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
)
);
Assert.Null(
archive
.Entries
.FirstOrDefault(x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase))
archive.Entries.FirstOrDefault(
x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
)
);
}
Directory.Delete(SCRATCH_FILES_PATH, true);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.