Skip to content

Commit

Permalink
enable allocation-free incremental hashing
Browse files Browse the repository at this point in the history
  • Loading branch information
saucecontrol committed Feb 16, 2020
1 parent d50f5fa commit e49f3b9
Show file tree
Hide file tree
Showing 22 changed files with 248 additions and 110 deletions.
8 changes: 4 additions & 4 deletions src/Blake2Fast/Blake2Fast.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
<None Update="Blake2b\Blake2b.tt" LastGenOutput="Blake2b.cs" Generator="TextTemplatingFileGenerator" />
<None Update="Blake2s\Blake2s.tt" LastGenOutput="Blake2s.cs" Generator="TextTemplatingFileGenerator" />
<None Update="Blake2b\Blake2bContext.tt" LastGenOutput="Blake2bContext.cs" Generator="TextTemplatingFileGenerator" />
<None Update="Blake2s\Blake2sContext.tt" LastGenOutput="Blake2sContext.cs" Generator="TextTemplatingFileGenerator" />
<None Update="Blake2b\Blake2bHashState.tt" LastGenOutput="Blake2bHashState.cs" Generator="TextTemplatingFileGenerator" />
<None Update="Blake2s\Blake2sHashState.tt" LastGenOutput="Blake2sHashState.cs" Generator="TextTemplatingFileGenerator" />
<None Update="Blake2b\Blake2bScalar.tt" LastGenOutput="Blake2bScalar.cs" Generator="TextTemplatingFileGenerator" />
<None Update="Blake2s\Blake2sScalar.tt" LastGenOutput="Blake2sScalar.cs" Generator="TextTemplatingFileGenerator" />
<None Update="Blake2b\Blake2bSse4.tt" LastGenOutput="Blake2bSse4.cs" Generator="TextTemplatingFileGenerator" />
<None Update="Blake2s\Blake2sSse4.tt" LastGenOutput="Blake2sSse4.cs" Generator="TextTemplatingFileGenerator" />
<None Update="Blake2b\Blake2bAvx2.tt" LastGenOutput="Blake2bAvx2.cs" Generator="TextTemplatingFileGenerator" />
<Compile Update="Blake2b\Blake2b.cs" DependentUpon="Blake2b.tt" DesignTime="True" AutoGen="True" />
<Compile Update="Blake2s\Blake2s.cs" DependentUpon="Blake2s.tt" DesignTime="True" AutoGen="True" />
<Compile Update="Blake2b\Blake2bContext.cs" DependentUpon="Blake2bContext.tt" DesignTime="True" AutoGen="True" />
<Compile Update="Blake2s\Blake2sContext.cs" DependentUpon="Blake2sContext.tt" DesignTime="True" AutoGen="True" />
<Compile Update="Blake2b\Blake2bHashState.cs" DependentUpon="Blake2bHashState.tt" DesignTime="True" AutoGen="True" />
<Compile Update="Blake2s\Blake2sHashState.cs" DependentUpon="Blake2sHashState.tt" DesignTime="True" AutoGen="True" />
<Compile Update="Blake2b\Blake2bScalar.cs" DependentUpon="Blake2bScalar.tt" DesignTime="True" AutoGen="True" />
<Compile Update="Blake2s\Blake2sScalar.cs" DependentUpon="Blake2sScalar.tt" DesignTime="True" AutoGen="True" />
<Compile Update="Blake2b\Blake2bSse4.cs" DependentUpon="Blake2bSse4.tt" DesignTime="True" AutoGen="True" />
Expand Down
4 changes: 2 additions & 2 deletions src/Blake2Fast/Blake2Hmac.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ private IBlake2Incremental createIncrementalInstance()
#endif

return alg == Algorithm.Blake2b ?
Blake2b.CreateIncrementalHasher(HashSizeValue / 8, key) :
Blake2s.CreateIncrementalHasher(HashSizeValue / 8, key);
(IBlake2Incremental)Blake2b.CreateIncrementalHasher(HashSizeValue / 8, key) :
(IBlake2Incremental)Blake2s.CreateIncrementalHasher(HashSizeValue / 8, key);
}
}
}
Expand Down
36 changes: 19 additions & 17 deletions src/Blake2Fast/Blake2b/Blake2b.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using System.Security.Cryptography;
#endif

using Blake2Fast.Implementation;

namespace Blake2Fast
{
/// <summary>Static helper methods for BLAKE2b hashing.</summary>
Expand All @@ -23,7 +25,7 @@ namespace Blake2Fast
static class Blake2b
{
/// <summary>The default hash digest length in bytes. For BLAKE2b, this value is 64.</summary>
public const int DefaultDigestLength = Blake2bContext.HashBytes;
public const int DefaultDigestLength = Blake2bHashState.HashBytes;

/// <inheritdoc cref="ComputeHash(int, ReadOnlySpan{byte}, ReadOnlySpan{byte})" />
public static byte[] ComputeHash(ReadOnlySpan<byte> input) => ComputeHash(DefaultDigestLength, default, input);
Expand All @@ -42,10 +44,10 @@ static class Blake2b
/// <returns>The computed hash digest from the message bytes in <paramref name="input" />.</returns>
public static byte[] ComputeHash(int digestLength, ReadOnlySpan<byte> key, ReadOnlySpan<byte> input)
{
var ctx = default(Blake2bContext);
ctx.Init(digestLength, key);
ctx.Update(input);
return ctx.Finish();
var hs = default(Blake2bHashState);
hs.Init(digestLength, key);
hs.Update(input);
return hs.Finish();
}

/// <inheritdoc cref="ComputeAndWriteHash(ReadOnlySpan{byte}, ReadOnlySpan{byte}, Span{byte})" />
Expand All @@ -69,31 +71,31 @@ public static void ComputeAndWriteHash(int digestLength, ReadOnlySpan<byte> key,
if (output.Length < digestLength)
throw new ArgumentException($"Output buffer must have a capacity of at least {digestLength} bytes.", nameof(output));

var ctx = default(Blake2bContext);
ctx.Init(digestLength, key);
ctx.Update(input);
ctx.TryFinish(output, out int _);
var hs = default(Blake2bHashState);
hs.Init(digestLength, key);
hs.Update(input);
hs.Finish(output);
}

/// <inheritdoc cref="CreateIncrementalHasher(int, ReadOnlySpan{byte})" />
public static IBlake2Incremental CreateIncrementalHasher() => CreateIncrementalHasher(DefaultDigestLength, default);
public static Blake2bHashState CreateIncrementalHasher() => CreateIncrementalHasher(DefaultDigestLength, default);

/// <inheritdoc cref="CreateIncrementalHasher(int, ReadOnlySpan{byte})" />
public static IBlake2Incremental CreateIncrementalHasher(int digestLength) => CreateIncrementalHasher(digestLength, default);
public static Blake2bHashState CreateIncrementalHasher(int digestLength) => CreateIncrementalHasher(digestLength, default);

/// <inheritdoc cref="CreateIncrementalHasher(int, ReadOnlySpan{byte})" />
public static IBlake2Incremental CreateIncrementalHasher(ReadOnlySpan<byte> key) => CreateIncrementalHasher(DefaultDigestLength, key);
public static Blake2bHashState CreateIncrementalHasher(ReadOnlySpan<byte> key) => CreateIncrementalHasher(DefaultDigestLength, key);

/// <summary>Create and initialize an incremental BLAKE2b hash computation.</summary>
/// <remarks>If you will receive the input in segments rather than all at once, this is the most efficient way to calculate the hash.</remarks>
/// <param name="digestLength">The hash digest length in bytes. Valid values are 1 to 64.</param>
/// <param name="key">0 to 64 bytes of input for initializing a keyed hash.</param>
/// <returns>An <see cref="IBlake2Incremental" /> interface for updating and finalizing the hash.</returns>
public static IBlake2Incremental CreateIncrementalHasher(int digestLength, ReadOnlySpan<byte> key)
/// <returns>An <see cref="Blake2bHashState" /> instance for updating and finalizing the hash.</returns>
public static Blake2bHashState CreateIncrementalHasher(int digestLength, ReadOnlySpan<byte> key)
{
var ctx = default(Blake2bContext);
ctx.Init(digestLength, key);
return ctx;
var hs = default(Blake2bHashState);
hs.Init(digestLength, key);
return hs;
}

#if BLAKE2_CRYPTOGRAPHY
Expand Down
9 changes: 7 additions & 2 deletions src/Blake2Fast/Blake2b/Blake2bAvx2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace Blake2Fast
namespace Blake2Fast.Implementation
{
unsafe internal partial struct Blake2bContext
#if BLAKE2_PUBLIC
public
#else
internal
#endif
unsafe partial struct Blake2bHashState
{
// SIMD algorithm described in https://eprint.iacr.org/2012/275.pdf
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
Expand Down
9 changes: 7 additions & 2 deletions src/Blake2Fast/Blake2b/Blake2bAvx2.tt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ using System.Runtime.Intrinsics.X86;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace Blake2Fast
namespace Blake2Fast.Implementation
{
unsafe internal partial struct Blake2bContext
#if BLAKE2_PUBLIC
public
#else
internal
#endif
unsafe partial struct Blake2bHashState
{
// SIMD algorithm described in https://eprint.iacr.org/2012/275.pdf
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,23 @@
using System.Runtime.Intrinsics.X86;
#endif

namespace Blake2Fast
namespace Blake2Fast.Implementation
{
unsafe internal partial struct Blake2bContext : IBlake2Incremental
/// <summary>Defines the state associated with an incremental BLAKE2b hashing operation.</summary>
/// <remarks>Instances of this struct must be created by <see cref="Blake2b.CreateIncrementalHasher()" />. An instance created directly will be unusable.</remarks>
#if BLAKE2_PUBLIC
public
#else
internal
#endif
unsafe partial struct Blake2bHashState : IBlake2Incremental
{
public const int WordSize = sizeof(ulong);
public const int BlockWords = 16;
public const int BlockBytes = BlockWords * WordSize;
public const int HashWords = 8;
public const int HashBytes = HashWords * WordSize;
public const int MaxKeyBytes = HashBytes;
internal const int WordSize = sizeof(ulong);
internal const int BlockWords = 16;
internal const int BlockBytes = BlockWords * WordSize;
internal const int HashWords = 8;
internal const int HashBytes = HashWords * WordSize;
internal const int MaxKeyBytes = HashBytes;

private fixed byte b[BlockBytes];
private fixed ulong h[HashWords];
Expand All @@ -49,14 +56,15 @@ unsafe internal partial struct Blake2bContext : IBlake2Incremental
};
#endif

/// <inheritdoc />
public int DigestLength => (int)outlen;

private void compress(ref byte input, uint offs, uint cb)
{
uint inc = Math.Min(cb, BlockBytes);

fixed (byte* pinput = &input)
fixed (Blake2bContext* s = &this)
fixed (Blake2bHashState* s = &this)
{
ulong* sh = s->h;
byte* pin = pinput + offs;
Expand Down Expand Up @@ -86,7 +94,7 @@ private void compress(ref byte input, uint offs, uint cb)
}
}

public void Init(int digestLength = HashBytes, ReadOnlySpan<byte> key = default)
internal void Init(int digestLength = HashBytes, ReadOnlySpan<byte> key = default)
{
uint keylen = (uint)key.Length;

Expand All @@ -106,8 +114,10 @@ public void Init(int digestLength = HashBytes, ReadOnlySpan<byte> key = default)
}
}

/// <inheritdoc />
public void Update(ReadOnlySpan<byte> input)
{
if (outlen == 0) ThrowHelper.HashNotInitialized();
if (f[0] != 0) ThrowHelper.HashFinalized();

uint consumed = 0;
Expand Down Expand Up @@ -141,13 +151,15 @@ public void Update(ReadOnlySpan<byte> input)
}
}

/// <inheritdoc />
public void Update<T>(ReadOnlySpan<T> input) where T : struct
{
ThrowHelper.ThrowIfIsRefOrContainsRefs<T>();

Update(MemoryMarshal.AsBytes(input));
}

/// <inheritdoc />
public void Update<T>(T input) where T : struct
{
ThrowHelper.ThrowIfIsRefOrContainsRefs<T>();
Expand All @@ -172,6 +184,7 @@ public void Update<T>(T input) where T : struct

private void finish(Span<byte> hash)
{
if (outlen == 0) ThrowHelper.HashNotInitialized();
if (f[0] != 0) ThrowHelper.HashFinalized();

if (c < BlockBytes)
Expand All @@ -183,6 +196,7 @@ private void finish(Span<byte> hash)
Unsafe.CopyBlockUnaligned(ref hash[0], ref Unsafe.As<ulong, byte>(ref h[0]), outlen);
}

/// <inheritdoc />
public byte[] Finish()
{
byte[] hash = new byte[outlen];
Expand All @@ -191,6 +205,15 @@ public byte[] Finish()
return hash;
}

/// <inheritdoc />
public void Finish(Span<byte> output)
{
if ((uint)output.Length < outlen) ThrowHelper.OutputTooSmall(DigestLength);

finish(output);
}

/// <inheritdoc />
public bool TryFinish(Span<byte> output, out int bytesWritten)
{
if ((uint)output.Length < outlen)
Expand Down
File renamed without changes.
9 changes: 7 additions & 2 deletions src/Blake2Fast/Blake2b/Blake2bScalar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
// </auto-generated>
//------------------------------------------------------------------------------

namespace Blake2Fast
namespace Blake2Fast.Implementation
{
unsafe internal partial struct Blake2bContext
#if BLAKE2_PUBLIC
public
#else
internal
#endif
unsafe partial struct Blake2bHashState
{
private static void mixScalar(ulong* sh, ulong* m)
{
Expand Down
9 changes: 7 additions & 2 deletions src/Blake2Fast/Blake2b/Blake2bSse4.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace Blake2Fast
namespace Blake2Fast.Implementation
{
unsafe internal partial struct Blake2bContext
#if BLAKE2_PUBLIC
public
#else
internal
#endif
unsafe partial struct Blake2bHashState
{
// SIMD algorithm described in https://eprint.iacr.org/2012/275.pdf
#if !HWINTRINSICS_EXP
Expand Down
9 changes: 7 additions & 2 deletions src/Blake2Fast/Blake2b/Blake2bSse4.tt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ using System.Runtime.Intrinsics.X86;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace Blake2Fast
namespace Blake2Fast.Implementation
{
unsafe internal partial struct Blake2bContext
#if BLAKE2_PUBLIC
public
#else
internal
#endif
unsafe partial struct Blake2bHashState
{
// SIMD algorithm described in https://eprint.iacr.org/2012/275.pdf
#if !HWINTRINSICS_EXP
Expand Down
36 changes: 19 additions & 17 deletions src/Blake2Fast/Blake2s/Blake2s.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using System.Security.Cryptography;
#endif

using Blake2Fast.Implementation;

namespace Blake2Fast
{
/// <summary>Static helper methods for BLAKE2s hashing.</summary>
Expand All @@ -23,7 +25,7 @@ namespace Blake2Fast
static class Blake2s
{
/// <summary>The default hash digest length in bytes. For BLAKE2s, this value is 32.</summary>
public const int DefaultDigestLength = Blake2sContext.HashBytes;
public const int DefaultDigestLength = Blake2sHashState.HashBytes;

/// <inheritdoc cref="ComputeHash(int, ReadOnlySpan{byte}, ReadOnlySpan{byte})" />
public static byte[] ComputeHash(ReadOnlySpan<byte> input) => ComputeHash(DefaultDigestLength, default, input);
Expand All @@ -42,10 +44,10 @@ static class Blake2s
/// <returns>The computed hash digest from the message bytes in <paramref name="input" />.</returns>
public static byte[] ComputeHash(int digestLength, ReadOnlySpan<byte> key, ReadOnlySpan<byte> input)
{
var ctx = default(Blake2sContext);
ctx.Init(digestLength, key);
ctx.Update(input);
return ctx.Finish();
var hs = default(Blake2sHashState);
hs.Init(digestLength, key);
hs.Update(input);
return hs.Finish();
}

/// <inheritdoc cref="ComputeAndWriteHash(ReadOnlySpan{byte}, ReadOnlySpan{byte}, Span{byte})" />
Expand All @@ -69,31 +71,31 @@ public static void ComputeAndWriteHash(int digestLength, ReadOnlySpan<byte> key,
if (output.Length < digestLength)
throw new ArgumentException($"Output buffer must have a capacity of at least {digestLength} bytes.", nameof(output));

var ctx = default(Blake2sContext);
ctx.Init(digestLength, key);
ctx.Update(input);
ctx.TryFinish(output, out int _);
var hs = default(Blake2sHashState);
hs.Init(digestLength, key);
hs.Update(input);
hs.Finish(output);
}

/// <inheritdoc cref="CreateIncrementalHasher(int, ReadOnlySpan{byte})" />
public static IBlake2Incremental CreateIncrementalHasher() => CreateIncrementalHasher(DefaultDigestLength, default);
public static Blake2sHashState CreateIncrementalHasher() => CreateIncrementalHasher(DefaultDigestLength, default);

/// <inheritdoc cref="CreateIncrementalHasher(int, ReadOnlySpan{byte})" />
public static IBlake2Incremental CreateIncrementalHasher(int digestLength) => CreateIncrementalHasher(digestLength, default);
public static Blake2sHashState CreateIncrementalHasher(int digestLength) => CreateIncrementalHasher(digestLength, default);

/// <inheritdoc cref="CreateIncrementalHasher(int, ReadOnlySpan{byte})" />
public static IBlake2Incremental CreateIncrementalHasher(ReadOnlySpan<byte> key) => CreateIncrementalHasher(DefaultDigestLength, key);
public static Blake2sHashState CreateIncrementalHasher(ReadOnlySpan<byte> key) => CreateIncrementalHasher(DefaultDigestLength, key);

/// <summary>Create and initialize an incremental BLAKE2s hash computation.</summary>
/// <remarks>If you will receive the input in segments rather than all at once, this is the most efficient way to calculate the hash.</remarks>
/// <param name="digestLength">The hash digest length in bytes. Valid values are 1 to 32.</param>
/// <param name="key">0 to 32 bytes of input for initializing a keyed hash.</param>
/// <returns>An <see cref="IBlake2Incremental" /> interface for updating and finalizing the hash.</returns>
public static IBlake2Incremental CreateIncrementalHasher(int digestLength, ReadOnlySpan<byte> key)
/// <returns>An <see cref="Blake2sHashState" /> instance for updating and finalizing the hash.</returns>
public static Blake2sHashState CreateIncrementalHasher(int digestLength, ReadOnlySpan<byte> key)
{
var ctx = default(Blake2sContext);
ctx.Init(digestLength, key);
return ctx;
var hs = default(Blake2sHashState);
hs.Init(digestLength, key);
return hs;
}

#if BLAKE2_CRYPTOGRAPHY
Expand Down
Loading

0 comments on commit e49f3b9

Please sign in to comment.