diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs index d147264e..021381c3 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs @@ -71,22 +71,19 @@ public partial class RarArchiveEntry : RarEntry, IArchiveEntry public Stream OpenEntryStream() { + var readStream = new MultiVolumeReadOnlyStream(Parts.Cast()); RarStream stream; if (IsRarV3) { - stream = new RarStream( - archive.UnpackV1.Value, - FileHeader, - new MultiVolumeReadOnlyStream(Parts.Cast()) - ); + stream = RarCrcStream.Create(archive.UnpackV1.Value, FileHeader, readStream); + } + else if (FileHeader.FileCrc?.Length > 5) + { + stream = RarBLAKE2spStream.Create(archive.UnpackV2017.Value, FileHeader, readStream); } else { - stream = new RarStream( - archive.UnpackV2017.Value, - FileHeader, - new MultiVolumeReadOnlyStream(Parts.Cast()) - ); + stream = RarCrcStream.Create(archive.UnpackV2017.Value, FileHeader, readStream); } stream.Initialize(); diff --git a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs index fb6d23fa..576c1d16 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs @@ -18,6 +18,28 @@ public class SevenZipEntry : Entry public override long Crc => FilePart.Header.Crc ?? 0; + internal override ChecksumDescriptor Checksum + { + get + { + if ( + IsDirectory + || FilePart.Header.IsAnti + || !FilePart.Header.HasStream + || !FilePart.Header.Crc.HasValue + ) + { + return default; + } + + return new ChecksumDescriptor( + ChecksumKind.Crc32, + FilePart.Header.Crc.Value, + IsAvailable: true + ); + } + } + public override string? Key => FilePart.Header.Name; public override string? LinkTarget => null; diff --git a/tests/SharpCompress.Test/Rar/RarCrcExtractionTests.cs b/tests/SharpCompress.Test/Rar/RarCrcExtractionTests.cs new file mode 100644 index 00000000..f2dc8f4f --- /dev/null +++ b/tests/SharpCompress.Test/Rar/RarCrcExtractionTests.cs @@ -0,0 +1,36 @@ +using System.IO; +using System.Linq; +using SharpCompress.Archives; +using SharpCompress.Archives.Rar; +using SharpCompress.Common; +using Xunit; + +namespace SharpCompress.Test.Rar; + +public class RarCrcExtractionTests : ArchiveTests +{ + [Theory] + [InlineData("Rar.rar")] + [InlineData("Rar5.rar")] + public void Rar_Archive_WriteToFile_Throws_On_Crc_Mismatch(string archiveName) + { + using var archive = RarArchive.OpenArchive(Path.Combine(TEST_ARCHIVES_PATH, archiveName)); + var entry = CorruptFirstFileCrc(archive); + var destination = Path.Combine(SCRATCH_FILES_PATH, $"{archiveName}-crc-mismatch.txt"); + + Assert.Throws(() => entry.WriteToFile(destination)); + } + + private static RarArchiveEntry CorruptFirstFileCrc(IArchive archive) + { + var entry = archive.Entries.OfType().First(e => !e.IsDirectory); + CorruptCrc(entry); + return entry; + } + + private static void CorruptCrc(RarArchiveEntry entry) + { + var crc = entry.FileHeader.FileCrc.NotNull(); + crc[0] ^= 0xFF; + } +} diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipCrcExtractionTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipCrcExtractionTests.cs new file mode 100644 index 00000000..3261f16a --- /dev/null +++ b/tests/SharpCompress.Test/SevenZip/SevenZipCrcExtractionTests.cs @@ -0,0 +1,81 @@ +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using SharpCompress.Archives; +using SharpCompress.Archives.SevenZip; +using SharpCompress.Common; +using SharpCompress.Common.SevenZip; +using Xunit; + +namespace SharpCompress.Test.SevenZip; + +public class SevenZipCrcExtractionTests : ArchiveTests +{ + [Fact] + public void SevenZip_Archive_WriteToFile_Throws_On_Crc_Mismatch() + { + using var archive = SevenZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "7Zip.LZMA.7z") + ); + var entry = CorruptFirstFileCrc(archive); + var destination = Path.Combine(SCRATCH_FILES_PATH, "7zip-crc-mismatch.txt"); + + var exception = Assert.Throws(() => entry.WriteToFile(destination)); + + Assert.Contains(entry.Key!, exception.Message); + } + + [Fact] + public void SevenZip_Archive_WriteToFile_Skips_Crc_Mismatch_When_Disabled() + { + using var archive = SevenZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "7Zip.LZMA.7z") + ); + var entry = CorruptFirstFileCrc(archive); + var destination = Path.Combine(SCRATCH_FILES_PATH, "7zip-crc-disabled.txt"); + + entry.WriteToFile(destination, new ExtractionOptions { CheckCrc = false }); + + Assert.True(new FileInfo(destination).Length > 0); + } + + [Fact] + public async Task SevenZip_Archive_WriteToFileAsync_Throws_On_Crc_Mismatch() + { + await using var archive = await SevenZipArchive.OpenAsyncArchive( + Path.Combine(TEST_ARCHIVES_PATH, "7Zip.LZMA.7z") + ); + var entries = await archive.EntriesAsync.ToListAsync(); + var entry = CorruptFirstFileCrc(entries); + var destination = Path.Combine(SCRATCH_FILES_PATH, "7zip-crc-mismatch-async.txt"); + + var exception = await Assert.ThrowsAsync(async () => + await entry.WriteToFileAsync(destination) + ); + + Assert.Contains(entry.Key!, exception.Message); + } + + private static IArchiveEntry CorruptFirstFileCrc(IArchive archive) + { + var entry = archive.Entries.First(e => !e.IsDirectory); + CorruptCrc(entry); + return entry; + } + + private static IArchiveEntry CorruptFirstFileCrc( + System.Collections.Generic.IEnumerable entries + ) + { + var entry = entries.First(e => !e.IsDirectory); + CorruptCrc(entry); + return entry; + } + + private static void CorruptCrc(IArchiveEntry entry) + { + var sevenZipEntry = Assert.IsAssignableFrom(entry); + var crc = sevenZipEntry.FilePart.Header.Crc.NotNull(); + sevenZipEntry.FilePart.Header.Crc = crc ^ 0xFFFFFFFF; + } +}