Add basic crc checking for xz

This commit is contained in:
Adam Hathcock
2026-06-13 09:24:04 +01:00
parent 5de1193aab
commit 4e3dcabbd0
5 changed files with 96 additions and 7 deletions

View File

@@ -52,6 +52,9 @@ public static class Crc32
return createTable;
}
public static uint Update(uint seed, ReadOnlySpan<byte> buffer) =>
CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer);
private static uint CalculateHash(uint[] table, uint seed, ReadOnlySpan<byte> buffer)
{
var crc = seed;

View File

@@ -34,6 +34,7 @@ public sealed partial class XZBlock
bytesRead = await _decomStream
.ReadAsync(buffer, offset, count, cancellationToken)
.ConfigureAwait(false);
UpdateCheck(buffer, offset, bytesRead);
}
if (bytesRead != count)
@@ -74,9 +75,10 @@ public sealed partial class XZBlock
private async ValueTask CheckCrcAsync(CancellationToken cancellationToken = default)
{
var crc = new byte[_checkSize];
await BaseStream.ReadAsync(crc, 0, _checkSize, cancellationToken).ConfigureAwait(false);
// Actually do a check (and read in the bytes
// into the function throughout the stream read).
await BaseStream
.ReadExactAsync(crc, 0, _checkSize, cancellationToken)
.ConfigureAwait(false);
VerifyCheck(crc);
_crcChecked = true;
}

View File

@@ -1,9 +1,11 @@
#nullable disable
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
@@ -19,7 +21,11 @@ public sealed partial class XZBlock : XZReadOnlyStream
public ulong? UncompressedSize { get; private set; }
public Stack<BlockFilter> Filters { get; private set; } = new();
public bool HeaderIsLoaded { get; private set; }
private readonly CheckType _checkType;
private readonly int _checkSize;
private uint _crc32 = Crc32.DefaultSeed;
private ulong _crc64 = Crc64.DefaultSeed;
private readonly SHA256 _sha256;
private bool _streamConnected;
private int _numFilters;
private byte _blockHeaderSizeByte;
@@ -32,7 +38,12 @@ public sealed partial class XZBlock : XZReadOnlyStream
public XZBlock(Stream stream, CheckType checkType, int checkSize)
: base(stream)
{
_checkType = checkType;
_checkSize = checkSize;
if (checkType == CheckType.SHA256)
{
_sha256 = SHA256.Create();
}
_startPosition = stream.Position;
}
@@ -52,6 +63,7 @@ public sealed partial class XZBlock : XZReadOnlyStream
if (!_endOfStream)
{
bytesRead = _decomStream.Read(buffer, offset, count);
UpdateCheck(buffer, offset, bytesRead);
}
if (bytesRead != count)
@@ -90,12 +102,71 @@ public sealed partial class XZBlock : XZReadOnlyStream
private void CheckCrc()
{
var crc = new byte[_checkSize];
BaseStream.Read(crc, 0, _checkSize);
// Actually do a check (and read in the bytes
// into the function throughout the stream read).
BaseStream.ReadExact(crc, 0, _checkSize);
VerifyCheck(crc);
_crcChecked = true;
}
private void UpdateCheck(byte[] buffer, int offset, int count)
{
if (count == 0 || _checkType == CheckType.NONE)
{
return;
}
var bytes = buffer.AsSpan(offset, count);
switch (_checkType)
{
case CheckType.CRC32:
_crc32 = Crc32.Update(_crc32, bytes);
break;
case CheckType.CRC64:
Crc64.Table ??= Crc64.CreateTable(Crc64.Iso3309Polynomial);
_crc64 = Crc64.CalculateHash(_crc64, Crc64.Table, bytes);
break;
case CheckType.SHA256:
_sha256.TransformBlock(buffer, offset, count, null, 0);
break;
}
}
private void VerifyCheck(byte[] expected)
{
var actual = _checkType switch
{
CheckType.NONE => Array.Empty<byte>(),
CheckType.CRC32 => GetLittleEndianBytes(~_crc32),
CheckType.CRC64 => GetLittleEndianBytes(_crc64),
CheckType.SHA256 => FinalizeSha256Check(),
_ => throw new InvalidFormatException("Unsupported XZ check type"),
};
if (!expected.SequenceEqual(actual))
{
throw new InvalidFormatException("Block check corrupt");
}
}
private static byte[] GetLittleEndianBytes(uint value)
{
var bytes = new byte[sizeof(uint)];
BinaryPrimitives.WriteUInt32LittleEndian(bytes, value);
return bytes;
}
private static byte[] GetLittleEndianBytes(ulong value)
{
var bytes = new byte[sizeof(ulong)];
BinaryPrimitives.WriteUInt64LittleEndian(bytes, value);
return bytes;
}
private byte[] FinalizeSha256Check()
{
_sha256.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
return _sha256.Hash;
}
private void ConnectStream()
{
_decomStream = BaseStream;