mirror of
https://github.com/SabreTools/SabreTools.Hashing.git
synced 2026-02-08 13:49:54 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
187932b091 | ||
|
|
89dbe0460f | ||
|
|
365ee9019f | ||
|
|
e117892f37 | ||
|
|
1e11e9abb8 | ||
|
|
1d2985023d | ||
|
|
cfddb3dab4 | ||
|
|
08512abc59 | ||
|
|
71a330cf68 | ||
|
|
ad8d119905 | ||
|
|
c82a6dc39b | ||
|
|
414759cbd2 | ||
|
|
142ca6f327 | ||
|
|
8dee2e2501 | ||
|
|
240098dd03 | ||
|
|
15a022eca5 | ||
|
|
0ede92a5d9 | ||
|
|
0c3815e17c | ||
|
|
8f5bff0375 | ||
|
|
dc3cb0be5d | ||
|
|
f7346b20e1 | ||
|
|
f971fcf5c8 | ||
|
|
4e0da77cb4 | ||
|
|
7776112ec6 | ||
|
|
c65184689d | ||
|
|
704e08b5ed | ||
|
|
99f770ce81 | ||
|
|
e5fea69815 | ||
|
|
80448302e8 | ||
|
|
434a10d3db | ||
|
|
4ea5f95b5e | ||
|
|
381dffccf9 | ||
|
|
d4885d389d |
13
.github/workflows/build_and_test.yml
vendored
13
.github/workflows/build_and_test.yml
vendored
@@ -16,20 +16,17 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
dotnet-version: |
|
||||
6.0.x
|
||||
8.0.x
|
||||
9.0.x
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
|
||||
- name: Run publish script
|
||||
run: ./publish-nix.sh
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'Nuget Package'
|
||||
path: "*.nupkg,*.snupkg"
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
|
||||
5
.github/workflows/check_pr.yml
vendored
5
.github/workflows/check_pr.yml
vendored
@@ -11,7 +11,10 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: |
|
||||
6.0.x
|
||||
8.0.x
|
||||
9.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
|
||||
@@ -24,6 +24,7 @@ All hash and checksum types here have been written to ensure compatibility acros
|
||||
| FNV | 32-, and 64-bit variants; 0, 1, and 1a algorithms |
|
||||
| Message Digest | MD2 and MD4 only |
|
||||
| RIPEMD | 128-, 160-, 256-, and 320-bit variants |
|
||||
| SpamSum | Based on the [SSDEEP source code](https://github.com/ssdeep-project/ssdeep/blob/master/fuzzy.c) |
|
||||
| Tiger | 128-, 160-, and 192-bit variants; 3- and 4-pass; `0x01` and `0x80` (Tiger2) pad-initialized |
|
||||
| xxHash | xxHash-32 and xxHash-64 only |
|
||||
|
||||
@@ -33,7 +34,6 @@ External implementations of hash and checksum types may not be compatible with a
|
||||
|
||||
| Source | Hash / Checksum Types | Notes |
|
||||
| --- | --- | --- |
|
||||
| [Aaru.Checksums](https://github.com/aaru-dps/Aaru.Checksums) | SpamSum | Some code tweaks made to support older .NET versions |
|
||||
| [Blake3.NET](https://github.com/xoofx/Blake3.NET) | BLAKE3 | Used in `net7.0` and above |
|
||||
| [System.IO.Hashing](https://www.nuget.org/packages/System.IO.Hashing) | XXH3, XXH128 | Used in `net462` and above |
|
||||
| [System.Security.Cryptography](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography) | MD5, SHA-1, SHA-256, SHA-384, SHA-512, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256 | Built-in library; SHA3-256, SHA3-384, SHA3-512, SHAKE128, and SHAKE256 are `net8.0` and above only for [supported platforms](https://learn.microsoft.com/en-us/dotnet/standard/security/cross-platform-cryptography) |
|
||||
|
||||
@@ -29,7 +29,8 @@ namespace SabreTools.Hashing.Test
|
||||
public void GetSingleGzipStreamHashesTest()
|
||||
{
|
||||
var gzipStream = new GZipStream(File.OpenRead(_singleGzipFilePath), CompressionMode.Decompress);
|
||||
var hashDict = HashTool.GetStreamHashes(gzipStream);
|
||||
var hashDict = HashTool.GetStreamHashesAndSize(gzipStream, out long actualSize);
|
||||
TestHelper.ValidateSize(actualSize);
|
||||
TestHelper.ValidateHashes(hashDict);
|
||||
}
|
||||
|
||||
@@ -38,7 +39,8 @@ namespace SabreTools.Hashing.Test
|
||||
{
|
||||
var zipFile = ZipFile.OpenRead(_singleZipFilePath);
|
||||
var fileStream = zipFile.Entries[0].Open();
|
||||
var hashDict = HashTool.GetStreamHashes(fileStream);
|
||||
var hashDict = HashTool.GetStreamHashesAndSize(fileStream, out long actualSize);
|
||||
TestHelper.ValidateSize(actualSize);
|
||||
TestHelper.ValidateHashes(hashDict);
|
||||
}
|
||||
|
||||
@@ -50,7 +52,8 @@ namespace SabreTools.Hashing.Test
|
||||
for (int i = 0; i < zipFile.Entries.Count; i++)
|
||||
{
|
||||
var fileStream = zipFile.Entries[i].Open();
|
||||
var hashDict = HashTool.GetStreamHashes(fileStream);
|
||||
var hashDict = HashTool.GetStreamHashesAndSize(fileStream, out long actualSize);
|
||||
TestHelper.ValidateSize(actualSize);
|
||||
TestHelper.ValidateHashes(hashDict);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace SabreTools.Hashing.Test
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetStandardHashesTest()
|
||||
public void GetStandardHashesFileTest()
|
||||
{
|
||||
bool gotHashes = HashTool.GetStandardHashes(_hashFilePath, out long actualSize, out string? crc32, out string? md5, out string? sha1);
|
||||
|
||||
@@ -43,6 +43,32 @@ namespace SabreTools.Hashing.Test
|
||||
TestHelper.ValidateHash(HashType.SHA1, sha1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetStandardHashesArrayTest()
|
||||
{
|
||||
byte[] fileBytes = File.ReadAllBytes(_hashFilePath);
|
||||
bool gotHashes = HashTool.GetStandardHashes(fileBytes, out long actualSize, out string? crc32, out string? md5, out string? sha1);
|
||||
|
||||
Assert.True(gotHashes);
|
||||
TestHelper.ValidateSize(actualSize);
|
||||
TestHelper.ValidateHash(HashType.CRC32, crc32);
|
||||
TestHelper.ValidateHash(HashType.MD5, md5);
|
||||
TestHelper.ValidateHash(HashType.SHA1, sha1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetStandardHashesStreamTest()
|
||||
{
|
||||
var fileStream = File.OpenRead(_hashFilePath);
|
||||
bool gotHashes = HashTool.GetStandardHashes(fileStream, out long actualSize, out string? crc32, out string? md5, out string? sha1);
|
||||
|
||||
Assert.True(gotHashes);
|
||||
TestHelper.ValidateSize(actualSize);
|
||||
TestHelper.ValidateHash(HashType.CRC32, crc32);
|
||||
TestHelper.ValidateHash(HashType.MD5, md5);
|
||||
TestHelper.ValidateHash(HashType.SHA1, sha1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFileHashesParallelTest()
|
||||
{
|
||||
@@ -74,6 +100,15 @@ namespace SabreTools.Hashing.Test
|
||||
TestHelper.ValidateHashes(hashDict);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetByteArrayHashesAndSizeTest()
|
||||
{
|
||||
byte[] fileBytes = File.ReadAllBytes(_hashFilePath);
|
||||
var hashDict = HashTool.GetByteArrayHashesAndSize(fileBytes, out long actualSize);
|
||||
TestHelper.ValidateSize(actualSize);
|
||||
TestHelper.ValidateHashes(hashDict);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetStreamHashesTest()
|
||||
{
|
||||
@@ -81,5 +116,14 @@ namespace SabreTools.Hashing.Test
|
||||
var hashDict = HashTool.GetStreamHashes(fileStream);
|
||||
TestHelper.ValidateHashes(hashDict);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetStreamHashesAndSizeTest()
|
||||
{
|
||||
var fileStream = File.OpenRead(_hashFilePath);
|
||||
var hashDict = HashTool.GetStreamHashesAndSize(fileStream, out long actualSize);
|
||||
TestHelper.ValidateSize(actualSize);
|
||||
TestHelper.ValidateHashes(hashDict);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,579 +0,0 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : SpamSumContext.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Checksums.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Implements the SpamSum fuzzy hashing algorithm.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 2.1 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2023 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
// Based on ssdeep
|
||||
// Copyright (C) 2002 Andrew Tridgell <tridge@samba.org>
|
||||
// Copyright (C) 2006 ManTech International Corporation
|
||||
// Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de>
|
||||
//
|
||||
// Earlier versions of this code were named fuzzy.c and can be found at:
|
||||
// http://www.samba.org/ftp/unpacked/junkcode/spamsum/
|
||||
// http://ssdeep.sf.net/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
|
||||
namespace Aaru.Checksums;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Implements the SpamSum fuzzy hashing algorithm.</summary>
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||
[SuppressMessage("ReSharper", "OutParameterValueIsAlwaysDiscarded.Global")]
|
||||
internal sealed class SpamSumContext : IChecksum
|
||||
{
|
||||
const uint ROLLING_WINDOW = 7;
|
||||
const uint MIN_BLOCKSIZE = 3;
|
||||
const uint HASH_PRIME = 0x01000193;
|
||||
const uint HASH_INIT = 0x28021967;
|
||||
const uint NUM_BLOCKHASHES = 31;
|
||||
const uint SPAMSUM_LENGTH = 64;
|
||||
const uint FUZZY_MAX_RESULT = 2 * SPAMSUM_LENGTH + 20;
|
||||
|
||||
//"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
#if NET20 || NET35 || NET40 || NET452
|
||||
readonly byte[] _b64 = Encoding.UTF8.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
|
||||
#else
|
||||
readonly byte[] _b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"u8.ToArray();
|
||||
#endif
|
||||
FuzzyState _self;
|
||||
|
||||
/// <summary>Initializes the SpamSum structures</summary>
|
||||
public SpamSumContext()
|
||||
{
|
||||
_self = new FuzzyState
|
||||
{
|
||||
Bh = new BlockhashContext[NUM_BLOCKHASHES]
|
||||
};
|
||||
|
||||
for (var i = 0; i < NUM_BLOCKHASHES; i++)
|
||||
_self.Bh[i].Digest = new byte[SPAMSUM_LENGTH];
|
||||
|
||||
_self.Bhstart = 0;
|
||||
_self.Bhend = 1;
|
||||
_self.Bh[0].H = HASH_INIT;
|
||||
_self.Bh[0].Halfh = HASH_INIT;
|
||||
_self.Bh[0].Digest[0] = 0;
|
||||
_self.Bh[0].Halfdigest = 0;
|
||||
_self.Bh[0].Dlen = 0;
|
||||
_self.TotalSize = 0;
|
||||
roll_init();
|
||||
}
|
||||
|
||||
#region IChecksum Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "SpamSum";
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id => new("DA692981-3291-47D8-B8B9-A87F0605F6E9");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Author => "Natalia Portillo";
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
public void Update(byte[] data, uint len)
|
||||
{
|
||||
_self.TotalSize += len;
|
||||
|
||||
for (var i = 0; i < len; i++)
|
||||
fuzzy_engine_step(data[i]);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
public void Update(byte[] data) => Update(data, (uint)data.Length);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
public byte[] Final()
|
||||
{
|
||||
FuzzyDigest(out byte[] result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Returns a base64 representation of the hash value.</summary>
|
||||
public string End()
|
||||
{
|
||||
FuzzyDigest(out byte[] result);
|
||||
|
||||
return CToString(result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
void roll_init() => _self.Roll = new RollState
|
||||
{
|
||||
Window = new byte[ROLLING_WINDOW]
|
||||
};
|
||||
|
||||
/*
|
||||
* a rolling hash, based on the Adler checksum. By using a rolling hash
|
||||
* we can perform auto resynchronisation after inserts/deletes
|
||||
|
||||
* internally, h1 is the sum of the bytes in the window and h2
|
||||
* is the sum of the bytes times the index
|
||||
|
||||
* h3 is a shift/xor based rolling hash, and is mostly needed to ensure that
|
||||
* we can cope with large blocksize values
|
||||
*/
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
void roll_hash(byte c)
|
||||
{
|
||||
_self.Roll.H2 -= _self.Roll.H1;
|
||||
_self.Roll.H2 += ROLLING_WINDOW * c;
|
||||
|
||||
_self.Roll.H1 += c;
|
||||
_self.Roll.H1 -= _self.Roll.Window[_self.Roll.N % ROLLING_WINDOW];
|
||||
|
||||
_self.Roll.Window[_self.Roll.N % ROLLING_WINDOW] = c;
|
||||
_self.Roll.N++;
|
||||
|
||||
/* The original spamsum AND'ed this value with 0xFFFFFFFF which
|
||||
* in theory should have no effect. This AND has been removed
|
||||
* for performance (jk) */
|
||||
_self.Roll.H3 <<= 5;
|
||||
_self.Roll.H3 ^= c;
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
uint roll_sum() => _self.Roll.H1 + _self.Roll.H2 + _self.Roll.H3;
|
||||
|
||||
/* A simple non-rolling hash, based on the FNV hash. */
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
static uint sum_hash(byte c, uint h) => h * HASH_PRIME ^ c;
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
static uint SSDEEP_BS(uint index) => MIN_BLOCKSIZE << (int)index;
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
void fuzzy_try_fork_blockhash()
|
||||
{
|
||||
switch (_self.Bhend)
|
||||
{
|
||||
case >= NUM_BLOCKHASHES:
|
||||
return;
|
||||
|
||||
// assert
|
||||
case 0:
|
||||
throw new Exception("Assertion failed");
|
||||
}
|
||||
|
||||
uint obh = _self.Bhend - 1;
|
||||
uint nbh = _self.Bhend;
|
||||
_self.Bh[nbh].H = _self.Bh[obh].H;
|
||||
_self.Bh[nbh].Halfh = _self.Bh[obh].Halfh;
|
||||
_self.Bh[nbh].Digest[0] = 0;
|
||||
_self.Bh[nbh].Halfdigest = 0;
|
||||
_self.Bh[nbh].Dlen = 0;
|
||||
++_self.Bhend;
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
void fuzzy_try_reduce_blockhash()
|
||||
{
|
||||
if (_self.Bhstart >= _self.Bhend)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
if (_self.Bhend - _self.Bhstart < 2)
|
||||
/* Need at least two working hashes. */
|
||||
return;
|
||||
|
||||
if ((ulong)SSDEEP_BS(_self.Bhstart) * SPAMSUM_LENGTH >= _self.TotalSize)
|
||||
/* Initial blocksize estimate would select this or a smaller
|
||||
* blocksize. */
|
||||
return;
|
||||
|
||||
if (_self.Bh[_self.Bhstart + 1].Dlen < SPAMSUM_LENGTH / 2)
|
||||
/* Estimate adjustment would select this blocksize. */
|
||||
return;
|
||||
|
||||
/* At this point we are clearly no longer interested in the
|
||||
* start_blocksize. Get rid of it. */
|
||||
++_self.Bhstart;
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
void fuzzy_engine_step(byte c)
|
||||
{
|
||||
uint i;
|
||||
/* At each character we update the rolling hash and the normal hashes.
|
||||
* When the rolling hash hits a reset value then we emit a normal hash
|
||||
* as a element of the signature and reset the normal hash. */
|
||||
roll_hash(c);
|
||||
ulong h = roll_sum();
|
||||
|
||||
for (i = _self.Bhstart; i < _self.Bhend; ++i)
|
||||
{
|
||||
_self.Bh[i].H = sum_hash(c, _self.Bh[i].H);
|
||||
_self.Bh[i].Halfh = sum_hash(c, _self.Bh[i].Halfh);
|
||||
}
|
||||
|
||||
for (i = _self.Bhstart; i < _self.Bhend; ++i)
|
||||
{
|
||||
/* With growing blocksize almost no runs fail the next test. */
|
||||
if (h % SSDEEP_BS(i) != SSDEEP_BS(i) - 1)
|
||||
/* Once this condition is false for one bs, it is
|
||||
* automatically false for all further bs. I.e. if
|
||||
* h === -1 (mod 2*bs) then h === -1 (mod bs). */
|
||||
break;
|
||||
|
||||
/* We have hit a reset point. We now emit hashes which are
|
||||
* based on all characters in the piece of the message between
|
||||
* the last reset point and this one */
|
||||
if (0 == _self.Bh[i].Dlen)
|
||||
fuzzy_try_fork_blockhash();
|
||||
|
||||
_self.Bh[i].Digest[_self.Bh[i].Dlen] = _b64[_self.Bh[i].H % 64];
|
||||
_self.Bh[i].Halfdigest = _b64[_self.Bh[i].Halfh % 64];
|
||||
|
||||
if (_self.Bh[i].Dlen < SPAMSUM_LENGTH - 1)
|
||||
{
|
||||
/* We can have a problem with the tail overflowing. The
|
||||
* easiest way to cope with this is to only reset the
|
||||
* normal hash if we have room for more characters in
|
||||
* our signature. This has the effect of combining the
|
||||
* last few pieces of the message into a single piece
|
||||
* */
|
||||
_self.Bh[i].Digest[++_self.Bh[i].Dlen] = 0;
|
||||
_self.Bh[i].H = HASH_INIT;
|
||||
|
||||
if (_self.Bh[i].Dlen >= SPAMSUM_LENGTH / 2)
|
||||
continue;
|
||||
|
||||
_self.Bh[i].Halfh = HASH_INIT;
|
||||
_self.Bh[i].Halfdigest = 0;
|
||||
}
|
||||
else
|
||||
fuzzy_try_reduce_blockhash();
|
||||
}
|
||||
}
|
||||
|
||||
// CLAUNIA: Flags seems to never be used in ssdeep, so I just removed it for code simplicity
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
void FuzzyDigest(out byte[] result)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
uint bi = _self.Bhstart;
|
||||
uint h = roll_sum();
|
||||
var remain = (int)(FUZZY_MAX_RESULT - 1); /* Exclude terminating '\0'. */
|
||||
result = new byte[FUZZY_MAX_RESULT];
|
||||
|
||||
/* Verify that our elimination was not overeager. */
|
||||
if (!(bi == 0 || (ulong)SSDEEP_BS(bi) / 2 * SPAMSUM_LENGTH < _self.TotalSize))
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
/* Initial blocksize guess. */
|
||||
while ((ulong)SSDEEP_BS(bi) * SPAMSUM_LENGTH < _self.TotalSize)
|
||||
{
|
||||
++bi;
|
||||
|
||||
if (bi >= NUM_BLOCKHASHES)
|
||||
throw new OverflowException("The input exceeds data types");
|
||||
}
|
||||
|
||||
/* Adapt blocksize guess to actual digest length. */
|
||||
while (bi >= _self.Bhend)
|
||||
--bi;
|
||||
|
||||
while (bi > _self.Bhstart && _self.Bh[bi].Dlen < SPAMSUM_LENGTH / 2)
|
||||
--bi;
|
||||
|
||||
if (bi > 0 && _self.Bh[bi].Dlen < SPAMSUM_LENGTH / 2)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
sb.Append($"{SSDEEP_BS(bi)}:");
|
||||
int i = Encoding.ASCII.GetBytes(sb.ToString()).Length;
|
||||
|
||||
if (i <= 0)
|
||||
/* Maybe snprintf has set errno here? */
|
||||
throw new OverflowException("The input exceeds data types");
|
||||
|
||||
if (i >= remain)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
remain -= i;
|
||||
|
||||
Array.Copy(Encoding.ASCII.GetBytes(sb.ToString()), 0, result, 0, i);
|
||||
|
||||
int resultOff = i;
|
||||
|
||||
i = (int)_self.Bh[bi].Dlen;
|
||||
|
||||
if (i > remain)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
Array.Copy(_self.Bh[bi].Digest, 0, result, resultOff, i);
|
||||
resultOff += i;
|
||||
remain -= i;
|
||||
|
||||
if (h != 0)
|
||||
{
|
||||
if (remain <= 0)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
result[resultOff] = _b64[_self.Bh[bi].H % 64];
|
||||
|
||||
if (i < 3 ||
|
||||
result[resultOff] != result[resultOff - 1] ||
|
||||
result[resultOff] != result[resultOff - 2] ||
|
||||
result[resultOff] != result[resultOff - 3])
|
||||
{
|
||||
++resultOff;
|
||||
--remain;
|
||||
}
|
||||
}
|
||||
else if (_self.Bh[bi].Digest[i] != 0)
|
||||
{
|
||||
if (remain <= 0)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
result[resultOff] = _self.Bh[bi].Digest[i];
|
||||
|
||||
if (i < 3 ||
|
||||
result[resultOff] != result[resultOff - 1] ||
|
||||
result[resultOff] != result[resultOff - 2] ||
|
||||
result[resultOff] != result[resultOff - 3])
|
||||
{
|
||||
++resultOff;
|
||||
--remain;
|
||||
}
|
||||
}
|
||||
|
||||
if (remain <= 0)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
result[resultOff++] = 0x3A; // ':'
|
||||
--remain;
|
||||
|
||||
if (bi < _self.Bhend - 1)
|
||||
{
|
||||
++bi;
|
||||
i = (int)_self.Bh[bi].Dlen;
|
||||
|
||||
if (i > remain)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
Array.Copy(_self.Bh[bi].Digest, 0, result, resultOff, i);
|
||||
resultOff += i;
|
||||
remain -= i;
|
||||
|
||||
if (h != 0)
|
||||
{
|
||||
if (remain <= 0)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
h = _self.Bh[bi].Halfh;
|
||||
result[resultOff] = _b64[h % 64];
|
||||
|
||||
if (i < 3 ||
|
||||
result[resultOff] != result[resultOff - 1] ||
|
||||
result[resultOff] != result[resultOff - 2] ||
|
||||
result[resultOff] != result[resultOff - 3])
|
||||
{
|
||||
++resultOff;
|
||||
--remain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i = _self.Bh[bi].Halfdigest;
|
||||
|
||||
if (i != 0)
|
||||
{
|
||||
if (remain <= 0)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
result[resultOff] = (byte)i;
|
||||
|
||||
if (i < 3 ||
|
||||
result[resultOff] != result[resultOff - 1] ||
|
||||
result[resultOff] != result[resultOff - 2] ||
|
||||
result[resultOff] != result[resultOff - 3])
|
||||
{
|
||||
++resultOff;
|
||||
--remain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (h != 0)
|
||||
{
|
||||
if (_self.Bh[bi].Dlen != 0)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
if (remain <= 0)
|
||||
throw new Exception("Assertion failed");
|
||||
|
||||
result[resultOff++] = _b64[_self.Bh[bi].H % 64];
|
||||
/* No need to bother with FUZZY_FLAG_ELIMSEQ, because this
|
||||
* digest has length 1. */
|
||||
--remain;
|
||||
}
|
||||
|
||||
result[resultOff] = 0;
|
||||
}
|
||||
|
||||
/// <summary>Gets the hash of a file</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
public static byte[] File(string filename) =>
|
||||
throw new NotImplementedException("SpamSum does not have a binary representation.");
|
||||
|
||||
/// <summary>Gets the hash of a file in hexadecimal and as a byte array.</summary>
|
||||
/// <param name="filename">File path.</param>
|
||||
/// <param name="hash">Byte array of the hash value.</param>
|
||||
public static string File(string filename, out byte[] hash) =>
|
||||
throw new NotImplementedException("Not yet implemented.");
|
||||
|
||||
/// <summary>Gets the hash of the specified data buffer.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of the data buffer to hash.</param>
|
||||
/// <param name="hash">null</param>
|
||||
/// <returns>Base64 representation of SpamSum $blocksize:$hash:$hash</returns>
|
||||
public static string Data(byte[] data, uint len, out byte[]? hash)
|
||||
{
|
||||
var fuzzyContext = new SpamSumContext();
|
||||
|
||||
fuzzyContext.Update(data, len);
|
||||
|
||||
hash = null;
|
||||
|
||||
return fuzzyContext.End();
|
||||
}
|
||||
|
||||
/// <summary>Gets the hash of the specified data buffer.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="hash">null</param>
|
||||
/// <returns>Base64 representation of SpamSum $blocksize:$hash:$hash</returns>
|
||||
public static string Data(byte[] data, out byte[]? hash) => Data(data, (uint)data.Length, out hash);
|
||||
|
||||
// Converts an ASCII null-terminated string to .NET string
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
static string CToString(byte[] cString)
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
// ReSharper disable once LoopCanBeConvertedToQuery
|
||||
// LINQ is six times slower
|
||||
foreach (byte c in cString)
|
||||
{
|
||||
if (c == 0)
|
||||
break;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return Encoding.ASCII.GetString(cString, 0, count);
|
||||
}
|
||||
|
||||
#region Nested type: BlockhashContext
|
||||
|
||||
/* A blockhash contains a signature state for a specific (implicit) blocksize.
|
||||
* The blocksize is given by SSDEEP_BS(index). The h and halfh members are the
|
||||
* FNV hashes, where halfh stops to be reset after digest is SPAMSUM_LENGTH/2
|
||||
* long. The halfh hash is needed be able to truncate digest for the second
|
||||
* output hash to stay compatible with ssdeep output. */
|
||||
struct BlockhashContext
|
||||
{
|
||||
public uint H;
|
||||
public uint Halfh;
|
||||
public byte[] Digest;
|
||||
|
||||
// SPAMSUM_LENGTH
|
||||
public byte Halfdigest;
|
||||
public uint Dlen;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested type: FuzzyState
|
||||
|
||||
struct FuzzyState
|
||||
{
|
||||
public uint Bhstart;
|
||||
public uint Bhend;
|
||||
public BlockhashContext[] Bh;
|
||||
|
||||
//NUM_BLOCKHASHES
|
||||
public ulong TotalSize;
|
||||
public RollState Roll;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested type: RollState
|
||||
|
||||
struct RollState
|
||||
{
|
||||
public byte[] Window;
|
||||
|
||||
// ROLLING_WINDOW
|
||||
public uint H1;
|
||||
public uint H2;
|
||||
public uint H3;
|
||||
public uint N;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : IChecksum.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Checksums.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Provides an interface for implementing checksums and hashes.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2023 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace Aaru.CommonTypes.Interfaces;
|
||||
|
||||
/// <summary>Defines the interface to implement a checksum or hashing algorithm</summary>
|
||||
internal interface IChecksum
|
||||
{
|
||||
/// <summary>Plugin author</summary>
|
||||
string Author { get; }
|
||||
|
||||
/// <summary>Plugin name.</summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>Plugin UUID.</summary>
|
||||
Guid Id { get; }
|
||||
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="len">Length of buffer to hash.</param>
|
||||
void Update(byte[] data, uint len);
|
||||
|
||||
/// <summary>Updates the hash with data.</summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
void Update(byte[] data);
|
||||
|
||||
/// <summary>Returns a byte array of the hash value.</summary>
|
||||
byte[] Final();
|
||||
|
||||
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
|
||||
string End();
|
||||
}
|
||||
@@ -6,21 +6,24 @@ namespace SabreTools.Hashing.Checksum
|
||||
/// <see href="https://github.com/madler/zlib/blob/v1.2.11/adler32.c"/>
|
||||
public class Adler32 : ChecksumBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 32;
|
||||
|
||||
public Adler32()
|
||||
{
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the internal hashing state
|
||||
/// </summary>
|
||||
public override void Reset()
|
||||
public override void Initialize()
|
||||
{
|
||||
_hash = 1;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Split Adler-32 into component sums
|
||||
uint sum2 = (_hash >> 16) & 0xffff;
|
||||
@@ -131,9 +134,11 @@ namespace SabreTools.Hashing.Checksum
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] Finalize()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
return BitConverter.GetBytes(_hash);
|
||||
byte[] hashArr = BitConverter.GetBytes(_hash);
|
||||
Array.Reverse(hashArr);
|
||||
return hashArr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,20 +5,9 @@ namespace SabreTools.Hashing.Checksum
|
||||
/// <summary>
|
||||
/// Common base class for Fletcher checksums
|
||||
/// </summary>
|
||||
public abstract class ChecksumBase
|
||||
public abstract class ChecksumBase : System.Security.Cryptography.HashAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Hash a block of data and append it to the existing hash
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the data</param>
|
||||
/// <param name="offset">Offset in the byte array to include</param>
|
||||
/// <param name="length">Length of the data to hash</param>
|
||||
public abstract void TransformBlock(byte[] data, int offset, int length);
|
||||
|
||||
/// <summary>
|
||||
/// Finalize the hash and return as a byte array
|
||||
/// </summary>
|
||||
public abstract byte[] Finalize();
|
||||
// No common, untyped functionality
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,18 +20,16 @@ namespace SabreTools.Hashing.Checksum
|
||||
/// </summary>
|
||||
protected T _hash;
|
||||
|
||||
/// <summary>
|
||||
/// Reset the internal hashing state
|
||||
/// </summary>
|
||||
public virtual void Reset()
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
_hash = default;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] Finalize()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
return _hash switch
|
||||
byte[] hashArr = _hash switch
|
||||
{
|
||||
short s => BitConverter.GetBytes(s),
|
||||
ushort s => BitConverter.GetBytes(s),
|
||||
@@ -55,6 +42,9 @@ namespace SabreTools.Hashing.Checksum
|
||||
|
||||
_ => [],
|
||||
};
|
||||
|
||||
Array.Reverse(hashArr);
|
||||
return hashArr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ namespace SabreTools.Hashing.Checksum
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
#region Adler-32 / Fletcher-32
|
||||
#region Adler-32
|
||||
|
||||
/// <summary>
|
||||
/// Largest prime smaller than 65536
|
||||
@@ -14,6 +14,10 @@ namespace SabreTools.Hashing.Checksum
|
||||
/// </summary>
|
||||
public const ushort A32NMAX = 5552;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fletcher-32
|
||||
|
||||
/// <summary>
|
||||
/// Max value for a single half of a Fletcher-32 checksum
|
||||
/// </summary>
|
||||
@@ -25,15 +29,5 @@ namespace SabreTools.Hashing.Checksum
|
||||
public const uint F64BASE = 0xffffffff;
|
||||
|
||||
#endregion
|
||||
|
||||
#region FNV
|
||||
|
||||
public const uint FNV32Basis = 0x811c9dc5;
|
||||
public const ulong FNV64Basis = 0xcbf29ce484222325;
|
||||
|
||||
public const uint FNV32Prime = 0x01000193;
|
||||
public const ulong FNV64Prime = 0x00000100000001b3;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,9 @@ namespace SabreTools.Hashing.Checksum
|
||||
{
|
||||
public class Crc : ChecksumBase<ulong>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => Def.Width;
|
||||
|
||||
/// <summary>
|
||||
/// Definition used to create the runner
|
||||
/// </summary>
|
||||
@@ -27,17 +30,17 @@ namespace SabreTools.Hashing.Checksum
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Reset()
|
||||
public override void Initialize()
|
||||
{
|
||||
_hash = Def.Init;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
=> _table.TransformBlock(ref _hash, data, offset, length);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] Finalize()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
// Create a copy of the hash
|
||||
ulong localHash = _hash;
|
||||
|
||||
@@ -3,17 +3,20 @@ namespace SabreTools.Hashing.Checksum
|
||||
/// <see href="https://en.wikipedia.org/wiki/Fletcher%27s_checksum#Optimizations"/>
|
||||
public class Fletcher16 : ChecksumBase<ushort>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 16;
|
||||
|
||||
public Fletcher16()
|
||||
{
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Split the existing hash
|
||||
uint c0 = (uint)(_hash & 0x00FF);
|
||||
uint c1 = (uint)(_hash << 16);
|
||||
uint c0 = (uint)(_hash & 0x00ff);
|
||||
uint c1 = (uint)((_hash >> 8) & 0x00ff);
|
||||
|
||||
// Found by solving for c1 overflow:
|
||||
// n > 0 and n * (n+1) / 2 * (2^8-1) < (2^32-1).
|
||||
@@ -34,7 +37,8 @@ namespace SabreTools.Hashing.Checksum
|
||||
c1 %= 255;
|
||||
}
|
||||
|
||||
_hash = (ushort)(c1 << 8 | c0);
|
||||
// Return recombined sums
|
||||
_hash = (ushort)((c1 << 8) | c0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,16 @@ namespace SabreTools.Hashing.Checksum
|
||||
/// <remarks>Uses an Adler-32-like implementation instead of the above</remarks>
|
||||
public class Fletcher32 : ChecksumBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 32;
|
||||
|
||||
public Fletcher32()
|
||||
{
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Split Fletcher-32 into component sums
|
||||
uint c0 = _hash & 0xffff;
|
||||
|
||||
@@ -6,13 +6,16 @@ namespace SabreTools.Hashing.Checksum
|
||||
/// <remarks>Uses an Adler-32-like implementation instead of the above</remarks>
|
||||
public class Fletcher64 : ChecksumBase<ulong>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 64;
|
||||
|
||||
public Fletcher64()
|
||||
{
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Split Fletcher-64 into component sums
|
||||
ulong c0 = _hash & 0xffffffff;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
namespace SabreTools.Hashing.Checksum
|
||||
{
|
||||
public abstract class FnvBase<T> : ChecksumBase<T> where T : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Initial value to use
|
||||
/// </summary>
|
||||
protected T _basis;
|
||||
|
||||
/// <summary>
|
||||
/// Round prime to use
|
||||
/// </summary>
|
||||
protected T _prime;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Reset()
|
||||
{
|
||||
_hash = _basis;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using static SabreTools.Hashing.MessageDigest.Constants;
|
||||
using static SabreTools.Hashing.CryptographicHash.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <see href="https://datatracker.ietf.org/doc/html/rfc1115"/>
|
||||
public class MD2 : MessageDigestBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 128;
|
||||
|
||||
/// <summary>
|
||||
/// Buffer for forming digest in
|
||||
/// </summary>
|
||||
@@ -41,7 +44,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Figure out how much buffer is needed
|
||||
int bufferLen = 16 - _byteCount;
|
||||
@@ -116,7 +119,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Terminate()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
// Determine the pad length
|
||||
byte padLength = (byte)(16 - _byteCount);
|
||||
@@ -131,18 +134,14 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
#else
|
||||
Array.Fill(padding, padLength);
|
||||
#endif
|
||||
TransformBlock(padding, 0, padLength);
|
||||
TransformBlock(_checksum, 0, _checksum.Length);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
// Pad the block
|
||||
HashCore(padding, 0, padLength);
|
||||
HashCore(_checksum, 0, _checksum.Length);
|
||||
|
||||
// Get the hash
|
||||
var hash = new byte[16];
|
||||
Array.Copy(_digest, hash, 16);
|
||||
|
||||
// Reset the state and return
|
||||
Reset();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
using static SabreTools.Hashing.CryptographicHash.Constants;
|
||||
using static SabreTools.Hashing.HashOperations;
|
||||
using static SabreTools.Hashing.MessageDigest.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <see href="https://datatracker.ietf.org/doc/html/rfc1320"/>
|
||||
public class MD4 : MessageDigestBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 128;
|
||||
|
||||
/// <summary>
|
||||
/// Set of 4 32-bit numbers representing the hash state
|
||||
/// </summary>
|
||||
@@ -26,7 +29,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Figure out how much buffer is needed
|
||||
int bufferLen = (int)(_totalBytes & 0x3f);
|
||||
@@ -81,7 +84,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Terminate()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
// Determine the pad length
|
||||
int padLength = 64 - (int)(_totalBytes & 0x3f);
|
||||
@@ -104,12 +107,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
padding[padLength - 8] = (byte)((totalBitCount >> 0) & 0xff);
|
||||
|
||||
// Pad the block
|
||||
TransformBlock(padding, 0, padding.Length);
|
||||
}
|
||||
HashCore(padding, 0, padding.Length);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
// Get the hash
|
||||
var hash = new byte[16];
|
||||
int hashOffset = 0;
|
||||
|
||||
@@ -121,8 +121,6 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
hashOffset += 4;
|
||||
}
|
||||
|
||||
// Reset the state and return
|
||||
Reset();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
public abstract class MessageDigestBase
|
||||
public abstract class MessageDigestBase : System.Security.Cryptography.HashAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Total number of bytes processed
|
||||
@@ -19,29 +19,8 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
/// </summary>
|
||||
protected abstract void ResetImpl();
|
||||
|
||||
/// <summary>
|
||||
/// Hash a block of data and append it to the existing hash
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the data</param>
|
||||
/// <param name="offset">Offset in the byte array to include</param>
|
||||
/// <param name="length">Length of the data to hash</param>
|
||||
public abstract void TransformBlock(byte[] data, int offset, int length);
|
||||
|
||||
/// <summary>
|
||||
/// End the hashing process
|
||||
/// </summary>
|
||||
/// TODO: Combine this when the padding byte can be set by implementing classes
|
||||
public abstract void Terminate();
|
||||
|
||||
/// <summary>
|
||||
/// Get the current value of the hash
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If <see cref="Terminate"/> has not been run, this value
|
||||
/// will not be accurate for the processed bytes so far.
|
||||
/// </remarks>
|
||||
/// TODO: Combine this when there's an easier way of passing the state
|
||||
public abstract byte[] GetHash();
|
||||
/// <inheritdoc/>
|
||||
protected abstract override void HashCore(byte[] array, int ibStart, int cbSize);
|
||||
}
|
||||
|
||||
public abstract class MessageDigestBase<T> : MessageDigestBase where T : struct
|
||||
@@ -63,13 +42,11 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
else
|
||||
throw new InvalidOperationException();
|
||||
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the internal hashing state
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
// Reset the seed values
|
||||
ResetImpl();
|
||||
@@ -1,13 +1,16 @@
|
||||
using System;
|
||||
using static SabreTools.Hashing.CryptographicHash.Constants;
|
||||
using static SabreTools.Hashing.HashOperations;
|
||||
using static SabreTools.Hashing.MessageDigest.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <see href="https://cdn.standards.iteh.ai/samples/39876/10f9f9f4bb614eaaaeba7e157e183ca3/ISO-IEC-10118-3-2004.pdf"/>
|
||||
/// <see href="https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf"/>
|
||||
public class RipeMD128 : MessageDigestBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 128;
|
||||
|
||||
/// <summary>
|
||||
/// Set of 4 32-bit numbers representing the hash state
|
||||
/// </summary>
|
||||
@@ -27,7 +30,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Figure out how much buffer is needed
|
||||
int bufferLen = (int)(_totalBytes & 0x3f);
|
||||
@@ -82,7 +85,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Terminate()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
// Determine the pad length
|
||||
int padLength = 64 - (int)(_totalBytes & 0x3f);
|
||||
@@ -105,12 +108,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
padding[padLength - 8] = (byte)((totalBitCount >> 0) & 0xff);
|
||||
|
||||
// Pad the block
|
||||
TransformBlock(padding, 0, padding.Length);
|
||||
}
|
||||
HashCore(padding, 0, padding.Length);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
// Get the hash
|
||||
var hash = new byte[16];
|
||||
int hashOffset = 0;
|
||||
|
||||
@@ -122,8 +122,6 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
hashOffset += 4;
|
||||
}
|
||||
|
||||
// Reset the state and return
|
||||
Reset();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using System;
|
||||
using static SabreTools.Hashing.CryptographicHash.Constants;
|
||||
using static SabreTools.Hashing.HashOperations;
|
||||
using static SabreTools.Hashing.MessageDigest.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <see href="https://cdn.standards.iteh.ai/samples/39876/10f9f9f4bb614eaaaeba7e157e183ca3/ISO-IEC-10118-3-2004.pdf"/>
|
||||
/// <see href="https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf"/>
|
||||
public class RipeMD160 : MessageDigestBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 160;
|
||||
|
||||
/// <summary>
|
||||
/// Set of 5 32-bit numbers representing the hash state
|
||||
/// </summary>
|
||||
@@ -28,7 +31,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Figure out how much buffer is needed
|
||||
int bufferLen = (int)(_totalBytes & 0x3f);
|
||||
@@ -83,7 +86,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Terminate()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
// Determine the pad length
|
||||
int padLength = 64 - (int)(_totalBytes & 0x3f);
|
||||
@@ -102,16 +105,13 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
padding[padLength - 4] = (byte)((totalBitCount >> 32) & 0xff);
|
||||
padding[padLength - 5] = (byte)((totalBitCount >> 24) & 0xff);
|
||||
padding[padLength - 6] = (byte)((totalBitCount >> 16) & 0xff);
|
||||
padding[padLength - 7] = (byte)((totalBitCount >> 8 ) & 0xff);
|
||||
padding[padLength - 8] = (byte)((totalBitCount >> 0 ) & 0xff);
|
||||
padding[padLength - 7] = (byte)((totalBitCount >> 8) & 0xff);
|
||||
padding[padLength - 8] = (byte)((totalBitCount >> 0) & 0xff);
|
||||
|
||||
// Pad the block
|
||||
TransformBlock(padding, 0, padding.Length);
|
||||
}
|
||||
HashCore(padding, 0, padding.Length);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
// Get the hash
|
||||
var hash = new byte[20];
|
||||
int hashOffset = 0;
|
||||
|
||||
@@ -123,8 +123,6 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
hashOffset += 4;
|
||||
}
|
||||
|
||||
// Reset the state and return
|
||||
Reset();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using System;
|
||||
using static SabreTools.Hashing.CryptographicHash.Constants;
|
||||
using static SabreTools.Hashing.HashOperations;
|
||||
using static SabreTools.Hashing.MessageDigest.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <see href="https://cdn.standards.iteh.ai/samples/39876/10f9f9f4bb614eaaaeba7e157e183ca3/ISO-IEC-10118-3-2004.pdf"/>
|
||||
/// <see href="https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf"/>
|
||||
public class RipeMD256 : MessageDigestBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 256;
|
||||
|
||||
/// <summary>
|
||||
/// Set of 4 32-bit numbers representing the hash state
|
||||
/// </summary>
|
||||
@@ -31,7 +34,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Figure out how much buffer is needed
|
||||
int bufferLen = (int)(_totalBytes & 0x3f);
|
||||
@@ -86,7 +89,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Terminate()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
// Determine the pad length
|
||||
int padLength = 64 - (int)(_totalBytes & 0x3f);
|
||||
@@ -109,12 +112,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
padding[padLength - 8] = (byte)((totalBitCount >> 0) & 0xff);
|
||||
|
||||
// Pad the block
|
||||
TransformBlock(padding, 0, padding.Length);
|
||||
}
|
||||
HashCore(padding, 0, padding.Length);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
// Get the hash
|
||||
var hash = new byte[32];
|
||||
int hashOffset = 0;
|
||||
|
||||
@@ -126,8 +126,6 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
hashOffset += 4;
|
||||
}
|
||||
|
||||
// Reset the state and return
|
||||
Reset();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using System;
|
||||
using static SabreTools.Hashing.CryptographicHash.Constants;
|
||||
using static SabreTools.Hashing.HashOperations;
|
||||
using static SabreTools.Hashing.MessageDigest.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <see href="https://cdn.standards.iteh.ai/samples/39876/10f9f9f4bb614eaaaeba7e157e183ca3/ISO-IEC-10118-3-2004.pdf"/>
|
||||
/// <see href="https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf"/>
|
||||
public class RipeMD320 : MessageDigestBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 320;
|
||||
|
||||
/// <summary>
|
||||
/// Set of 10 32-bit numbers representing the hash state
|
||||
/// </summary>
|
||||
@@ -33,7 +36,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Figure out how much buffer is needed
|
||||
int bufferLen = (int)(_totalBytes & 0x3f);
|
||||
@@ -88,7 +91,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Terminate()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
// Determine the pad length
|
||||
int padLength = 64 - (int)(_totalBytes & 0x3f);
|
||||
@@ -107,16 +110,13 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
padding[padLength - 4] = (byte)((totalBitCount >> 32) & 0xff);
|
||||
padding[padLength - 5] = (byte)((totalBitCount >> 24) & 0xff);
|
||||
padding[padLength - 6] = (byte)((totalBitCount >> 16) & 0xff);
|
||||
padding[padLength - 7] = (byte)((totalBitCount >> 8 ) & 0xff);
|
||||
padding[padLength - 8] = (byte)((totalBitCount >> 0 ) & 0xff);
|
||||
padding[padLength - 7] = (byte)((totalBitCount >> 8) & 0xff);
|
||||
padding[padLength - 8] = (byte)((totalBitCount >> 0) & 0xff);
|
||||
|
||||
// Pad the block
|
||||
TransformBlock(padding, 0, padding.Length);
|
||||
}
|
||||
HashCore(padding, 0, padding.Length);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
// Get the hash
|
||||
var hash = new byte[40];
|
||||
int hashOffset = 0;
|
||||
|
||||
@@ -128,8 +128,6 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
hashOffset += 4;
|
||||
}
|
||||
|
||||
// Reset the state and return
|
||||
Reset();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 3-pass variant of Tiger-128
|
||||
/// </summary>
|
||||
public class Tiger128_3 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 128;
|
||||
|
||||
public Tiger128_3() : base()
|
||||
{
|
||||
_passes = 3;
|
||||
@@ -14,9 +17,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
byte[] hash = base.GetHash();
|
||||
byte[] hash = base.HashFinal();
|
||||
byte[] trimmedHash = new byte[16];
|
||||
Array.Copy(hash, trimmedHash, 16);
|
||||
return trimmedHash;
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 4-pass variant of Tiger-128
|
||||
/// </summary>
|
||||
public class Tiger128_4 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 128;
|
||||
|
||||
public Tiger128_4() : base()
|
||||
{
|
||||
_passes = 4;
|
||||
@@ -14,9 +17,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
byte[] hash = base.GetHash();
|
||||
byte[] hash = base.HashFinal();
|
||||
byte[] trimmedHash = new byte[16];
|
||||
Array.Copy(hash, trimmedHash, 16);
|
||||
return trimmedHash;
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 3-pass variant of Tiger-160
|
||||
/// </summary>
|
||||
public class Tiger160_3 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 160;
|
||||
|
||||
public Tiger160_3() : base()
|
||||
{
|
||||
_passes = 3;
|
||||
@@ -14,9 +17,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
byte[] hash = base.GetHash();
|
||||
byte[] hash = base.HashFinal();
|
||||
byte[] trimmedHash = new byte[20];
|
||||
Array.Copy(hash, trimmedHash, 20);
|
||||
return trimmedHash;
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 4-pass variant of Tiger-160
|
||||
/// </summary>
|
||||
public class Tiger160_4 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 160;
|
||||
|
||||
public Tiger160_4() : base()
|
||||
{
|
||||
_passes = 4;
|
||||
@@ -14,9 +17,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
byte[] hash = base.GetHash();
|
||||
byte[] hash = base.HashFinal();
|
||||
byte[] trimmedHash = new byte[20];
|
||||
Array.Copy(hash, trimmedHash, 20);
|
||||
return trimmedHash;
|
||||
@@ -1,10 +1,13 @@
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 3-pass variant of Tiger-192
|
||||
/// </summary>
|
||||
public class Tiger192_3 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 192;
|
||||
|
||||
public Tiger192_3() : base()
|
||||
{
|
||||
_passes = 3;
|
||||
@@ -1,10 +1,13 @@
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 4-pass variant of Tiger-192
|
||||
/// </summary>
|
||||
public class Tiger192_4 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 192;
|
||||
|
||||
public Tiger192_4() : base()
|
||||
{
|
||||
_passes = 4;
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 3-pass variant of Tiger2-128
|
||||
/// </summary>
|
||||
public class Tiger2_128_3 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 128;
|
||||
|
||||
public Tiger2_128_3() : base()
|
||||
{
|
||||
_passes = 3;
|
||||
@@ -14,9 +17,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
byte[] hash = base.GetHash();
|
||||
byte[] hash = base.HashFinal();
|
||||
byte[] trimmedHash = new byte[16];
|
||||
Array.Copy(hash, trimmedHash, 16);
|
||||
return trimmedHash;
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 4-pass variant of Tiger2-128
|
||||
/// </summary>
|
||||
public class Tiger2_128_4 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 128;
|
||||
|
||||
public Tiger2_128_4() : base()
|
||||
{
|
||||
_passes = 4;
|
||||
@@ -14,9 +17,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
byte[] hash = base.GetHash();
|
||||
byte[] hash = base.HashFinal();
|
||||
byte[] trimmedHash = new byte[16];
|
||||
Array.Copy(hash, trimmedHash, 16);
|
||||
return trimmedHash;
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 3-pass variant of Tiger2-160
|
||||
/// </summary>
|
||||
public class Tiger2_160_3 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 160;
|
||||
|
||||
public Tiger2_160_3() : base()
|
||||
{
|
||||
_passes = 3;
|
||||
@@ -14,9 +17,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
byte[] hash = base.GetHash();
|
||||
byte[] hash = base.HashFinal();
|
||||
byte[] trimmedHash = new byte[20];
|
||||
Array.Copy(hash, trimmedHash, 20);
|
||||
return trimmedHash;
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 4-pass variant of Tiger2-160
|
||||
/// </summary>
|
||||
public class Tiger2_160_4 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 160;
|
||||
|
||||
public Tiger2_160_4() : base()
|
||||
{
|
||||
_passes = 4;
|
||||
@@ -14,9 +17,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
byte[] hash = base.GetHash();
|
||||
byte[] hash = base.HashFinal();
|
||||
byte[] trimmedHash = new byte[20];
|
||||
Array.Copy(hash, trimmedHash, 20);
|
||||
return trimmedHash;
|
||||
@@ -1,10 +1,13 @@
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 3-pass variant of Tiger2-192
|
||||
/// </summary>
|
||||
public class Tiger2_192_3 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 192;
|
||||
|
||||
public Tiger2_192_3() : base()
|
||||
{
|
||||
_passes = 3;
|
||||
@@ -1,10 +1,13 @@
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// 4-pass variant of Tiger2-192
|
||||
/// </summary>
|
||||
public class Tiger2_192_4 : TigerHashBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 192;
|
||||
|
||||
public Tiger2_192_4() : base()
|
||||
{
|
||||
_passes = 4;
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using static SabreTools.Hashing.CryptographicHash.Constants;
|
||||
using static SabreTools.Hashing.HashOperations;
|
||||
using static SabreTools.Hashing.MessageDigest.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.MessageDigest
|
||||
namespace SabreTools.Hashing.CryptographicHash
|
||||
{
|
||||
/// <see href="https://biham.cs.technion.ac.il/Reports/Tiger//>
|
||||
public abstract class TigerHashBase : MessageDigestBase<ulong>
|
||||
@@ -35,7 +35,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
// Figure out how much buffer is needed
|
||||
int bufferLen = (int)(_totalBytes & 0x3f);
|
||||
@@ -90,7 +90,7 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Terminate()
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
// Determine the pad length
|
||||
int padLength = 64 - (int)(_totalBytes & 0x3f);
|
||||
@@ -113,12 +113,9 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
padding[padLength - 8] = (byte)((totalBitCount >> 0) & 0xff);
|
||||
|
||||
// Pad the block
|
||||
TransformBlock(padding, 0, padding.Length);
|
||||
}
|
||||
HashCore(padding, 0, padding.Length);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte[] GetHash()
|
||||
{
|
||||
// Get the hash
|
||||
var hash = new byte[24];
|
||||
int hashOffset = 0;
|
||||
|
||||
@@ -130,8 +127,6 @@ namespace SabreTools.Hashing.MessageDigest
|
||||
hashOffset += 8;
|
||||
}
|
||||
|
||||
// Reset the state and return
|
||||
Reset();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,16 @@ namespace SabreTools.Hashing
|
||||
/// </summary>
|
||||
public static class HashTool
|
||||
{
|
||||
#region Standard Hashes
|
||||
|
||||
/// <summary>
|
||||
/// Get CRC-32, MD5, and SHA-1 hashes from an input file path
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <param name="size">Calculated file size on success, -1 on error</param>
|
||||
/// <param name="crc32">CRC-32 for the input file</param>
|
||||
/// <param name="md5">MD5 for the input file</param>
|
||||
/// <param name="sha1">SHA-1 for the input file</param>
|
||||
/// <returns>True if hashing was successful, false otherwise</returns>
|
||||
public static bool GetStandardHashes(string filename, out long size, out string? crc32, out string? md5, out string? sha1)
|
||||
{
|
||||
@@ -35,6 +41,62 @@ namespace SabreTools.Hashing
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get CRC-32, MD5, and SHA-1 hashes from an input byte array
|
||||
/// </summary>
|
||||
/// <param name="array">Input byte array</param>
|
||||
/// <param name="size">Calculated file size on success, -1 on error</param>
|
||||
/// <param name="crc32">CRC-32 for the input file</param>
|
||||
/// <param name="md5">MD5 for the input file</param>
|
||||
/// <param name="sha1">SHA-1 for the input file</param>
|
||||
/// <returns>True if hashing was successful, false otherwise</returns>
|
||||
public static bool GetStandardHashes(byte[] array, out long size, out string? crc32, out string? md5, out string? sha1)
|
||||
{
|
||||
// Set all initial values
|
||||
crc32 = null; md5 = null; sha1 = null;
|
||||
|
||||
// Get all file hashes
|
||||
HashType[] standardHashTypes = [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
var fileHashes = GetByteArrayHashesAndSize(array, standardHashTypes, out size);
|
||||
if (fileHashes == null)
|
||||
return false;
|
||||
|
||||
// Assign the file hashes and return
|
||||
crc32 = fileHashes[HashType.CRC32];
|
||||
md5 = fileHashes[HashType.MD5];
|
||||
sha1 = fileHashes[HashType.SHA1];
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get CRC-32, MD5, and SHA-1 hashes from an input stream
|
||||
/// </summary>
|
||||
/// <param name="stream">Input stream</param>
|
||||
/// <param name="size">Calculated file size on success, -1 on error</param>
|
||||
/// <param name="crc32">CRC-32 for the input file</param>
|
||||
/// <param name="md5">MD5 for the input file</param>
|
||||
/// <param name="sha1">SHA-1 for the input file</param>
|
||||
/// <returns>True if hashing was successful, false otherwise</returns>
|
||||
public static bool GetStandardHashes(Stream stream, out long size, out string? crc32, out string? md5, out string? sha1)
|
||||
{
|
||||
// Set all initial values
|
||||
crc32 = null; md5 = null; sha1 = null;
|
||||
|
||||
// Get all file hashes
|
||||
HashType[] standardHashTypes = [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
||||
var fileHashes = GetStreamHashesAndSize(stream, standardHashTypes, out size);
|
||||
if (fileHashes == null)
|
||||
return false;
|
||||
|
||||
// Assign the file hashes and return
|
||||
crc32 = fileHashes[HashType.CRC32];
|
||||
md5 = fileHashes[HashType.MD5];
|
||||
sha1 = fileHashes[HashType.SHA1];
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File Hashes Without Size
|
||||
|
||||
/// <summary>
|
||||
@@ -60,10 +122,7 @@ namespace SabreTools.Hashing
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static string? GetFileHash(string filename, HashType hashType)
|
||||
{
|
||||
var hashes = GetFileHashes(filename, [hashType]);
|
||||
return hashes?[hashType];
|
||||
}
|
||||
=> GetFileHashAndSize(filename, hashType, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash from an input file path
|
||||
@@ -72,10 +131,7 @@ namespace SabreTools.Hashing
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static byte[]? GetFileHashArray(string filename, HashType hashType)
|
||||
{
|
||||
var hashes = GetFileHashArrays(filename, [hashType]);
|
||||
return hashes?[hashType];
|
||||
}
|
||||
=> GetFileHashArrayAndSize(filename, hashType, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input file path
|
||||
@@ -103,6 +159,7 @@ namespace SabreTools.Hashing
|
||||
/// Get hashes and size from an input file path
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetFileHashesAndSize(string filename, out long size)
|
||||
{
|
||||
@@ -117,6 +174,7 @@ namespace SabreTools.Hashing
|
||||
/// Get hashes and size from an input file path
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetFileHashArraysAndSize(string filename, out long size)
|
||||
{
|
||||
@@ -132,6 +190,7 @@ namespace SabreTools.Hashing
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Hash and size on success, null on error</returns>
|
||||
public static string? GetFileHashAndSize(string filename, HashType hashType, out long size)
|
||||
{
|
||||
@@ -144,6 +203,7 @@ namespace SabreTools.Hashing
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Hash and size on success, null on error</returns>
|
||||
public static byte[]? GetFileHashArrayAndSize(string filename, HashType hashType, out long size)
|
||||
{
|
||||
@@ -156,6 +216,7 @@ namespace SabreTools.Hashing
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetFileHashesAndSize(string filename, HashType[] hashTypes, out long size)
|
||||
{
|
||||
@@ -166,14 +227,11 @@ namespace SabreTools.Hashing
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set the file size
|
||||
size = new FileInfo(filename).Length;
|
||||
|
||||
// Open the input file
|
||||
var input = File.OpenRead(filename);
|
||||
|
||||
// Return the hashes from the stream
|
||||
return GetStreamHashes(input, hashTypes);
|
||||
return GetStreamHashesAndSize(input, hashTypes, out size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -181,6 +239,7 @@ namespace SabreTools.Hashing
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetFileHashArraysAndSize(string filename, HashType[] hashTypes, out long size)
|
||||
{
|
||||
@@ -191,19 +250,16 @@ namespace SabreTools.Hashing
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set the file size
|
||||
size = new FileInfo(filename).Length;
|
||||
|
||||
// Open the input file
|
||||
var input = File.OpenRead(filename);
|
||||
|
||||
// Return the hashes from the stream
|
||||
return GetStreamHashArrays(input, hashTypes);
|
||||
return GetStreamHashArraysAndSize(input, hashTypes, out size);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Byte Array Hashes
|
||||
#region Byte Array Hashes Without Size
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input byte array
|
||||
@@ -211,13 +267,7 @@ namespace SabreTools.Hashing
|
||||
/// <param name="input">Byte array to hash</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetByteArrayHashes(byte[] input)
|
||||
{
|
||||
// Create a hash array for all entries
|
||||
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
|
||||
|
||||
// Return the hashes from the stream
|
||||
return GetStreamHashes(new MemoryStream(input), hashTypes);
|
||||
}
|
||||
=> GetByteArrayHashesAndSize(input, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input byte array
|
||||
@@ -225,13 +275,7 @@ namespace SabreTools.Hashing
|
||||
/// <param name="input">Byte array to hash</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetByteArrayHashArrays(byte[] input)
|
||||
{
|
||||
// Create a hash array for all entries
|
||||
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
|
||||
|
||||
// Return the hashes from the stream
|
||||
return GetStreamHashArrays(new MemoryStream(input), hashTypes);
|
||||
}
|
||||
=> GetByteArrayHashArraysAndSize(input, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash from an input byte array
|
||||
@@ -240,10 +284,7 @@ namespace SabreTools.Hashing
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static string? GetByteArrayHash(byte[] input, HashType hashType)
|
||||
{
|
||||
var hashes = GetStreamHashes(new MemoryStream(input), [hashType]);
|
||||
return hashes?[hashType];
|
||||
}
|
||||
=> GetByteArrayHashAndSize(input, hashType, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash from an input byte array
|
||||
@@ -252,10 +293,7 @@ namespace SabreTools.Hashing
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static byte[]? GetByteArrayHashArray(byte[] input, HashType hashType)
|
||||
{
|
||||
var hashes = GetStreamHashArrays(new MemoryStream(input), [hashType]);
|
||||
return hashes?[hashType];
|
||||
}
|
||||
=> GetByteArrayHashArrayAndSize(input, hashType, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input byte array
|
||||
@@ -264,7 +302,7 @@ namespace SabreTools.Hashing
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetByteArrayHashes(byte[] input, HashType[] hashTypes)
|
||||
=> GetStreamHashes(new MemoryStream(input), hashTypes);
|
||||
=> GetByteArrayHashesAndSize(input, hashTypes, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input byte array
|
||||
@@ -273,71 +311,271 @@ namespace SabreTools.Hashing
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetByteArrayHashArrays(byte[] input, HashType[] hashTypes)
|
||||
=> GetStreamHashArrays(new MemoryStream(input), hashTypes);
|
||||
=> GetByteArrayHashArraysAndSize(input, hashTypes, out _);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Hashes
|
||||
#region Byte Array Hashes With Size
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input byte array
|
||||
/// </summary>
|
||||
/// <param name="input">Byte array to hash</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetByteArrayHashesAndSize(byte[] input, out long size)
|
||||
{
|
||||
// Create a hash array for all entries
|
||||
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
|
||||
|
||||
// Return the hashes from the stream
|
||||
return GetStreamHashesAndSize(new MemoryStream(input), hashTypes, out size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input byte array
|
||||
/// </summary>
|
||||
/// <param name="input">Byte array to hash</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetByteArrayHashArraysAndSize(byte[] input, out long size)
|
||||
{
|
||||
// Create a hash array for all entries
|
||||
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
|
||||
|
||||
// Return the hashes from the stream
|
||||
return GetStreamHashArraysAndSize(new MemoryStream(input), hashTypes, out size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash from an input byte array
|
||||
/// </summary>
|
||||
/// <param name="input">Byte array to hash</param>
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static string? GetByteArrayHashAndSize(byte[] input, HashType hashType, out long size)
|
||||
{
|
||||
var hashes = GetStreamHashesAndSize(new MemoryStream(input), [hashType], out size);
|
||||
return hashes?[hashType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash from an input byte array
|
||||
/// </summary>
|
||||
/// <param name="input">Byte array to hash</param>
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static byte[]? GetByteArrayHashArrayAndSize(byte[] input, HashType hashType, out long size)
|
||||
{
|
||||
var hashes = GetStreamHashArraysAndSize(new MemoryStream(input), [hashType], out size);
|
||||
return hashes?[hashType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input byte array
|
||||
/// </summary>
|
||||
/// <param name="input">Byte array to hash</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetByteArrayHashesAndSize(byte[] input, HashType[] hashTypes, out long size)
|
||||
=> GetStreamHashesAndSize(new MemoryStream(input), hashTypes, out size);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input byte array
|
||||
/// </summary>
|
||||
/// <param name="input">Byte array to hash</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetByteArrayHashArraysAndSize(byte[] input, HashType[] hashTypes, out long size)
|
||||
=> GetStreamHashArraysAndSize(new MemoryStream(input), hashTypes, out size);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Hashes Without Size
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetStreamHashes(Stream input, bool leaveOpen = false)
|
||||
{
|
||||
// Create a hash array for all entries
|
||||
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
|
||||
|
||||
// Get the output hashes
|
||||
return GetStreamHashes(input, hashTypes, leaveOpen);
|
||||
}
|
||||
=> GetStreamHashesAndSize(input, leaveOpen, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetStreamHashArrays(Stream input, bool leaveOpen = false)
|
||||
{
|
||||
// Create a hash array for all entries
|
||||
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
|
||||
|
||||
// Get the output hashes
|
||||
return GetStreamHashArrays(input, hashTypes, leaveOpen);
|
||||
}
|
||||
=> GetStreamHashArraysAndSize(input, leaveOpen, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash and size from an input Stream
|
||||
/// Get a hash from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static string? GetStreamHash(Stream input, HashType hashType, bool leaveOpen = false)
|
||||
{
|
||||
var hashes = GetStreamHashes(input, [hashType], leaveOpen);
|
||||
return hashes?[hashType];
|
||||
}
|
||||
=> GetStreamHashAndSize(input, hashType, leaveOpen, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash and size from an input Stream
|
||||
/// Get a hash from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static byte[]? GetStreamHashArray(Stream input, HashType hashType, bool leaveOpen = false)
|
||||
{
|
||||
var hashes = GetStreamHashArrays(input, [hashType], leaveOpen);
|
||||
return hashes?[hashType];
|
||||
}
|
||||
=> GetStreamHashArrayAndSize(input, hashType, leaveOpen, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetStreamHashes(Stream input, HashType[] hashTypes, bool leaveOpen = false)
|
||||
=> GetStreamHashesAndSize(input, hashTypes, leaveOpen, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetStreamHashArrays(Stream input, HashType[] hashTypes, bool leaveOpen = false)
|
||||
=> GetStreamHashArraysAndSize(input, hashTypes, leaveOpen, out _);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Hashes With Size
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetStreamHashesAndSize(Stream input, out long size)
|
||||
=> GetStreamHashesAndSize(input, leaveOpen: false, out size);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetStreamHashesAndSize(Stream input, bool leaveOpen, out long size)
|
||||
{
|
||||
// Create a hash array for all entries
|
||||
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
|
||||
|
||||
// Get the output hashes
|
||||
return GetStreamHashesAndSize(input, hashTypes, leaveOpen, out size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetStreamHashArraysAndSize(Stream input, out long size)
|
||||
=> GetStreamHashArraysAndSize(input, leaveOpen: false, out size);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetStreamHashArraysAndSize(Stream input, bool leaveOpen, out long size)
|
||||
{
|
||||
// Create a hash array for all entries
|
||||
HashType[] hashTypes = (HashType[])Enum.GetValues(typeof(HashType));
|
||||
|
||||
// Get the output hashes
|
||||
return GetStreamHashArraysAndSize(input, hashTypes, leaveOpen, out size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static string? GetStreamHashAndSize(Stream input, HashType hashType, out long size)
|
||||
=> GetStreamHashAndSize(input, hashType, leaveOpen: false, out size);
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static string? GetStreamHashAndSize(Stream input, HashType hashType, bool leaveOpen, out long size)
|
||||
{
|
||||
var hashes = GetStreamHashesAndSize(input, [hashType], leaveOpen, out size);
|
||||
return hashes?[hashType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static byte[]? GetStreamHashArrayAndSize(Stream input, HashType hashType, out long size)
|
||||
=> GetStreamHashArrayAndSize(input, hashType, leaveOpen: false, out size);
|
||||
|
||||
/// <summary>
|
||||
/// Get a hash and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashType">Hash type to get from the file</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Hash on success, null on error</returns>
|
||||
public static byte[]? GetStreamHashArrayAndSize(Stream input, HashType hashType, bool leaveOpen, out long size)
|
||||
{
|
||||
var hashes = GetStreamHashArraysAndSize(input, [hashType], leaveOpen, out size);
|
||||
return hashes?[hashType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetStreamHashesAndSize(Stream input, HashType[] hashTypes, out long size)
|
||||
=> GetStreamHashesAndSize(input, hashTypes, leaveOpen: false, out size);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, string?>? GetStreamHashesAndSize(Stream input, HashType[] hashTypes, bool leaveOpen, out long size)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var hashDict = new Dictionary<HashType, string?>();
|
||||
@@ -352,13 +590,14 @@ namespace SabreTools.Hashing
|
||||
hashDict[hashType] = ZeroHash.GetString(hashType);
|
||||
}
|
||||
|
||||
size = 0;
|
||||
return hashDict;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Run the hashing
|
||||
var hashers = GetStreamHashesInternal(input, hashTypes, leaveOpen);
|
||||
var hashers = GetStreamHashesInternal(input, hashTypes, leaveOpen, out size);
|
||||
if (hashers == null)
|
||||
return null;
|
||||
|
||||
@@ -378,12 +617,24 @@ namespace SabreTools.Hashing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input Stream
|
||||
/// Get hashes and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetStreamHashArrays(Stream input, HashType[] hashTypes, bool leaveOpen = false)
|
||||
public static Dictionary<HashType, byte[]?>? GetStreamHashArraysAndSize(Stream input, HashType[] hashTypes, out long size)
|
||||
=> GetStreamHashArraysAndSize(input, hashTypes, leaveOpen: false, out size);
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<HashType, byte[]?>? GetStreamHashArraysAndSize(Stream input, HashType[] hashTypes, bool leaveOpen, out long size)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var hashDict = new Dictionary<HashType, byte[]?>();
|
||||
@@ -398,13 +649,14 @@ namespace SabreTools.Hashing
|
||||
hashDict[hashType] = ZeroHash.GetBytes(hashType);
|
||||
}
|
||||
|
||||
size = 0;
|
||||
return hashDict;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Run the hashing
|
||||
var hashers = GetStreamHashesInternal(input, hashTypes, leaveOpen);
|
||||
var hashers = GetStreamHashesInternal(input, hashTypes, leaveOpen, out size);
|
||||
if (hashers == null)
|
||||
return null;
|
||||
|
||||
@@ -424,16 +676,18 @@ namespace SabreTools.Hashing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input stream
|
||||
/// Get hashes and size from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="hashTypes"></param>
|
||||
/// <param name="leaveOpen"></param>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <param name="hashTypes">Array of hash types to get from the file</param>
|
||||
/// <param name="leaveOpen">Indicates if the source stream should be left open after hashing</param>
|
||||
/// <param name="size">Amount of bytes read during hashing</param>
|
||||
/// <returns></returns>
|
||||
private static Dictionary<HashType, HashWrapper>? GetStreamHashesInternal(Stream input, HashType[] hashTypes, bool leaveOpen)
|
||||
private static Dictionary<HashType, HashWrapper>? GetStreamHashesInternal(Stream input, HashType[] hashTypes, bool leaveOpen, out long size)
|
||||
{
|
||||
// Create the output dictionary
|
||||
// Create the output dictionary and size counter
|
||||
var hashDict = new Dictionary<HashType, string?>();
|
||||
size = 0;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -456,6 +710,7 @@ namespace SabreTools.Hashing
|
||||
{
|
||||
// Load the buffer and hold the number of bytes read
|
||||
lastRead = input.Read(buffer, 0, buffersize);
|
||||
size += lastRead;
|
||||
if (lastRead == 0)
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
using System;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using System.IO.Hashing;
|
||||
#endif
|
||||
using System.Security.Cryptography;
|
||||
using Aaru.Checksums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
#if NET7_0_OR_GREATER
|
||||
using Blake3;
|
||||
#endif
|
||||
using SabreTools.Hashing.Checksum;
|
||||
using SabreTools.Hashing.CryptographicHash;
|
||||
using SabreTools.Hashing.NonCryptographicHash;
|
||||
using static SabreTools.Hashing.HashOperations;
|
||||
|
||||
namespace SabreTools.Hashing
|
||||
@@ -28,81 +22,33 @@ namespace SabreTools.Hashing
|
||||
/// <summary>
|
||||
/// Current hash in bytes
|
||||
/// </summary>
|
||||
public byte[]? CurrentHashBytes
|
||||
public byte[]? CurrentHashBytes => _hasher switch
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (_hasher)
|
||||
{
|
||||
case ChecksumBase cb:
|
||||
var cbArr = cb.Finalize();
|
||||
Array.Reverse(cbArr);
|
||||
return cbArr;
|
||||
|
||||
case HashAlgorithm ha:
|
||||
return ha.Hash;
|
||||
|
||||
case IChecksum ic:
|
||||
return ic.Final();
|
||||
|
||||
case MessageDigest.MessageDigestBase mdb:
|
||||
return mdb.GetHash();
|
||||
|
||||
HashAlgorithm ha => ha.Hash,
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
case XxHash3 xxh3:
|
||||
return xxh3.GetCurrentHash();
|
||||
case XxHash128 xxh128:
|
||||
return xxh128.GetCurrentHash();
|
||||
case NonCryptographicHashAlgorithm ncha:
|
||||
var nchaArr = ncha.GetCurrentHash();
|
||||
Array.Reverse(nchaArr);
|
||||
return nchaArr;
|
||||
System.IO.Hashing.NonCryptographicHashAlgorithm ncha => ncha.GetCurrentHash(),
|
||||
#endif
|
||||
|
||||
#if NET8_0_OR_GREATER
|
||||
case Shake128 s128:
|
||||
return s128.GetCurrentHash(32);
|
||||
case Shake256 s256:
|
||||
return s256.GetCurrentHash(64);
|
||||
Shake128 s128 => s128.GetCurrentHash(32),
|
||||
Shake256 s256 => s256.GetCurrentHash(64),
|
||||
#endif
|
||||
|
||||
case XxHash.XxHash32 xxh32:
|
||||
var xxh32Arr = xxh32.Finalize();
|
||||
Array.Reverse(xxh32Arr);
|
||||
return xxh32Arr;
|
||||
case XxHash.XxHash64 xxh64:
|
||||
var xxh64Arr = xxh64.Finalize();
|
||||
Array.Reverse(xxh64Arr);
|
||||
return xxh64Arr;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => null,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Current hash as a string
|
||||
/// </summary>
|
||||
public string? CurrentHashString
|
||||
public string? CurrentHashString => _hasher switch
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (_hasher)
|
||||
{
|
||||
case Crc cr:
|
||||
var crArr = cr.Finalize();
|
||||
ulong crHash = BytesToUInt64(crArr);
|
||||
int length = cr.Def.Width / 4 + (cr.Def.Width % 4 > 0 ? 1 : 0);
|
||||
return crHash.ToString($"x{length}");
|
||||
case IChecksum ic:
|
||||
return ic.End();
|
||||
// Needed due to variable bit widths
|
||||
Crc cr => GetCRCVariableLengthString(cr),
|
||||
|
||||
default:
|
||||
return ByteArrayToString(CurrentHashBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Needed due to Base64 text output
|
||||
SpamSum.SpamSum ss => GetSpamSumBase64String(ss),
|
||||
|
||||
// Everything else are direct conversions
|
||||
_ => ByteArrayToString(CurrentHashBytes),
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -111,7 +57,6 @@ namespace SabreTools.Hashing
|
||||
/// <summary>
|
||||
/// Internal hasher being used for processing
|
||||
/// </summary>
|
||||
/// <remarks>May be either a HashAlgorithm or NonCryptographicHashAlgorithm</remarks>
|
||||
private object? _hasher;
|
||||
|
||||
#endregion
|
||||
@@ -138,7 +83,7 @@ namespace SabreTools.Hashing
|
||||
HashType.Adler32 => new Adler32(),
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
HashType.BLAKE3 => new Blake3HashAlgorithm(),
|
||||
HashType.BLAKE3 => new Blake3.Blake3HashAlgorithm(),
|
||||
#endif
|
||||
|
||||
HashType.CRC1_ZERO => new Crc(StandardDefinitions.CRC1_ZERO),
|
||||
@@ -292,14 +237,14 @@ namespace SabreTools.Hashing
|
||||
HashType.FNV1a_32 => new FNV1a_32(),
|
||||
HashType.FNV1a_64 => new FNV1a_64(),
|
||||
|
||||
HashType.MD2 => new MessageDigest.MD2(),
|
||||
HashType.MD4 => new MessageDigest.MD4(),
|
||||
HashType.MD2 => new MD2(),
|
||||
HashType.MD4 => new MD4(),
|
||||
HashType.MD5 => MD5.Create(),
|
||||
|
||||
HashType.RIPEMD128 => new MessageDigest.RipeMD128(),
|
||||
HashType.RIPEMD160 => new MessageDigest.RipeMD160(),
|
||||
HashType.RIPEMD256 => new MessageDigest.RipeMD256(),
|
||||
HashType.RIPEMD320 => new MessageDigest.RipeMD320(),
|
||||
HashType.RIPEMD128 => new RipeMD128(),
|
||||
HashType.RIPEMD160 => new CryptographicHash.RipeMD160(),
|
||||
HashType.RIPEMD256 => new RipeMD256(),
|
||||
HashType.RIPEMD320 => new RipeMD320(),
|
||||
|
||||
HashType.SHA1 => SHA1.Create(),
|
||||
HashType.SHA256 => SHA256.Create(),
|
||||
@@ -313,26 +258,26 @@ namespace SabreTools.Hashing
|
||||
HashType.SHAKE256 => Shake256.IsSupported ? new Shake256() : null,
|
||||
#endif
|
||||
|
||||
HashType.SpamSum => new SpamSumContext(),
|
||||
HashType.SpamSum => new SpamSum.SpamSum(),
|
||||
|
||||
HashType.Tiger128_3 => new MessageDigest.Tiger128_3(),
|
||||
HashType.Tiger128_4 => new MessageDigest.Tiger128_4(),
|
||||
HashType.Tiger160_3 => new MessageDigest.Tiger160_3(),
|
||||
HashType.Tiger160_4 => new MessageDigest.Tiger160_4(),
|
||||
HashType.Tiger192_3 => new MessageDigest.Tiger192_3(),
|
||||
HashType.Tiger192_4 => new MessageDigest.Tiger192_4(),
|
||||
HashType.Tiger2_128_3 => new MessageDigest.Tiger2_128_3(),
|
||||
HashType.Tiger2_128_4 => new MessageDigest.Tiger2_128_4(),
|
||||
HashType.Tiger2_160_3 => new MessageDigest.Tiger2_160_3(),
|
||||
HashType.Tiger2_160_4 => new MessageDigest.Tiger2_160_4(),
|
||||
HashType.Tiger2_192_3 => new MessageDigest.Tiger2_192_3(),
|
||||
HashType.Tiger2_192_4 => new MessageDigest.Tiger2_192_4(),
|
||||
HashType.Tiger128_3 => new Tiger128_3(),
|
||||
HashType.Tiger128_4 => new Tiger128_4(),
|
||||
HashType.Tiger160_3 => new Tiger160_3(),
|
||||
HashType.Tiger160_4 => new Tiger160_4(),
|
||||
HashType.Tiger192_3 => new Tiger192_3(),
|
||||
HashType.Tiger192_4 => new Tiger192_4(),
|
||||
HashType.Tiger2_128_3 => new Tiger2_128_3(),
|
||||
HashType.Tiger2_128_4 => new Tiger2_128_4(),
|
||||
HashType.Tiger2_160_3 => new Tiger2_160_3(),
|
||||
HashType.Tiger2_160_4 => new Tiger2_160_4(),
|
||||
HashType.Tiger2_192_3 => new Tiger2_192_3(),
|
||||
HashType.Tiger2_192_4 => new Tiger2_192_4(),
|
||||
|
||||
HashType.XxHash32 => new XxHash.XxHash32(),
|
||||
HashType.XxHash64 => new XxHash.XxHash64(),
|
||||
HashType.XxHash32 => new XxHash32(),
|
||||
HashType.XxHash64 => new XxHash64(),
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
HashType.XxHash3 => new XxHash3(),
|
||||
HashType.XxHash128 => new XxHash128(),
|
||||
HashType.XxHash3 => new System.IO.Hashing.XxHash3(),
|
||||
HashType.XxHash128 => new System.IO.Hashing.XxHash128(),
|
||||
#endif
|
||||
_ => null,
|
||||
};
|
||||
@@ -356,26 +301,12 @@ namespace SabreTools.Hashing
|
||||
{
|
||||
switch (_hasher)
|
||||
{
|
||||
case ChecksumBase cb:
|
||||
cb.TransformBlock(buffer, offset, size);
|
||||
break;
|
||||
|
||||
case HashAlgorithm ha:
|
||||
ha.TransformBlock(buffer, offset, size, null, 0);
|
||||
break;
|
||||
|
||||
case IChecksum ic:
|
||||
byte[] icBlock = new byte[size];
|
||||
Array.Copy(buffer, offset, icBlock, 0, size);
|
||||
ic.Update(icBlock);
|
||||
break;
|
||||
|
||||
case MessageDigest.MessageDigestBase mdb:
|
||||
mdb.TransformBlock(buffer, offset, size);
|
||||
break;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
case NonCryptographicHashAlgorithm ncha:
|
||||
case System.IO.Hashing.NonCryptographicHashAlgorithm ncha:
|
||||
var nchaBufferSpan = new ReadOnlySpan<byte>(buffer, offset, size);
|
||||
ncha.Append(nchaBufferSpan);
|
||||
break;
|
||||
@@ -391,13 +322,6 @@ namespace SabreTools.Hashing
|
||||
s256.AppendData(s256BufferSpan);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case XxHash.XxHash32 xxh32:
|
||||
xxh32.TransformBlock(buffer, offset, size);
|
||||
break;
|
||||
case XxHash.XxHash64 xxh64:
|
||||
xxh64.TransformBlock(buffer, offset, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,13 +337,40 @@ namespace SabreTools.Hashing
|
||||
case HashAlgorithm ha:
|
||||
ha.TransformFinalBlock(emptyBuffer, 0, 0);
|
||||
break;
|
||||
|
||||
case MessageDigest.MessageDigestBase mdb:
|
||||
mdb.Terminate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the variable-length string representing a CRC value
|
||||
/// </summary>
|
||||
/// <param name="cr">Crc to get the value from</param>
|
||||
/// <returns>String representing the CRC, null on error</returns>
|
||||
private string? GetCRCVariableLengthString(Crc cr)
|
||||
{
|
||||
// Ignore null values
|
||||
if (cr.Hash == null)
|
||||
return null;
|
||||
|
||||
// Get the total number of characters needed
|
||||
ulong hash = BytesToUInt64(cr.Hash);
|
||||
int length = cr.Def.Width / 4 + (cr.Def.Width % 4 > 0 ? 1 : 0);
|
||||
return hash.ToString($"x{length}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Base64 representation of a SpamSum value
|
||||
/// </summary>
|
||||
/// <param name="ss">SpamSum to get the value from</param>
|
||||
/// <returns>String representing the SpamSum, null on error</returns>
|
||||
private string? GetSpamSumBase64String(SpamSum.SpamSum ss)
|
||||
{
|
||||
// Ignore null values
|
||||
if (ss.Hash == null)
|
||||
return null;
|
||||
|
||||
return System.Text.Encoding.ASCII.GetString(ss.Hash);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
43
SabreTools.Hashing/NonCryptographicHash/Constants.cs
Normal file
43
SabreTools.Hashing/NonCryptographicHash/Constants.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
#region FNV
|
||||
|
||||
public const uint FNV32Basis = 0x811c9dc5;
|
||||
public const ulong FNV64Basis = 0xcbf29ce484222325;
|
||||
|
||||
public const uint FNV32Prime = 0x01000193;
|
||||
public const ulong FNV64Prime = 0x00000100000001b3;
|
||||
|
||||
#endregion
|
||||
|
||||
#region xxHash-32
|
||||
|
||||
public const uint XXH_PRIME32_1 = 0x9E3779B1;
|
||||
|
||||
public const uint XXH_PRIME32_2 = 0x85EBCA77;
|
||||
|
||||
public const uint XXH_PRIME32_3 = 0xC2B2AE3D;
|
||||
|
||||
public const uint XXH_PRIME32_4 = 0x27D4EB2F;
|
||||
|
||||
public const uint XXH_PRIME32_5 = 0x165667B1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region xxHash-64
|
||||
|
||||
public const ulong XXH_PRIME64_1 = 0x9E3779B185EBCA87;
|
||||
|
||||
public const ulong XXH_PRIME64_2 = 0xC2B2AE3D27D4EB4F;
|
||||
|
||||
public const ulong XXH_PRIME64_3 = 0x165667B19E3779F9;
|
||||
|
||||
public const ulong XXH_PRIME64_4 = 0x85EBCA77C2B2AE63;
|
||||
|
||||
public const ulong XXH_PRIME64_5 = 0x27D4EB2F165667C5;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,21 @@
|
||||
using static SabreTools.Hashing.Checksum.Constants;
|
||||
using static SabreTools.Hashing.NonCryptographicHash.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.Checksum
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
public class FNV0_32 : FnvBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 32;
|
||||
|
||||
public FNV0_32()
|
||||
{
|
||||
_basis = 0;
|
||||
_prime = FNV32Prime;
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
for (int i = offset; length > 0; i++, length--)
|
||||
{
|
||||
@@ -1,18 +1,21 @@
|
||||
using static SabreTools.Hashing.Checksum.Constants;
|
||||
using static SabreTools.Hashing.NonCryptographicHash.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.Checksum
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
public class FNV0_64 : FnvBase<ulong>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 64;
|
||||
|
||||
public FNV0_64()
|
||||
{
|
||||
_basis = 0;
|
||||
_prime = FNV64Prime;
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
for (int i = offset; length > 0; i++, length--)
|
||||
{
|
||||
@@ -1,18 +1,21 @@
|
||||
using static SabreTools.Hashing.Checksum.Constants;
|
||||
using static SabreTools.Hashing.NonCryptographicHash.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.Checksum
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
public class FNV1_32 : FnvBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 32;
|
||||
|
||||
public FNV1_32()
|
||||
{
|
||||
_basis = FNV32Basis;
|
||||
_prime = FNV32Prime;
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
for (int i = offset; length > 0; i++, length--)
|
||||
{
|
||||
@@ -1,18 +1,21 @@
|
||||
using static SabreTools.Hashing.Checksum.Constants;
|
||||
using static SabreTools.Hashing.NonCryptographicHash.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.Checksum
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
public class FNV1_64 : FnvBase<ulong>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 64;
|
||||
|
||||
public FNV1_64()
|
||||
{
|
||||
_basis = FNV64Basis;
|
||||
_prime = FNV64Prime;
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
for (int i = offset; length > 0; i++, length--)
|
||||
{
|
||||
@@ -1,18 +1,21 @@
|
||||
using static SabreTools.Hashing.Checksum.Constants;
|
||||
using static SabreTools.Hashing.NonCryptographicHash.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.Checksum
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
public class FNV1a_32 : FnvBase<uint>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 32;
|
||||
|
||||
public FNV1a_32()
|
||||
{
|
||||
_basis = FNV32Basis;
|
||||
_prime = FNV32Prime;
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
for (int i = offset; length > 0; i++, length--)
|
||||
{
|
||||
@@ -1,18 +1,21 @@
|
||||
using static SabreTools.Hashing.Checksum.Constants;
|
||||
using static SabreTools.Hashing.NonCryptographicHash.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.Checksum
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
public class FNV1a_64 : FnvBase<ulong>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 64;
|
||||
|
||||
public FNV1a_64()
|
||||
{
|
||||
_basis = FNV64Basis;
|
||||
_prime = FNV64Prime;
|
||||
Reset();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void TransformBlock(byte[] data, int offset, int length)
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
{
|
||||
for (int i = offset; length > 0; i++, length--)
|
||||
{
|
||||
60
SabreTools.Hashing/NonCryptographicHash/FnvBase.cs
Normal file
60
SabreTools.Hashing/NonCryptographicHash/FnvBase.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// Common base class for FNV non-cryptographic hashes
|
||||
/// </summary>
|
||||
public abstract class FnvBase : System.Security.Cryptography.HashAlgorithm
|
||||
{
|
||||
// No common, untyped functionality
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Common base class for FNV non-cryptographic hashes
|
||||
/// </summary>
|
||||
public abstract class FnvBase<T> : FnvBase where T : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Initial value to use
|
||||
/// </summary>
|
||||
protected T _basis;
|
||||
|
||||
/// <summary>
|
||||
/// Round prime to use
|
||||
/// </summary>
|
||||
protected T _prime;
|
||||
|
||||
/// <summary>
|
||||
/// The current value of the hash
|
||||
/// </summary>
|
||||
protected T _hash;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
_hash = _basis;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
byte[] hashArr = _hash switch
|
||||
{
|
||||
short s => BitConverter.GetBytes(s),
|
||||
ushort s => BitConverter.GetBytes(s),
|
||||
|
||||
int i => BitConverter.GetBytes(i),
|
||||
uint i => BitConverter.GetBytes(i),
|
||||
|
||||
long l => BitConverter.GetBytes(l),
|
||||
ulong l => BitConverter.GetBytes(l),
|
||||
|
||||
_ => [],
|
||||
};
|
||||
|
||||
Array.Reverse(hashArr);
|
||||
return hashArr;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
SabreTools.Hashing/NonCryptographicHash/XxHash32.cs
Normal file
46
SabreTools.Hashing/NonCryptographicHash/XxHash32.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
public class XxHash32 : System.Security.Cryptography.HashAlgorithm
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 32;
|
||||
|
||||
/// <summary>
|
||||
/// The 32-bit seed to alter the hash result predictably.
|
||||
/// </summary>
|
||||
private readonly uint _seed;
|
||||
|
||||
/// <summary>
|
||||
/// Internal xxHash-32 state
|
||||
/// </summary>
|
||||
private readonly XxHash32State _state;
|
||||
|
||||
public XxHash32(uint seed = 0)
|
||||
{
|
||||
_seed = seed;
|
||||
_state = new XxHash32State();
|
||||
_state.Reset(seed);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
_state.Reset(_seed);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
=> _state.Update(data, offset, length);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
uint hash = _state.Digest();
|
||||
byte[] hashArr = BitConverter.GetBytes(hash);
|
||||
Array.Reverse(hashArr);
|
||||
return hashArr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using static SabreTools.Hashing.HashOperations;
|
||||
using static SabreTools.Hashing.XxHash.Constants;
|
||||
using static SabreTools.Hashing.NonCryptographicHash.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.XxHash
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// Structure for XXH32 streaming API.
|
||||
/// Structure for xxHash-32 streaming API.
|
||||
/// </summary>
|
||||
/// <see href="https://github.com/Cyan4973/xxHash/blob/dev/xxhash.h"/>
|
||||
internal class XXH32State
|
||||
internal class XxHash32State
|
||||
{
|
||||
/// <summary>
|
||||
/// Total length hashed, modulo 2^32
|
||||
46
SabreTools.Hashing/NonCryptographicHash/XxHash64.cs
Normal file
46
SabreTools.Hashing/NonCryptographicHash/XxHash64.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
public class XxHash64 : System.Security.Cryptography.HashAlgorithm
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override int HashSize => 64;
|
||||
|
||||
/// <summary>
|
||||
/// The 64-bit seed to alter the hash result predictably.
|
||||
/// </summary>
|
||||
private readonly uint _seed;
|
||||
|
||||
/// <summary>
|
||||
/// Internal xxHash-64 state
|
||||
/// </summary>
|
||||
private readonly XxHash64State _state;
|
||||
|
||||
public XxHash64(uint seed = 0)
|
||||
{
|
||||
_seed = seed;
|
||||
_state = new XxHash64State();
|
||||
_state.Reset(seed);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
_state.Reset(_seed);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void HashCore(byte[] data, int offset, int length)
|
||||
=> _state.Update(data, offset, length);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
ulong hash = _state.Digest();
|
||||
byte[] hashArr = BitConverter.GetBytes(hash);
|
||||
Array.Reverse(hashArr);
|
||||
return hashArr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
using System;
|
||||
using static SabreTools.Hashing.HashOperations;
|
||||
using static SabreTools.Hashing.XxHash.Constants;
|
||||
using static SabreTools.Hashing.XxHash.Utility;
|
||||
using static SabreTools.Hashing.NonCryptographicHash.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.XxHash
|
||||
namespace SabreTools.Hashing.NonCryptographicHash
|
||||
{
|
||||
/// <summary>
|
||||
/// Structure for XXH64 streaming API.
|
||||
/// Structure for xxHash-64 streaming API.
|
||||
/// </summary>
|
||||
/// <see href="https://github.com/Cyan4973/xxHash/blob/dev/xxhash.h"/>
|
||||
internal class XXH64State
|
||||
internal class XxHash64State
|
||||
{
|
||||
/// <summary>
|
||||
/// Total length hashed. This is always 64-bit.
|
||||
@@ -198,7 +197,23 @@ namespace SabreTools.Hashing.XxHash
|
||||
--length;
|
||||
}
|
||||
|
||||
return XXH64Avalanche(hash);
|
||||
return Avalanche(hash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mixes all bits to finalize the hash.
|
||||
///
|
||||
/// The final mix ensures that all input bits have a chance to impact any bit in
|
||||
/// the output digest, resulting in an unbiased distribution.
|
||||
/// </summary>
|
||||
private static ulong Avalanche(ulong hash)
|
||||
{
|
||||
hash ^= hash >> 33;
|
||||
hash *= XXH_PRIME64_2;
|
||||
hash ^= hash >> 29;
|
||||
hash *= XXH_PRIME64_3;
|
||||
hash ^= hash >> 32;
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,11 @@
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>1.4.1</Version>
|
||||
<Version>1.4.2</Version>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Copyright>Copyright (c)2016-2024 Matt Nadareski</Copyright>
|
||||
<Copyright>Copyright (c)2016-2025 Matt Nadareski</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/SabreTools/SabreTools.Hashing</RepositoryUrl>
|
||||
|
||||
45
SabreTools.Hashing/SpamSum/BlockhashContext.cs
Normal file
45
SabreTools.Hashing/SpamSum/BlockhashContext.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using static SabreTools.Hashing.SpamSum.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.SpamSum
|
||||
{
|
||||
/// <summary>
|
||||
/// A blockhash contains a signature state for a specific (implicit) blocksize.
|
||||
/// The blocksize is given by <see cref="SSDEEP_BS(uint)"/>
|
||||
/// </summary>
|
||||
/// <see href="https://github.com/ssdeep-project/ssdeep/blob/master/fuzzy.c"/>
|
||||
internal class BlockhashContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Current digest length
|
||||
/// </summary>
|
||||
public uint DIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current message digest
|
||||
/// </summary>
|
||||
public byte[] Digest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest value at <see cref="HalfH"/>
|
||||
/// </summary>
|
||||
public byte HalfDigest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Partial FNV hash
|
||||
/// </summary>
|
||||
public byte H { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Partial FNV hash reset after <see cref="Digest"/> is
|
||||
/// <see cref="SPAMSUM_LENGTH"/> / 2 long. This is needed
|
||||
/// to be able to truncate digest for the second output hash
|
||||
/// to stay compatible with ssdeep output.
|
||||
/// </summary>
|
||||
public byte HalfH { get; set; }
|
||||
|
||||
public BlockhashContext()
|
||||
{
|
||||
Digest = new byte[SPAMSUM_LENGTH];
|
||||
}
|
||||
}
|
||||
}
|
||||
446
SabreTools.Hashing/SpamSum/Constants.cs
Normal file
446
SabreTools.Hashing/SpamSum/Constants.cs
Normal file
@@ -0,0 +1,446 @@
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.Hashing.SpamSum
|
||||
{
|
||||
/// <see href="github.com/ssdeep-project/ssdeep/blob/master/fuzzy.c"/>
|
||||
/// <see href="github.com/ssdeep-project/ssdeep/blob/master/fuzzy.h"/>
|
||||
internal static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// fuzzy_digest flag indicating to eliminate sequences of more than
|
||||
/// three identical characters
|
||||
/// </summary>
|
||||
public const uint FUZZY_FLAG_ELIMSEQ = 1;
|
||||
|
||||
/// <summary>
|
||||
/// fuzzy_digest flag indicating not to truncate the second part to
|
||||
/// SPAMSUM_LENGTH / 2 characters.
|
||||
/// </summary>
|
||||
public const uint FUZZY_FLAG_NOTRUNC = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Length of an individual fuzzy hash signature component.
|
||||
/// </summary>
|
||||
public const int SPAMSUM_LENGTH = 64;
|
||||
|
||||
/// <summary>
|
||||
/// The longest possible length for a fuzzy hash signature
|
||||
/// (without the filename)
|
||||
/// </summary>
|
||||
public const int FUZZY_MAX_RESULT = 2 * SPAMSUM_LENGTH + 20;
|
||||
|
||||
public const uint ROLLING_WINDOW = 7;
|
||||
|
||||
public const uint MIN_BLOCKSIZE = 3;
|
||||
|
||||
public const byte HASH_INIT = 0x27;
|
||||
|
||||
public const int NUM_BLOCKHASHES = 31;
|
||||
|
||||
public const uint FUZZY_STATE_NEED_LASTHASH = 1;
|
||||
|
||||
public const uint FUZZY_STATE_SIZE_FIXED = 2;
|
||||
|
||||
public static uint SSDEEP_BS(uint index) => MIN_BLOCKSIZE << (int)index;
|
||||
|
||||
public static ulong SSDEEP_TOTAL_SIZE_MAX
|
||||
=> (ulong)SSDEEP_BS(NUM_BLOCKHASHES - 1) * SPAMSUM_LENGTH;
|
||||
|
||||
public static readonly byte[] B64 = Encoding.ASCII.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
|
||||
|
||||
#region Precomputed Tables
|
||||
|
||||
/// <summary>
|
||||
/// Precomputed patrial FNV hash table
|
||||
/// </summary>
|
||||
public static readonly byte[][] SUM_TABLE = // [64][64]
|
||||
[
|
||||
[ // 0x00
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
],
|
||||
[ // 0x01
|
||||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c,
|
||||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c,
|
||||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
],
|
||||
[ // 0x02
|
||||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29,
|
||||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39,
|
||||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09,
|
||||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19,
|
||||
],
|
||||
[ // 0x03
|
||||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36,
|
||||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26,
|
||||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16,
|
||||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06,
|
||||
],
|
||||
[ // 0x04
|
||||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03,
|
||||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13,
|
||||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23,
|
||||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33,
|
||||
],
|
||||
[ // 0x05
|
||||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,
|
||||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,
|
||||
],
|
||||
[ // 0x06
|
||||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d,
|
||||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d,
|
||||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d,
|
||||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d,
|
||||
],
|
||||
[ // 0x07
|
||||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a,
|
||||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a,
|
||||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a,
|
||||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a,
|
||||
],
|
||||
[ // 0x08
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
],
|
||||
[ // 0x09
|
||||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24,
|
||||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34,
|
||||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04,
|
||||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14,
|
||||
],
|
||||
[ // 0x0a
|
||||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31,
|
||||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21,
|
||||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11,
|
||||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01,
|
||||
],
|
||||
[ // 0x0b
|
||||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e,
|
||||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e,
|
||||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e,
|
||||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e,
|
||||
],
|
||||
[ // 0x0c
|
||||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b,
|
||||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b,
|
||||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b,
|
||||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b,
|
||||
],
|
||||
[ // 0x0d
|
||||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38,
|
||||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
],
|
||||
[ // 0x0e
|
||||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05,
|
||||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15,
|
||||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25,
|
||||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35,
|
||||
],
|
||||
[ // 0x0f
|
||||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12,
|
||||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02,
|
||||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32,
|
||||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22,
|
||||
],
|
||||
[ // 0x10
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
],
|
||||
[ // 0x11
|
||||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c,
|
||||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c,
|
||||
],
|
||||
[ // 0x12
|
||||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19,
|
||||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09,
|
||||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39,
|
||||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29,
|
||||
],
|
||||
[ // 0x13
|
||||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26,
|
||||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36,
|
||||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06,
|
||||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16,
|
||||
],
|
||||
[ // 0x14
|
||||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33,
|
||||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23,
|
||||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13,
|
||||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03,
|
||||
],
|
||||
[ // 0x15
|
||||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,
|
||||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,
|
||||
],
|
||||
[ // 0x16
|
||||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d,
|
||||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d,
|
||||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d,
|
||||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d,
|
||||
],
|
||||
[ // 0x17
|
||||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a,
|
||||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a,
|
||||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a,
|
||||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a,
|
||||
],
|
||||
[ // 0x18
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
],
|
||||
[ // 0x19
|
||||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14,
|
||||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04,
|
||||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34,
|
||||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24,
|
||||
],
|
||||
[ // 0x1a
|
||||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21,
|
||||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31,
|
||||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01,
|
||||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11,
|
||||
],
|
||||
[ // 0x1b
|
||||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e,
|
||||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e,
|
||||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e,
|
||||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e,
|
||||
],
|
||||
[ // 0x1c
|
||||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b,
|
||||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b,
|
||||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b,
|
||||
],
|
||||
[ // 0x1d
|
||||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28,
|
||||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
|
||||
],
|
||||
[ // 0x1e
|
||||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35,
|
||||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25,
|
||||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15,
|
||||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05,
|
||||
],
|
||||
[ // 0x1f
|
||||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02,
|
||||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12,
|
||||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22,
|
||||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32,
|
||||
],
|
||||
[ // 0x20
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
],
|
||||
[ // 0x21
|
||||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c,
|
||||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c,
|
||||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
],
|
||||
[ // 0x22
|
||||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09,
|
||||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19,
|
||||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29,
|
||||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39,
|
||||
],
|
||||
[ // 0x23
|
||||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16,
|
||||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06,
|
||||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36,
|
||||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26,
|
||||
],
|
||||
[ // 0x24
|
||||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23,
|
||||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33,
|
||||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03,
|
||||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13,
|
||||
],
|
||||
[ // 0x25
|
||||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,
|
||||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,
|
||||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
],
|
||||
[ // 0x26
|
||||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d,
|
||||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d,
|
||||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d,
|
||||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d,
|
||||
],
|
||||
[ // 0x27
|
||||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a,
|
||||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a,
|
||||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a,
|
||||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a,
|
||||
],
|
||||
[ // 0x28
|
||||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
],
|
||||
[ // 0x29
|
||||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04,
|
||||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14,
|
||||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24,
|
||||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34,
|
||||
],
|
||||
[ // 0x2a
|
||||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11,
|
||||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01,
|
||||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31,
|
||||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21,
|
||||
],
|
||||
[ // 0x2b
|
||||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e,
|
||||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e,
|
||||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e,
|
||||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e,
|
||||
],
|
||||
[ // 0x2c
|
||||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b,
|
||||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b,
|
||||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b,
|
||||
],
|
||||
[ // 0x2d
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38,
|
||||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28,
|
||||
],
|
||||
[ // 0x2e
|
||||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25,
|
||||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35,
|
||||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05,
|
||||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15,
|
||||
],
|
||||
[ // 0x2f
|
||||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32,
|
||||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22,
|
||||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12,
|
||||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02,
|
||||
],
|
||||
[ // 0x30
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
],
|
||||
[ // 0x31
|
||||
0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24, 0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34, 0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c,
|
||||
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c,
|
||||
],
|
||||
[ // 0x32
|
||||
0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39,
|
||||
0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29,
|
||||
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19,
|
||||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09,
|
||||
],
|
||||
[ // 0x33
|
||||
0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06,
|
||||
0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e, 0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16,
|
||||
0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e, 0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26,
|
||||
0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e, 0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36,
|
||||
],
|
||||
[ // 0x34
|
||||
0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b, 0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13,
|
||||
0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03,
|
||||
0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b, 0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33,
|
||||
0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b, 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23,
|
||||
],
|
||||
[ // 0x35
|
||||
0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,
|
||||
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,
|
||||
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
],
|
||||
[ // 0x36
|
||||
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d,
|
||||
0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d,
|
||||
0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25, 0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d,
|
||||
0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35, 0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d,
|
||||
],
|
||||
[ // 0x37
|
||||
0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12, 0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a,
|
||||
0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02, 0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a,
|
||||
0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32, 0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a,
|
||||
0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22, 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a,
|
||||
],
|
||||
[ // 0x38
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
],
|
||||
[ // 0x39
|
||||
0x3b, 0x3a, 0x39, 0x38, 0x3f, 0x3e, 0x3d, 0x3c, 0x33, 0x32, 0x31, 0x30, 0x37, 0x36, 0x35, 0x34,
|
||||
0x2b, 0x2a, 0x29, 0x28, 0x2f, 0x2e, 0x2d, 0x2c, 0x23, 0x22, 0x21, 0x20, 0x27, 0x26, 0x25, 0x24,
|
||||
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14,
|
||||
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04,
|
||||
],
|
||||
[ // 0x3a
|
||||
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01,
|
||||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11,
|
||||
0x2e, 0x2f, 0x2c, 0x2d, 0x2a, 0x2b, 0x28, 0x29, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21,
|
||||
0x3e, 0x3f, 0x3c, 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31,
|
||||
],
|
||||
[ // 0x3b
|
||||
0x21, 0x20, 0x23, 0x22, 0x25, 0x24, 0x27, 0x26, 0x29, 0x28, 0x2b, 0x2a, 0x2d, 0x2c, 0x2f, 0x2e,
|
||||
0x31, 0x30, 0x33, 0x32, 0x35, 0x34, 0x37, 0x36, 0x39, 0x38, 0x3b, 0x3a, 0x3d, 0x3c, 0x3f, 0x3e,
|
||||
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e,
|
||||
0x11, 0x10, 0x13, 0x12, 0x15, 0x14, 0x17, 0x16, 0x19, 0x18, 0x1b, 0x1a, 0x1d, 0x1c, 0x1f, 0x1e,
|
||||
],
|
||||
[ // 0x3c
|
||||
0x34, 0x35, 0x36, 0x37, 0x30, 0x31, 0x32, 0x33, 0x3c, 0x3d, 0x3e, 0x3f, 0x38, 0x39, 0x3a, 0x3b,
|
||||
0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f, 0x28, 0x29, 0x2a, 0x2b,
|
||||
0x14, 0x15, 0x16, 0x17, 0x10, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x1f, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b,
|
||||
],
|
||||
[ // 0x3d
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
|
||||
0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28,
|
||||
0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38,
|
||||
],
|
||||
[ // 0x3e
|
||||
0x1a, 0x1b, 0x18, 0x19, 0x1e, 0x1f, 0x1c, 0x1d, 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15,
|
||||
0x0a, 0x0b, 0x08, 0x09, 0x0e, 0x0f, 0x0c, 0x0d, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05,
|
||||
0x3a, 0x3b, 0x38, 0x39, 0x3e, 0x3f, 0x3c, 0x3d, 0x32, 0x33, 0x30, 0x31, 0x36, 0x37, 0x34, 0x35,
|
||||
0x2a, 0x2b, 0x28, 0x29, 0x2e, 0x2f, 0x2c, 0x2d, 0x22, 0x23, 0x20, 0x21, 0x26, 0x27, 0x24, 0x25,
|
||||
],
|
||||
[ // 0x3f
|
||||
0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, 0x25, 0x24, 0x27, 0x26, 0x21, 0x20, 0x23, 0x22,
|
||||
0x3d, 0x3c, 0x3f, 0x3e, 0x39, 0x38, 0x3b, 0x3a, 0x35, 0x34, 0x37, 0x36, 0x31, 0x30, 0x33, 0x32,
|
||||
0x0d, 0x0c, 0x0f, 0x0e, 0x09, 0x08, 0x0b, 0x0a, 0x05, 0x04, 0x07, 0x06, 0x01, 0x00, 0x03, 0x02,
|
||||
0x1d, 0x1c, 0x1f, 0x1e, 0x19, 0x18, 0x1b, 0x1a, 0x15, 0x14, 0x17, 0x16, 0x11, 0x10, 0x13, 0x12,
|
||||
],
|
||||
];
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
91
SabreTools.Hashing/SpamSum/FuzzyState.cs
Normal file
91
SabreTools.Hashing/SpamSum/FuzzyState.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using static SabreTools.Hashing.SpamSum.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.SpamSum
|
||||
{
|
||||
/// <see href="https://github.com/ssdeep-project/ssdeep/blob/master/fuzzy.c"/>
|
||||
internal class FuzzyState
|
||||
{
|
||||
public ulong TotalSize { get; set; }
|
||||
|
||||
public ulong FixedSize { get; set; }
|
||||
|
||||
public ulong ReduceBorder { get; set; }
|
||||
|
||||
public uint BHStart { get; set; }
|
||||
|
||||
public uint BHEnd { get; set; }
|
||||
|
||||
public uint BHEndLimit { get; set; }
|
||||
|
||||
public uint Flags { get; set; }
|
||||
|
||||
public uint RollMask { get; set; }
|
||||
|
||||
public BlockhashContext[] BH { get; set; }
|
||||
|
||||
public RollState Roll { get; set; }
|
||||
|
||||
public byte LastH { get; set; }
|
||||
|
||||
public FuzzyState()
|
||||
{
|
||||
BH = new BlockhashContext[NUM_BLOCKHASHES];
|
||||
for (int i = 0; i < NUM_BLOCKHASHES; i++)
|
||||
{
|
||||
BH[i] = new BlockhashContext();
|
||||
}
|
||||
|
||||
Roll = new RollState();
|
||||
}
|
||||
|
||||
public void TryForkBlockhash()
|
||||
{
|
||||
uint obh, nbh;
|
||||
if (BHEnd <= 0)
|
||||
throw new Exception("assert(BHEnd > 0)");
|
||||
|
||||
obh = BHEnd - 1;
|
||||
if (BHEnd <= BHEndLimit)
|
||||
{
|
||||
nbh = obh + 1;
|
||||
BH[nbh].H = BH[obh].H;
|
||||
BH[nbh].HalfH = BH[obh].HalfH;
|
||||
BH[nbh].Digest[0] = 0x00;
|
||||
BH[nbh].HalfDigest = 0x00;
|
||||
BH[nbh].DIndex = 0;
|
||||
++BHEnd;
|
||||
}
|
||||
else if (BHEnd == NUM_BLOCKHASHES
|
||||
&& ((Flags & FUZZY_STATE_NEED_LASTHASH) == 0))
|
||||
{
|
||||
Flags |= FUZZY_STATE_NEED_LASTHASH;
|
||||
LastH = BH[obh].H;
|
||||
}
|
||||
}
|
||||
|
||||
public void TryReduceBlockhash()
|
||||
{
|
||||
if (BHStart >= BHEnd)
|
||||
throw new Exception("assert(BHStart < BHEnd)");
|
||||
|
||||
// Need at least two working hashes.
|
||||
if (BHEnd - BHStart < 2)
|
||||
return;
|
||||
|
||||
// Initial blocksize estimate would select this or a smaller blocksize.
|
||||
if (ReduceBorder >= (((Flags & FUZZY_STATE_SIZE_FIXED) != 0) ? FixedSize : TotalSize))
|
||||
return;
|
||||
|
||||
// Estimate adjustment would select this blocksize.
|
||||
if (BH[BHStart + 1].DIndex < SPAMSUM_LENGTH / 2)
|
||||
return;
|
||||
|
||||
// At this point we are clearly no longer interested in the
|
||||
// start_blocksize. Get rid of it.
|
||||
++BHStart;
|
||||
ReduceBorder *= 2;
|
||||
RollMask = RollMask * 2 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
56
SabreTools.Hashing/SpamSum/RollState.cs
Normal file
56
SabreTools.Hashing/SpamSum/RollState.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using static SabreTools.Hashing.SpamSum.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.SpamSum
|
||||
{
|
||||
/// <see href="https://github.com/ssdeep-project/ssdeep/blob/master/fuzzy.c"/>
|
||||
internal class RollState
|
||||
{
|
||||
public byte[] Window { get; set; }
|
||||
|
||||
public uint H1 { get; set; }
|
||||
|
||||
public uint H2 { get; set; }
|
||||
|
||||
public uint H3 { get; set; }
|
||||
|
||||
public uint N { get; set; }
|
||||
|
||||
public RollState()
|
||||
{
|
||||
Window = new byte[ROLLING_WINDOW];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A rolling hash, based on the Adler checksum. By using a rolling hash
|
||||
/// we can perform auto resynchronisation after inserts/deletes.
|
||||
///
|
||||
/// Internally, H1 is the sum of the bytes in the window and H2
|
||||
/// is the sum of the bytes times the index.
|
||||
///
|
||||
/// H3 is a shift/xor based rolling hash, and is mostly needed to ensure that
|
||||
/// we can cope with large blocksize values.
|
||||
/// </summary>
|
||||
public void RollHash(byte c)
|
||||
{
|
||||
H2 -= H1;
|
||||
H2 += ROLLING_WINDOW * c;
|
||||
|
||||
H1 += c;
|
||||
H1 -= Window[N % ROLLING_WINDOW];
|
||||
|
||||
Window[N % ROLLING_WINDOW] = c;
|
||||
N++;
|
||||
|
||||
// The original spamsum AND'ed this value with 0xFFFFFFFF which
|
||||
// in theory should have no effect. This AND has been removed
|
||||
// for performance (jk)
|
||||
H3 <<= 5;
|
||||
H3 ^= c;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the current rolling sum
|
||||
/// </summary>
|
||||
public uint RollSum() => H1 + H2 + H3;
|
||||
}
|
||||
}
|
||||
356
SabreTools.Hashing/SpamSum/SpamSum.cs
Normal file
356
SabreTools.Hashing/SpamSum/SpamSum.cs
Normal file
@@ -0,0 +1,356 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using static SabreTools.Hashing.SpamSum.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.SpamSum
|
||||
{
|
||||
/// <see href="https://github.com/ssdeep-project/ssdeep/blob/master/fuzzy.c"/>
|
||||
public class SpamSum : System.Security.Cryptography.HashAlgorithm
|
||||
{
|
||||
private FuzzyState _state;
|
||||
|
||||
public SpamSum()
|
||||
{
|
||||
_state = new();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
_state = new FuzzyState
|
||||
{
|
||||
BHStart = 0,
|
||||
BHEnd = 1,
|
||||
BHEndLimit = NUM_BLOCKHASHES - 1,
|
||||
TotalSize = 0,
|
||||
ReduceBorder = MIN_BLOCKSIZE * SPAMSUM_LENGTH,
|
||||
Flags = 0,
|
||||
RollMask = 0,
|
||||
};
|
||||
|
||||
_state.BH[0].H = HASH_INIT;
|
||||
_state.BH[0].HalfH = HASH_INIT;
|
||||
_state.BH[0].Digest[0] = 0x00;
|
||||
_state.BH[0].HalfDigest = 0x00;
|
||||
_state.BH[0].DIndex = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void HashCore(byte[] array, int ibStart, int cbSize)
|
||||
{
|
||||
_state.TotalSize += (ulong)cbSize;
|
||||
for (int i = ibStart; i < cbSize; i++)
|
||||
{
|
||||
ProcessByte(array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
string? digest = Finalize(0);
|
||||
if (digest == null)
|
||||
return [];
|
||||
|
||||
return Encoding.ASCII.GetBytes(digest.TrimEnd('\0'));
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Originally named `fuzzy_engine_step`
|
||||
/// </remarks>
|
||||
private void ProcessByte(byte c)
|
||||
{
|
||||
// At each character we update the rolling hash and the normal hashes.
|
||||
// When the rolling hash hits a reset value then we emit a normal hash
|
||||
// as a element of the signature and reset the normal hash.
|
||||
_state.Roll.RollHash(c);
|
||||
uint horg = _state.Roll.RollSum() + 1;
|
||||
uint h = horg / MIN_BLOCKSIZE;
|
||||
|
||||
uint i;
|
||||
for (i = _state.BHStart; i < _state.BHEnd; ++i)
|
||||
{
|
||||
_state.BH[i].H = SumHash(c, _state.BH[i].H);
|
||||
_state.BH[i].HalfH = SumHash(c, _state.BH[i].HalfH);
|
||||
}
|
||||
|
||||
if ((_state.Flags & FUZZY_STATE_NEED_LASTHASH) != 0)
|
||||
_state.LastH = SumHash(c, _state.LastH);
|
||||
|
||||
// 0xffffffff !== -1 (mod 3)
|
||||
if (horg == 0)
|
||||
return;
|
||||
|
||||
// With growing blocksize almost no runs fail the next test.
|
||||
if ((h & _state.RollMask) != 0)
|
||||
return;
|
||||
|
||||
// Delay computation of modulo as possible.
|
||||
if ((horg % MIN_BLOCKSIZE) != 0)
|
||||
return;
|
||||
|
||||
h >>= (int)_state.BHStart;
|
||||
i = _state.BHStart;
|
||||
do
|
||||
{
|
||||
// We have hit a reset point. We now emit hashes which are
|
||||
// based on all characters in the piece of the message between
|
||||
// the last reset point and this one
|
||||
if (_state.BH[i].DIndex == 0)
|
||||
{
|
||||
// Can only happen 30 times.
|
||||
// First step for this blocksize. Clone next.
|
||||
_state.TryForkBlockhash();
|
||||
}
|
||||
|
||||
_state.BH[i].Digest[_state.BH[i].DIndex] = B64[_state.BH[i].H];
|
||||
_state.BH[i].HalfDigest = B64[_state.BH[i].HalfH];
|
||||
|
||||
if (_state.BH[i].DIndex < SPAMSUM_LENGTH - 1)
|
||||
{
|
||||
// We can have a problem with the tail overflowing. The
|
||||
// easiest way to cope with this is to only reset the
|
||||
// normal hash if we have room for more characters in
|
||||
// our signature. This has the effect of combining the
|
||||
// last few pieces of the message into a single piece
|
||||
_state.BH[i].Digest[++_state.BH[i].DIndex] = 0x00;
|
||||
_state.BH[i].H = HASH_INIT;
|
||||
if (_state.BH[i].DIndex < SPAMSUM_LENGTH / 2)
|
||||
{
|
||||
_state.BH[i].HalfH = HASH_INIT;
|
||||
_state.BH[i].HalfDigest = 0x00;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_state.TryReduceBlockhash();
|
||||
}
|
||||
|
||||
if ((h & 1) != 0)
|
||||
break;
|
||||
|
||||
h >>= 1;
|
||||
} while (++i < _state.BHEnd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A simple non-rolling hash, based on the FNV hash
|
||||
/// </summary>
|
||||
private static byte SumHash(byte c, byte h) => SUM_TABLE[h][c & 0x3f];
|
||||
|
||||
/// <remarks>
|
||||
/// Originally named `fuzzy_digest`
|
||||
/// </remarks>
|
||||
private string? Finalize(uint flags)
|
||||
{
|
||||
uint bi = _state.BHStart;
|
||||
uint h = _state.Roll.RollSum();
|
||||
int i;
|
||||
|
||||
// Exclude terminating '\0'.
|
||||
int remain = FUZZY_MAX_RESULT - 1;
|
||||
|
||||
// Verify that our elimination was not overeager.
|
||||
if (bi != 0 && (ulong)SSDEEP_BS(bi) / 2 * SPAMSUM_LENGTH >= _state.TotalSize)
|
||||
return null;
|
||||
|
||||
// The input exceeds data types.
|
||||
if (_state.TotalSize > SSDEEP_TOTAL_SIZE_MAX)
|
||||
return null;
|
||||
|
||||
// Initial blocksize guess.
|
||||
while ((ulong)SSDEEP_BS(bi) * SPAMSUM_LENGTH < _state.TotalSize)
|
||||
{
|
||||
++bi;
|
||||
}
|
||||
|
||||
// Adapt blocksize guess to actual digest length.
|
||||
if (bi >= _state.BHEnd)
|
||||
bi = _state.BHEnd - 1;
|
||||
|
||||
while (bi > _state.BHStart && _state.BH[bi].DIndex < SPAMSUM_LENGTH / 2)
|
||||
{
|
||||
--bi;
|
||||
}
|
||||
|
||||
if (bi > 0 && _state.BH[bi].DIndex < SPAMSUM_LENGTH / 2)
|
||||
return null;
|
||||
|
||||
byte[] result = new byte[FUZZY_MAX_RESULT];
|
||||
int resultPtr = 0;
|
||||
|
||||
string prefixStr = $"{(ulong)SSDEEP_BS(bi)}:";
|
||||
byte[] prefixArr = Encoding.ASCII.GetBytes(prefixStr);
|
||||
Array.Copy(prefixArr, result, prefixArr.Length);
|
||||
|
||||
i = prefixArr.Length;
|
||||
if (i >= remain)
|
||||
return null;
|
||||
|
||||
remain -= i;
|
||||
resultPtr += i;
|
||||
|
||||
i = (int)_state.BH[bi].DIndex;
|
||||
if (i > remain)
|
||||
return null;
|
||||
|
||||
if ((flags & FUZZY_FLAG_ELIMSEQ) != 0)
|
||||
i = EliminateSequences(result, resultPtr, _state.BH[bi].Digest, 0, i);
|
||||
else
|
||||
Array.Copy(_state.BH[bi].Digest, 0, result, resultPtr, i);
|
||||
|
||||
resultPtr += i;
|
||||
remain -= i;
|
||||
if (h != 0)
|
||||
{
|
||||
if (remain <= 0)
|
||||
return null;
|
||||
|
||||
result[resultPtr] = B64[_state.BH[bi].H];
|
||||
if ((flags & FUZZY_FLAG_ELIMSEQ) == 0
|
||||
|| i < 3
|
||||
|| result[resultPtr] != result[resultPtr - 1]
|
||||
|| result[resultPtr] != result[resultPtr - 2]
|
||||
|| result[resultPtr] != result[resultPtr - 3])
|
||||
{
|
||||
++resultPtr;
|
||||
--remain;
|
||||
}
|
||||
}
|
||||
else if (_state.BH[bi].Digest[_state.BH[bi].DIndex] != '\0')
|
||||
{
|
||||
if (remain <= 0)
|
||||
return null;
|
||||
|
||||
result[resultPtr] = _state.BH[bi].Digest[_state.BH[bi].DIndex];
|
||||
if ((flags & FUZZY_FLAG_ELIMSEQ) == 0
|
||||
|| i < 3
|
||||
|| result[resultPtr] != result[resultPtr - 1]
|
||||
|| result[resultPtr] != result[resultPtr - 2]
|
||||
|| result[resultPtr] != result[resultPtr - 3])
|
||||
{
|
||||
++resultPtr;
|
||||
--remain;
|
||||
}
|
||||
}
|
||||
|
||||
if (remain <= 0)
|
||||
return null;
|
||||
|
||||
result[resultPtr++] = (byte)':';
|
||||
--remain;
|
||||
|
||||
if (bi < _state.BHEnd - 1)
|
||||
{
|
||||
++bi;
|
||||
i = (int)_state.BH[bi].DIndex;
|
||||
if ((flags & FUZZY_FLAG_NOTRUNC) == 0 && i > SPAMSUM_LENGTH / 2 - 1)
|
||||
i = SPAMSUM_LENGTH / 2 - 1;
|
||||
|
||||
if (i > remain)
|
||||
return null;
|
||||
|
||||
if ((flags & FUZZY_FLAG_ELIMSEQ) != 0)
|
||||
i = EliminateSequences(result, resultPtr, _state.BH[bi].Digest, 0, i);
|
||||
else
|
||||
Array.Copy(_state.BH[bi].Digest, 0, result, resultPtr, i);
|
||||
|
||||
resultPtr += i;
|
||||
remain -= i;
|
||||
if (h != 0)
|
||||
{
|
||||
if (remain <= 0)
|
||||
return null;
|
||||
|
||||
h = (flags & FUZZY_FLAG_NOTRUNC) != 0
|
||||
? _state.BH[bi].H
|
||||
: _state.BH[bi].HalfH;
|
||||
|
||||
result[resultPtr] = B64[h];
|
||||
if ((flags & FUZZY_FLAG_ELIMSEQ) == 0
|
||||
|| i < 3
|
||||
|| result[resultPtr] != result[resultPtr - 1]
|
||||
|| result[resultPtr] != result[resultPtr - 2]
|
||||
|| result[resultPtr] != result[resultPtr - 3])
|
||||
{
|
||||
++resultPtr;
|
||||
--remain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i = (flags & FUZZY_FLAG_NOTRUNC) != 0
|
||||
? _state.BH[bi].Digest[_state.BH[bi].DIndex]
|
||||
: _state.BH[bi].HalfDigest;
|
||||
|
||||
if (i != 0x00)
|
||||
{
|
||||
if (remain <= 0)
|
||||
return null;
|
||||
|
||||
result[resultPtr] = (byte)i;
|
||||
if ((flags & FUZZY_FLAG_ELIMSEQ) == 0
|
||||
|| i < 3
|
||||
|| result[resultPtr] != result[resultPtr - 1]
|
||||
|| result[resultPtr] != result[resultPtr - 2]
|
||||
|| result[resultPtr] != result[resultPtr - 3])
|
||||
{
|
||||
++resultPtr;
|
||||
--remain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (h != 0)
|
||||
{
|
||||
if (bi != 0 && bi != NUM_BLOCKHASHES - 1)
|
||||
return null;
|
||||
if (remain <= 0)
|
||||
return null;
|
||||
|
||||
if (bi == 0)
|
||||
result[resultPtr++] = B64[_state.BH[bi].H];
|
||||
else
|
||||
result[resultPtr++] = B64[_state.LastH];
|
||||
|
||||
/* No need to bother with FUZZY_FLAG_ELIMSEQ, because this
|
||||
* digest has length 1. */
|
||||
--remain;
|
||||
}
|
||||
|
||||
result[resultPtr] = 0x00;
|
||||
return Encoding.ASCII.GetString(result);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Originally named `memcpy_eliminate_sequences`
|
||||
/// </remarks>
|
||||
private static int EliminateSequences(byte[] dst, int dstPtr, byte[] src, int srcPtr, int n)
|
||||
{
|
||||
int srcend = srcPtr + n;
|
||||
if (n < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(n));
|
||||
|
||||
if (srcPtr < srcend) dst[dstPtr++] = src[srcPtr++];
|
||||
if (srcPtr < srcend) dst[dstPtr++] = src[srcPtr++];
|
||||
if (srcPtr < srcend) dst[dstPtr++] = src[srcPtr++];
|
||||
while (srcPtr < srcend)
|
||||
{
|
||||
if (src[srcPtr] == dst[dstPtr - 1]
|
||||
&& src[srcPtr] == dst[dstPtr - 2]
|
||||
&& src[srcPtr] == dst[dstPtr - 3])
|
||||
{
|
||||
++srcPtr;
|
||||
--n;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[dstPtr++] = src[srcPtr++];
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,34 +13,6 @@ namespace SabreTools.Hashing.XxHash
|
||||
|
||||
#endregion
|
||||
|
||||
#region XXH32
|
||||
|
||||
public const uint XXH_PRIME32_1 = 0x9E3779B1;
|
||||
|
||||
public const uint XXH_PRIME32_2 = 0x85EBCA77;
|
||||
|
||||
public const uint XXH_PRIME32_3 = 0xC2B2AE3D;
|
||||
|
||||
public const uint XXH_PRIME32_4 = 0x27D4EB2F;
|
||||
|
||||
public const uint XXH_PRIME32_5 = 0x165667B1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region XXH64
|
||||
|
||||
public const ulong XXH_PRIME64_1 = 0x9E3779B185EBCA87;
|
||||
|
||||
public const ulong XXH_PRIME64_2 = 0xC2B2AE3D27D4EB4F;
|
||||
|
||||
public const ulong XXH_PRIME64_3 = 0x165667B19E3779F9;
|
||||
|
||||
public const ulong XXH_PRIME64_4 = 0x85EBCA77C2B2AE63;
|
||||
|
||||
public const ulong XXH_PRIME64_5 = 0x27D4EB2F165667C5;
|
||||
|
||||
#endregion
|
||||
|
||||
#region XXH3
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using static SabreTools.Hashing.HashOperations;
|
||||
using static SabreTools.Hashing.NonCryptographicHash.Constants;
|
||||
using static SabreTools.Hashing.XxHash.Constants;
|
||||
|
||||
namespace SabreTools.Hashing.XxHash
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.XxHash
|
||||
{
|
||||
public class XxHash32
|
||||
{
|
||||
/// <summary>
|
||||
/// The 32-bit seed to alter the hash result predictably.
|
||||
/// </summary>
|
||||
private readonly uint _seed;
|
||||
|
||||
/// <summary>
|
||||
/// Internal xxHash32 state
|
||||
/// </summary>
|
||||
private readonly XXH32State _state;
|
||||
|
||||
public XxHash32(uint seed = 0)
|
||||
{
|
||||
_seed = seed;
|
||||
_state = new XXH32State();
|
||||
_state.Reset(seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the internal hashing state
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_state.Reset(_seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hash a block of data and append it to the existing hash
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the data</param>
|
||||
/// <param name="offset">Offset in the byte array to include</param>
|
||||
/// <param name="length">Length of the data to hash</param>
|
||||
public void TransformBlock(byte[] data, int offset, int length)
|
||||
=> _state.Update(data, offset, length);
|
||||
|
||||
/// <summary>
|
||||
/// Finalize the hash and return as a byte array
|
||||
/// </summary>
|
||||
public byte[] Finalize()
|
||||
{
|
||||
uint hash = _state.Digest();
|
||||
return BitConverter.GetBytes(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Hashing.XxHash
|
||||
{
|
||||
public class XxHash64
|
||||
{
|
||||
/// <summary>
|
||||
/// The 64-bit seed to alter the hash result predictably.
|
||||
/// </summary>
|
||||
private readonly uint _seed;
|
||||
|
||||
/// <summary>
|
||||
/// Internal xxHash64 state
|
||||
/// </summary>
|
||||
private readonly XXH64State _state;
|
||||
|
||||
public XxHash64(uint seed = 0)
|
||||
{
|
||||
_seed = seed;
|
||||
_state = new XXH64State();
|
||||
_state.Reset(seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the internal hashing state
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_state.Reset(_seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hash a block of data and append it to the existing hash
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the data</param>
|
||||
/// <param name="offset">Offset in the byte array to include</param>
|
||||
/// <param name="length">Length of the data to hash</param>
|
||||
public void TransformBlock(byte[] data, int offset, int length)
|
||||
=> _state.Update(data, offset, length);
|
||||
|
||||
/// <summary>
|
||||
/// Finalize the hash and return as a byte array
|
||||
/// </summary>
|
||||
public byte[] Finalize()
|
||||
{
|
||||
ulong hash = _state.Digest();
|
||||
return BitConverter.GetBytes(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user