blake3_ffi 1.0.2 copy "blake3_ffi: ^1.0.2" to clipboard
blake3_ffi: ^1.0.2 copied to clipboard

BLAKE3 hashing for Dart over native FFI, up to 14x faster than SHA-256. One- shot, streaming, keyed (MAC), and key derivation. C auto-compiled at build, no binaries to ship.

blake3_ffi #

blake3_ffi banner

Fast BLAKE3 cryptographic hashing for Dart, backed by the official BLAKE3 C implementation over FFI. The native code is compiled automatically at build time through Dart build hooks; there is nothing to install and no prebuilt binary to ship.

  • One-shot hashing of a byte buffer.
  • Incremental (streaming) hashing for data that arrives in pieces or does not fit in memory, either through Blake3Hasher or the one-call blake3Stream over a Stream<List<int>>.
  • Keyed hashing (a MAC / PRF) and key derivation (KDF).
  • Extendable output (XOF): request any number of output bytes.
import 'dart:convert';
import 'package:blake3_ffi/blake3_ffi.dart';

void main() {
  // One-shot, as a hex string.
  print(blake3Hex(utf8.encode('hello')));

  // Streaming: feed chunks, then finalize.
  final hasher = Blake3Hasher();
  try {
    hasher.update(utf8.encode('hel'));
    hasher.update(utf8.encode('lo'));
    final digest = hasher.finalize(); // Uint8List, 32 bytes.
    print(digest.length);
  } finally {
    hasher.dispose();
  }
}

Hashing a file or stream #

To hash something too large to load at once, pass its byte stream to blake3Stream (or blake3StreamHex). It drives a Blake3Hasher for you and disposes it when the stream ends, so the input is never held in memory all at once:

import 'dart:io';
import 'package:blake3_ffi/blake3_ffi.dart';

Future<String> hashFile(String path) =>
    blake3StreamHex(File(path).openRead());

This is the same job as binding a Stream to a crypto SHA-256 sink, but it runs at BLAKE3's throughput, which is where the win over SHA-256 is largest (see below).

What BLAKE3 is, and why native #

BLAKE3 is a modern cryptographic hash function. It is a tree hash, so a single input is split into chunks that can be compressed independently. On top of that, its compression function maps cleanly onto SIMD instructions, which lets one core hash several chunks at once. A pure-Dart implementation cannot reach those instructions, so the throughput gap is large on bulk data. This package compiles the reference C implementation and, on arm64, its NEON kernel, so you get that throughput from Dart.

BLAKE3 is not a password hash. For passwords use a memory-hard function (Argon2, scrypt). BLAKE3 is for content addressing, deduplication, integrity checks, MACs, and key derivation.

Performance, honestly #

Measured with bench/bench.dart on an Apple M4 Pro (macOS 26.3, arm64 NEON, Dart 3.11.0, Apple clang 21). The baseline is SHA-256 from the crypto package, the usual pure-Dart choice today. Your numbers will differ by machine and architecture; run the benchmark on your own data before drawing conclusions.

BLAKE3 throughput vs sha256 and pure-Dart

Architecture: Dart to FFI to native BLAKE3

Bulk throughput:

Input BLAKE3 (this package) SHA-256 (crypto) Speedup
1 MB 2451 MB/s 174 MB/s 14.0x
16 MB 2519 MB/s 181 MB/s 13.9x
64 MB 2490 MB/s 177 MB/s 14.0x

Bulk throughput is essentially flat from 1 MB up: once the input clears BLAKE3's internal block size, the per-call and FFI overheads are already amortized, so all three rows land near 14x. (An earlier version of this table showed 1 MB at 7.2x; that was a benchmark artifact — bench/bench.dart hashed only a few megabytes at that size, too little to average out scheduling jitter, and the fixed benchmark now scales the iteration count so every size hashes a comparable total volume.)

Small inputs (time per call, including FFI overhead):

Input BLAKE3 SHA-256 Speedup
64 B 0.17 µs 0.81 µs 4.7x
1 KB 0.86 µs 6.23 µs 7.2x
4 KB 1.79 µs 23.6 µs 13.2x

The win holds at every size measured here, so there is no small-input crossover where the FFI call cost dominates. The largest wins are on bulk data, which is where BLAKE3's SIMD tree hashing pays off.

Keyed hashing and key derivation #

Keyed mode turns BLAKE3 into a MAC or PRF. The key must be exactly 32 bytes.

final key = Uint8List(32); // a real, secret 32-byte key
final tag = blake3Keyed(key, utf8.encode('message'));

Key-derivation mode produces subkeys from input keying material, separated by a hardcoded context string. The context should be application-specific and globally unique; do not let it be attacker-controlled.

final subkey = blake3DeriveKey(
  'example.com 2026 session cookie v1',
  masterSecret,
);

Both modes are also available on Blake3Hasher for streaming input: Blake3Hasher.keyed(key) and Blake3Hasher.deriveKey(context).

Extendable output (XOF) #

BLAKE3 produces an unbounded output stream; the default 32 bytes are just its start. Pass outputLength for more, and seek to skip into the stream. Extending never changes earlier bytes.

final long = blake3(data, outputLength: 64);   // 64 bytes
final tail = Blake3Hasher()
  ..update(data);
// tail.finalize(seek: 32, outputLength: 32) == long bytes 32..63

API notes #

  • finalize() does not consume the hasher: you may keep calling update and finalize, and reset() returns it to its initial state (keeping the key/mode) so one hasher can hash many independent inputs.
  • dispose() frees the small native hasher buffer. A finalizer also frees forgotten hashers at garbage collection, but that memory is invisible to the Dart heap, so prefer explicit disposal. Using a hasher after dispose() throws StateError.
  • Digests are returned as Uint8List; blake3Hex returns lowercase hex.

Platform support #

Requires Dart 3.10+ with build hooks (dart run, dart test, and dart build compile the C automatically; a C toolchain must be present: Xcode CLT, gcc/clang, or MSVC).

Supported platforms are the desktop targets the pubspec declares: Linux, macOS, and Windows on the Dart VM. The rows below describe which CPU architecture uses which kernel, not additional platforms.

Architecture Kernel in this release Status
arm64 (macOS, Linux) NEON SIMD Developed and tested on macOS arm64; CI covers macOS arm64
x86-64 (Linux, macOS, Windows) Portable C Correct (passes the official vectors); CI covers Linux and Windows x64

Correctness is identical on both paths: every target passes the official BLAKE3 test vectors. The difference is throughput. The x86-64 SIMD kernels (SSE2/SSE4.1/AVX2/AVX512) are vendored in src/third_party/blake3/ but not yet compiled, because they need per-source compiler flags the build system cannot express per file today; enabling them is planned for a later release. There is no Flutter, iOS, or Android support yet; it arrives when build hooks land in stable Flutter.

Correctness #

The package is verified against the official BLAKE3 test vectors: all 35 cases, in the default, keyed, and derive-key modes, checking both the 32-byte digest and the extended (131-byte) output, plus streaming-equals-one-shot for every case. Correctness is the reason this package exists; if a vector did not match, there would be no package.

Credits and licenses #

This package is MIT licensed (see LICENSE). It vendors the official BLAKE3 C implementation (version 1.8.5), which is released into the public domain via CC0 1.0 or, at your option, under Apache 2.0. The upstream license texts and a NOTICE are in src/third_party/blake3/. BLAKE3 was designed by Jack O'Connor, Jean-Philippe Aumasson, Samuel Neves, and Zooko Wilcox-O'Hearn.

0
likes
160
points
307
downloads
screenshot

Documentation

API reference

Publisher

verified publisherdeveloperyusuf.com

Weekly Downloads

BLAKE3 hashing for Dart over native FFI, up to 14x faster than SHA-256. One- shot, streaming, keyed (MAC), and key derivation. C auto-compiled at build, no binaries to ship.

Repository (GitHub)
View/report issues

Topics

#blake3 #hashing #cryptography #ffi #performance

License

MIT (license)

Dependencies

code_assets, ffi, hooks, native_toolchain_c

More

Packages that depend on blake3_ffi