Blake3.NET

repository·master·Indexed 41 days ago

https://github.com/xoofx/blake3.net

High-performance, SIMD-accelerated implementations of the BLAKE3 cryptographic hash function for .NET. Offers a fully managed version (Blake3 package) and a native Rust-interop version (Blake3.Native package) supporting Windows, Linux, and macOS. Features include incremental hashing via Hasher, parallel hashing with UpdateWithJoin, keyed hashing for MACs, and subkey derivation.

Tokens
3.3K
Snippets
10
Records
15
Agent score
53%

What's inside Blake3.NET

  1. Compare Blake3 managed vs native performance

    master

    Blake3.NET provides two primary implementations for hashing:

    1. Blake3: A fully managed implementation. It is easy to deploy as it does not require shipping or loading native libraries. It delivers near-native performance, typically ranging from 11% faster to 22% slower than the native version depending on input size and architecture.
    2. Blake3.Native: A wrapper around the SIMD-optimized official Rust implementation. This is generally faster for larger inputs but requires native interop.

    Performance Characteristics:

    • Small inputs: The managed Blake3 can occasionally outperform Blake3.Native on very small inputs (e.g., 4-byte or 100-byte inputs on certain architectures).
    • Large inputs: Blake3.Native typically maintains a performance advantage (roughly 12% to 22% faster on x64) as input size increases.
    • Allocations: Both serial implementations (Blake3 and Blake3.Native) complete without managed allocations.
  2. Use explicit parallel hashing with UpdateWithJoin

    master

    For multi-megabyte inputs, you can achieve significantly higher throughput by using explicit parallel hashing via Hasher.UpdateWithJoin.

    While serial hashing is efficient, UpdateWithJoin leverages multiple cores to process large data chunks. For example, on x64 systems with 10 MB inputs, UpdateWithJoin has been measured to be approximately 4.4x faster than serial Blake3 and 6.5x faster than serial Blake3.Native.

    Note that parallel hashing introduces some scheduling and allocation overhead, so it is most effective for larger inputs (multi-megabyte range) where the throughput gains outweigh the overhead.

  3. Parallel hashing with UpdateWithJoin

    master
    Both Blake3 and Blake3.Native support multi-threaded hashing via Hasher.UpdateWithJoin. This method hashes large aligned subtrees in parallel to improve throughput for multi-megabyte inputs. Smaller inputs are automatically handled on a serial path to avoid scheduling overhead.
  4. Build the native BLAKE3 shared libraries

    master

    To build the BLAKE3 Rust libraries as native shared libraries, you must have rustup installed on your system. Use the platform-specific build scripts provided in the repository to compile for your target architecture.

    Supported build scripts:

    • Windows (x64): build-win-x64.ps1
    • Linux (x64): build-linux-x64.sh
    • Linux (Musl x64): build-linux-musl-x64.sh (targets x86_64-unknown-linux-musl)
    • Linux (Musl ARM64): build-linux-musl-arm64.sh (targets aarch64-unknown-linux-musl)
    # Example for Linux x64
    ./build-linux-x64.sh
  5. Install Blake3.NET via NuGet

    master

    Starting with version 3.0, you must choose between two packages. Both packages expose the same hashing API and type names in the Blake3 namespace. You should reference only one package because both define the same fully qualified public types.

    • Blake3: A fully managed implementation using runtime-selected SIMD code. It has no native library dependencies and runs on any platform supported by .NET.
    • Blake3.Native: Wraps the official Rust implementation (version 1.8.2) through native interop for optimized performance.
    # For the fully managed implementation
    dotnet add package Blake3
    
    # Or, to use the Rust implementation through native interop
    dotnet add package Blake3.Native
  6. Migrating from Blake3 v2.x to v3.0

    master

    In versions 2.x and earlier, the Blake3 package used the native Rust implementation. Starting with version 3.0, the Blake3 package is fully managed.

    To continue using the native Rust implementation after upgrading to 3.0, you must replace your package reference from Blake3 to Blake3.Native. Because both packages retain the Blake3 namespace and the same public API, this change typically does not require any modifications to your source code.

  7. Hash a buffer directly

    master

    Use the static Blake3.Hasher.Hash method to compute a hash from a byte array in a single call.

    var hash = Blake3.Hasher.Hash(Encoding.UTF8.GetBytes("BLAKE3"));
    Console.WriteLine(hash);
    // Prints f890484173e516bfd935ef3d22b912dc9738de38743993cfedf2c9473b3216a4
  8. Hash a stream using Blake3Stream

    master

    The Blake3Stream class allows you to compute a hash from a Stream object (like MemoryStream or FileStream) incrementally.

    using var blake3Stream = new Blake3Stream(new MemoryStream());
    blake3Stream.Write(Encoding.UTF8.GetBytes("BLAKE3"));
    var hash = blake3Stream.ComputeHash();
  9. Seek to a position in the output stream

    master

    You can use Hasher.Finalize to extract a hash from a specific offset within the output stream.

    using var hasher = Blake3.Hasher.New();
    hasher.Update(Encoding.UTF8.GetBytes("BLAKE3"));
    var hashAtPosition = new byte[1024];
    hasher.Finalize(4242, hashAtPosition);
  10. Derive a subkey using NewDeriveKey

    master

    Use Hasher.NewDeriveKey(context) to derive a subkey from a master key using a specific context string.

    const string context = "[application] [commit timestamp] [purpose]";
    using var blake3 = Hasher.NewDeriveKey(Encoding.UTF8.GetBytes(context));
    blake3.Update(inputKeyingMaterial);
    var derivedKey = blake3.Finalize();
    byte[] subkey = derivedKey.AsSpan().ToArray();
  11. Use Hasher for incremental updates

    master

    For scenarios where data arrives in chunks, use the disposable Blake3.Hasher type. Call Update to feed data into the hasher and Finalize to retrieve the resulting hash.

    using var hasher = Blake3.Hasher.New();
    hasher.Update(Encoding.UTF8.GetBytes("BLAKE3"));
    var hash = hasher.Finalize();
  12. Generate a Message Authentication Code (MAC)

    master

    Use Hasher.NewKeyed(macKey) to create a keyed hasher for producing message authentication codes using a 256-bit key.

    using var blake3 = Hasher.NewKeyed(macKey);
    blake3.UpdateWithJoin(message);
    var tag = blake3.Finalize();
    byte[] authenticationTag = tag.AsSpan().ToArray();