diff --git a/Cargo.lock b/Cargo.lock index 3b092ed..6d889b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "allocator-api2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c880a97d28a3681c0267bd29cff89621202715b065127cd445fa0f0fe0aa2880" + [[package]] name = "arrayref" version = "0.3.9" @@ -47,10 +53,11 @@ dependencies = [ [[package]] name = "blake3-wasm-rs" -version = "0.1.3" +version = "0.1.4" dependencies = [ "blake3", "hex", + "talc", "wasm-bindgen", "wasm-bindgen-test", ] @@ -164,6 +171,15 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "memchr" version = "2.8.0" @@ -250,6 +266,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.228" @@ -316,6 +338,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "talc" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3081e3f3ad542dc4cd0ae720f752598f8880fd364dc2d031132e3509f673aa23" +dependencies = [ + "allocator-api2", + "lock_api", +] + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/Cargo.toml b/Cargo.toml index 11bce0a..bd08110 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "blake3-wasm-rs" -version = "0.1.3" +version = "0.1.4" edition = "2024" license = "MIT" authors = ["UneBaguette "] @@ -13,10 +13,16 @@ categories = ["cryptography", "wasm"] [lib] crate-type = ["cdylib", "rlib"] +[features] +talc = ["dep:talc"] + [dependencies] blake3 = { version = "1.8", features = ["wasm32_simd"] } wasm-bindgen = "0.2" [dev-dependencies] wasm-bindgen-test = "0.3" -hex = "0.4" \ No newline at end of file +hex = "0.4" + +[target.'cfg(target_family = "wasm")'.dependencies] +talc = { version = "5", optional = true } \ No newline at end of file diff --git a/Makefile b/Makefile index d5b71ab..0724dab 100644 --- a/Makefile +++ b/Makefile @@ -1,78 +1,83 @@ ROOT := pkg CRATE := blake3_wasm_rs -VERSION := 0.3.0 +VERSION := 0.4.0 -.PHONY: all clean build bundler node web node-esm package verify +CARGO := cargo +WASM_BINDGEN := wasm-bindgen +WASM_OPT := wasm-opt +WASM_OPT_FLAGS := --enable-bulk-memory --enable-nontrapping-float-to-int -O +WASM_TARGET := wasm32-unknown-unknown +TARGET_DIR := target/$(WASM_TARGET)/release + +.PHONY: all clean build build-all node-esm package verify all: clean build package clean: rm -rf $(ROOT) -build: bundler node web node-esm +build: build-all node-esm -bundler: - @echo "Building bundler target..." - wasm-pack build --target bundler -d $(ROOT)/bundler +build-all: + @echo "Building all target..." + RUSTFLAGS='-C opt-level=s' $(CARGO) build --target $(WASM_TARGET) --release --features talc + @mkdir -p $(ROOT)/bundler $(ROOT)/web $(ROOT)/node + $(WASM_BINDGEN) --target bundler --out-dir $(ROOT)/bundler $(TARGET_DIR)/$(CRATE).wasm + $(WASM_BINDGEN) --target web --out-dir $(ROOT)/web $(TARGET_DIR)/$(CRATE).wasm + $(WASM_BINDGEN) --target nodejs --out-dir $(ROOT)/node $(TARGET_DIR)/$(CRATE).wasm + @mv $(ROOT)/bundler/$(CRATE)_bg.wasm $(ROOT)/$(CRATE)_bg.wasm + @rm -f $(ROOT)/web/$(CRATE)_bg.wasm $(ROOT)/node/$(CRATE)_bg.wasm + $(WASM_OPT) $(WASM_OPT_FLAGS) $(ROOT)/$(CRATE)_bg.wasm -o $(ROOT)/$(CRATE)_bg.wasm + @node -e "['bundler','web','node'].forEach(d=>{const f='$(ROOT)/'+d+'/$(CRATE).js';require('fs').writeFileSync(f,require('fs').readFileSync(f,'utf8').replace(/$(CRATE)_bg\.wasm/g,'../$(CRATE)_bg.wasm'))})" + @rm -f $(ROOT)/bundler/package.json $(ROOT)/web/package.json + @rm -f $(ROOT)/bundler/.gitignore $(ROOT)/web/.gitignore $(ROOT)/node/.gitignore + @echo '{"type":"commonjs"}' > $(ROOT)/node/package.json + @cp scripts/tpl/index.js.template $(ROOT)/index.js + @cp scripts/tpl/index.d.ts $(ROOT)/index.d.ts + @cp scripts/tpl/README.md $(ROOT)/README.md + @cp LICENSE $(ROOT)/LICENSE + @sed -i 's|// @ts-nocheck|// Types|' $(ROOT)/index.d.ts + @node -e "\ + const pkg = {\ + name: 'blake3-wasm-rs',\ + type: 'module',\ + version: '$(VERSION)',\ + description: 'BLAKE3 hashing via Rust/WASM - works in Node.js (CJS + ESM), browsers, and bundlers',\ + license: 'MIT',\ + repository: { type: 'git', url: 'https://github.com/UneBaguette/blake3.wasm' },\ + main: 'index.js',\ + types: 'index.d.ts',\ + exports: { '.': {\ + node: { require: './node/$(CRATE).js', import: './node-esm/index.mjs' },\ + import: './bundler/$(CRATE).js',\ + default: './web/$(CRATE).js'\ + }},\ + files: ['bundler/', 'web/', 'node/', 'node-esm/', '$(CRATE)_bg.wasm', 'README.md', 'LICENSE'],\ + keywords: ['blake3', 'wasm', 'hash', 'cryptography', 'wasm-bindgen']\ + };\ + require('fs').writeFileSync('./$(ROOT)/package.json', JSON.stringify(pkg, null, 2) + '\n');" -node: - @echo "Building nodejs target..." - wasm-pack build --target nodejs -d $(ROOT)/node - -web: - @echo "Building web target..." - wasm-pack build --target web -d $(ROOT)/web - -node-esm: node +node-esm: @echo "Generating node-esm wrapper..." @mkdir -p $(ROOT)/node-esm @node -e "\ - const fs = require('fs'); \ - const dts = fs.readFileSync('$(ROOT)/node/$(CRATE).d.ts', 'utf8'); \ - const fns = [...dts.matchAll(/export function (\w+)/g)].map(m => m[1]); \ - const cls = [...dts.matchAll(/export class (\w+)/g)].map(m => m[1]); \ - const lines = [ \ - \"import { createRequire } from 'module';\", \ - \"const require = createRequire(import.meta.url);\", \ - \"const mod = require('../node/$(CRATE).js');\", \ - \"export default mod;\", \ - ...fns.map(n => 'export const ' + n + ' = mod.' + n + ';'), \ - ...cls.map(n => 'export const ' + n + ' = mod.' + n + ';'), \ - ]; \ - fs.writeFileSync('$(ROOT)/node-esm/index.mjs', lines.join('\n') + '\n'); \ + const fs = require('fs'); \ + const dts = fs.readFileSync('$(ROOT)/node/$(CRATE).d.ts', 'utf8'); \ + const fns = [...dts.matchAll(/export function (\w+)/g)].map(m => m[1]); \ + const cls = [...dts.matchAll(/export class (\w+)/g)].map(m => m[1]); \ + const lines = [ \ + \"import { createRequire } from 'module';\", \ + \"const require = createRequire(import.meta.url);\", \ + \"const mod = require('../node/$(CRATE).js');\", \ + \"export default mod;\", \ + ...fns.map(n => 'export const ' + n + ' = mod.' + n + ';'), \ + ...cls.map(n => 'export const ' + n + ' = mod.' + n + ';'), \ + ]; \ + fs.writeFileSync('$(ROOT)/node-esm/index.mjs', lines.join('\n') + '\n'); \ " -package: build - @cp README.md $(ROOT)/README.md - @rm -f $(ROOT)/bundler/package.json $(ROOT)/node/package.json $(ROOT)/web/package.json - @rm -f $(ROOT)/bundler/.gitignore $(ROOT)/node/.gitignore $(ROOT)/web/.gitignore - @node -e "\ - const pkg = { \ - name: 'blake3-wasm-rs', \ - version: '$(VERSION)', \ - description: 'BLAKE3 hashing via Rust/WASM - works in Node.js (CJS + ESM), browsers, and bundlers', \ - license: 'MIT', \ - repository: { \ - type: 'git', \ - url: 'https://github.com/UneBaguette/blake3.wasm', \ - }, \ - exports: { \ - '.': { \ - node: { \ - require: './node/$(CRATE).js', \ - import: './node-esm/index.mjs', \ - }, \ - import: './bundler/$(CRATE).js', \ - default: './web/$(CRATE).js', \ - }, \ - }, \ - types: './bundler/$(CRATE).d.ts', \ - files: ['node/', 'node-esm/', 'bundler/', 'web/', 'README.md'], \ - keywords: ['blake3', 'wasm', 'hash', 'cryptography', 'wasm-bindgen'], \ - }; \ - require('fs').writeFileSync('$(ROOT)/package.json', JSON.stringify(pkg, null, 2) + '\n'); \ - " +package: @echo "" @echo "Done! Package ready in $(ROOT)/" @echo " node (CJS): $(ROOT)/node/" diff --git a/README.md b/README.md index 5acffef..2bfadab 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# blake3 to wasm +# blake3-wasm-rs [![npm version](https://img.shields.io/npm/v/blake3-wasm-rs)](https://www.npmjs.com/package/blake3-wasm-rs) [![CI](https://github.com/UneBaguette/blake3.wasm/actions/workflows/ci.yml/badge.svg)](https://github.com/UneBaguette/blake3.wasm/actions/workflows/ci.yml) [![license](https://img.shields.io/npm/l/blake3-wasm-rs)](https://github.com/UneBaguette/blake3.wasm/blob/master/LICENSE) [![npm downloads](https://img.shields.io/npm/dm/blake3-wasm-rs)](https://www.npmjs.com/package/blake3-wasm-rs) -**blake3.wasm** is a WebAssembly port of the BLAKE3 cryptographic hash function written in **Rust**. It enables fast and secure hashing right inside **browsers** and **Node.js**. +**blake3-wasm-rs** is a WebAssembly port of the BLAKE3 cryptographic hash function written in **Rust**. It enables fast and secure hashing right inside **browsers** and **Node.js**. ## build @@ -36,43 +36,125 @@ const key = new Uint8Array(32).fill(1); // One-shot hashing blake3.hash(data); -blake3.hashXof(data, 64); // variable output length - -// MAC and key derivation -blake3.keyedHash(data, key); +blake3.hashXof(data, 64); // variable output length +blake3.keyedHash(data, key); // key must be exactly 32 bytes blake3.deriveKey('my context', key); -// Conctruct for streaming -const hasher = new blake3.Hasher(); - -hasher.update(data.slice(0, 5)); -hasher.update(data.slice(5)); -hasher.finalize(); -``` - -#### OR - -```js -import { hash, hashXof, keyedHash, deriveKey, Hasher } from 'blake3-wasm-rs' - -const data = new TextEncoder().encode('hello world') -const key = new Uint8Array(32).fill(1) - -// One-shot hashing -hash(data) -hashXof(data, 64) // variable output length - -// MAC and key derivation -keyedHash(data, key) -deriveKey('my context', key) +// Conctruct for Streaming +{ + using h = new blake3.Hasher(); + h.update(data.slice(0, 5)); + h.update(data.slice(5)); + h.finalize(); + h.finalizeXof(64); + h.reset(); +} // Streaming -const h = new Hasher() -h.update(data.slice(0, 5)) -h.update(data.slice(5)) -h.finalize() +// Keyed (MAC mode) +{ + using mac = blake3.Hasher.newKeyed(key); + mac.update(data); + mac.finalize(); +} + +// Streaming +// Key derivation mode +{ + using kdf = blake3.Hasher.newDeriveKey('my app v1 :: subkey'); + kdf.update(key); + kdf.finalize(); +} + +// Batch hashing without re-allocating +{ + using h = new blake3.Hasher(); + h.update(chunk1); + const first = h.finalizeAndReset(); + h.update(chunk2); + const second = h.finalizeAndReset(); +} ``` +#### Named imports + +```ts +import { hash, hashXof, keyedHash, deriveKey, Hasher } from 'blake3-wasm-rs'; + +const data = new TextEncoder().encode('hello world'); +const key = new Uint8Array(32).fill(1); + +// One-shot hashing +hash(data); +hashXof(data, 64); // variable output length +keyedHash(data, key); // key must be exactly 32 bytes +deriveKey('my context', key); + +// Streaming +{ + using h = new Hasher(); + h.update(data.slice(0, 5)); + h.update(data.slice(5)); + h.finalize(); + h.finalizeXof(64); + h.reset(); +} + +// Keyed (MAC mode) +{ + using mac = Hasher.newKeyed(key); + mac.update(data); + mac.finalize(); +} + +// Key derivation mode +{ + using kdf = Hasher.newDeriveKey('my app v1 :: subkey'); + kdf.update(key); + kdf.finalize(); +} + +// Batch hashing without re-allocating +{ + using h = new Hasher(); + h.update(chunk1); + const first = h.finalizeAndReset(); + h.update(chunk2); + const second = h.finalizeAndReset(); +} +``` + +## API + +### Functions + +| Function | Returns | Description | +|-----------------------------------|--------------|-------------------------------------------------| +| `hash(data)` | `Uint8Array` | One-shot 32-byte BLAKE3 digest | +| `hashXof(data, outLen)` | `Uint8Array` | Variable-length digest (XOF mode) | +| `keyedHash(data, key)` | `Uint8Array` | Keyed hash / MAC (key must be exactly 32 bytes) | +| `deriveKey(context, keyMaterial)` | `Uint8Array` | Derive a 32-byte subkey | + +### Hasher class + +| Method | Returns | Description | +|--------------------------------|--------------|-----------------------------------------------------------| +| `new Hasher()` | `Hasher` | Streaming hasher, unkeyed | +| `Hasher.newKeyed(key)` | `Hasher` | Streaming hasher, MAC mode (key must be exactly 32 bytes) | +| `Hasher.newDeriveKey(context)` | `Hasher` | Streaming hasher, KDF mode | +| `.update(data)` | `void` | Feed data, can be called multiple times | +| `.finalize()` | `Uint8Array` | 32-byte digest, non-destructive | +| `.finalizeXof(outLen)` | `Uint8Array` | Variable-length digest, non-destructive | +| `.finalizeAndReset()` | `Uint8Array` | Finalize then reset (useful for batch hashing) | +| `.reset()` | `void` | Reset to initial state, preserves mode | +| `.free()` | `void` | Release WASM memory manually (prefer `using` instead) | + +> **Memory management:** In all modern browsers (and wasm-bindgen ≥ 0.2.91), WASM memory is freed automatically via the TC39 weak references proposal when the JS object goes out of scope. +> +> In practice, you often don't need to think about this. For deterministic cleanup or environments without weak reference support (older browsers, some Node.js setups), use `using` (TypeScript 5.2+ / ES2026) or call `.free()` manually. +> +> Never call `.free()` on a `using`-managed instance otherwise it will double-free. + ## Benchmarks Tested on **Apple M4**, Node.js v24. diff --git a/scripts/tpl/README.md b/scripts/tpl/README.md new file mode 100644 index 0000000..2bfadab --- /dev/null +++ b/scripts/tpl/README.md @@ -0,0 +1,188 @@ +# blake3-wasm-rs + +[![npm version](https://img.shields.io/npm/v/blake3-wasm-rs)](https://www.npmjs.com/package/blake3-wasm-rs) +[![CI](https://github.com/UneBaguette/blake3.wasm/actions/workflows/ci.yml/badge.svg)](https://github.com/UneBaguette/blake3.wasm/actions/workflows/ci.yml) +[![license](https://img.shields.io/npm/l/blake3-wasm-rs)](https://github.com/UneBaguette/blake3.wasm/blob/master/LICENSE) +[![npm downloads](https://img.shields.io/npm/dm/blake3-wasm-rs)](https://www.npmjs.com/package/blake3-wasm-rs) + +**blake3-wasm-rs** is a WebAssembly port of the BLAKE3 cryptographic hash function written in **Rust**. It enables fast and secure hashing right inside **browsers** and **Node.js**. + +## build + +```sh +# lets build it! + +# For NodeJS +wasm-pack build --target nodejs --release + +# For the web! +wasm-pack build --target web --release +``` + +SIMD is enabled via `wasm32_simd` feature in `Cargo.toml` and requires: +```toml +# .cargo/config.toml +[target.wasm32-unknown-unknown] +rustflags = ["-C", "target-feature=+simd128"] +``` + +## Usage + +```ts +import * as blake3 from 'blake3-wasm-rs'; + +const data = new TextEncoder().encode('hello world'); +const key = new Uint8Array(32).fill(1); + +// One-shot hashing +blake3.hash(data); +blake3.hashXof(data, 64); // variable output length +blake3.keyedHash(data, key); // key must be exactly 32 bytes +blake3.deriveKey('my context', key); + +// Conctruct for Streaming +{ + using h = new blake3.Hasher(); + h.update(data.slice(0, 5)); + h.update(data.slice(5)); + h.finalize(); + h.finalizeXof(64); + h.reset(); +} + +// Streaming +// Keyed (MAC mode) +{ + using mac = blake3.Hasher.newKeyed(key); + mac.update(data); + mac.finalize(); +} + +// Streaming +// Key derivation mode +{ + using kdf = blake3.Hasher.newDeriveKey('my app v1 :: subkey'); + kdf.update(key); + kdf.finalize(); +} + +// Batch hashing without re-allocating +{ + using h = new blake3.Hasher(); + h.update(chunk1); + const first = h.finalizeAndReset(); + h.update(chunk2); + const second = h.finalizeAndReset(); +} +``` + +#### Named imports + +```ts +import { hash, hashXof, keyedHash, deriveKey, Hasher } from 'blake3-wasm-rs'; + +const data = new TextEncoder().encode('hello world'); +const key = new Uint8Array(32).fill(1); + +// One-shot hashing +hash(data); +hashXof(data, 64); // variable output length +keyedHash(data, key); // key must be exactly 32 bytes +deriveKey('my context', key); + +// Streaming +{ + using h = new Hasher(); + h.update(data.slice(0, 5)); + h.update(data.slice(5)); + h.finalize(); + h.finalizeXof(64); + h.reset(); +} + +// Keyed (MAC mode) +{ + using mac = Hasher.newKeyed(key); + mac.update(data); + mac.finalize(); +} + +// Key derivation mode +{ + using kdf = Hasher.newDeriveKey('my app v1 :: subkey'); + kdf.update(key); + kdf.finalize(); +} + +// Batch hashing without re-allocating +{ + using h = new Hasher(); + h.update(chunk1); + const first = h.finalizeAndReset(); + h.update(chunk2); + const second = h.finalizeAndReset(); +} +``` + +## API + +### Functions + +| Function | Returns | Description | +|-----------------------------------|--------------|-------------------------------------------------| +| `hash(data)` | `Uint8Array` | One-shot 32-byte BLAKE3 digest | +| `hashXof(data, outLen)` | `Uint8Array` | Variable-length digest (XOF mode) | +| `keyedHash(data, key)` | `Uint8Array` | Keyed hash / MAC (key must be exactly 32 bytes) | +| `deriveKey(context, keyMaterial)` | `Uint8Array` | Derive a 32-byte subkey | + +### Hasher class + +| Method | Returns | Description | +|--------------------------------|--------------|-----------------------------------------------------------| +| `new Hasher()` | `Hasher` | Streaming hasher, unkeyed | +| `Hasher.newKeyed(key)` | `Hasher` | Streaming hasher, MAC mode (key must be exactly 32 bytes) | +| `Hasher.newDeriveKey(context)` | `Hasher` | Streaming hasher, KDF mode | +| `.update(data)` | `void` | Feed data, can be called multiple times | +| `.finalize()` | `Uint8Array` | 32-byte digest, non-destructive | +| `.finalizeXof(outLen)` | `Uint8Array` | Variable-length digest, non-destructive | +| `.finalizeAndReset()` | `Uint8Array` | Finalize then reset (useful for batch hashing) | +| `.reset()` | `void` | Reset to initial state, preserves mode | +| `.free()` | `void` | Release WASM memory manually (prefer `using` instead) | + +> **Memory management:** In all modern browsers (and wasm-bindgen ≥ 0.2.91), WASM memory is freed automatically via the TC39 weak references proposal when the JS object goes out of scope. +> +> In practice, you often don't need to think about this. For deterministic cleanup or environments without weak reference support (older browsers, some Node.js setups), use `using` (TypeScript 5.2+ / ES2026) or call `.free()` manually. +> +> Never call `.free()` on a `using`-managed instance otherwise it will double-free. + +## Benchmarks + +Tested on **Apple M4**, Node.js v24. + +| Size | @noble/hashes | awasm-noble | awasm-noble (threads) | blake3-wasm | +|-------|---------------|-------------|-----------------------|-------------| +| 32 B | 28 MB/s | 105 MB/s | 94 MB/s | 129 MB/s | +| 1 KB | 105 MB/s | 843 MB/s | 819 MB/s | 568 MB/s | +| 64 KB | 102 MB/s | 1,898 MB/s | 1,855 MB/s | 2,004 MB/s | +| 1 MB | 101 MB/s | 1,943 MB/s | 4,711 MB/s | 1,893 MB/s | +| 10 MB | 101 MB/s | 1,911 MB/s | 6,456 MB/s | 2,185 MB/s | + +Tested on **Ryzen 7 5800X**, Node.js v24. + +| Size | @noble/hashes | awasm-noble | awasm-noble (threads) | blake3-wasm | +|-------|---------------|-------------|-----------------------|-------------| +| 32 B | 11 MB/s | 34 MB/s | 45 MB/s | 70 MB/s | +| 1 KB | 56 MB/s | 499 MB/s | 526 MB/s | 885 MB/s | +| 64 KB | 52 MB/s | 1,729 MB/s | 1,684 MB/s | 2,037 MB/s | +| 1 MB | 51 MB/s | 1,550 MB/s | 4,036 MB/s | 1,829 MB/s | +| 10 MB | 50 MB/s | 1,497 MB/s | 4,946 MB/s | 1,877 MB/s | + +## Security + +The underlying `blake3` Rust crate targets algorithmic constant time. However, the JavaScript boundary (via napi-rs or WASM) introduces non-determinism from the V8 runtime that is outside our control. For absolute security, use the `blake3` Rust crate directly in a Rust program. + +## See also + +- [@noble/hashes](https://github.com/paulmillr/noble-hashes) | pure JS implementation +- [awasm-noble](https://github.com/paulmillr/awasm-noble) | auditable WASM implementation +- [blake3-napi](https://github.com/UneBaguette/blake3-napi) | native Node.js addon, faster for large inputs diff --git a/scripts/tpl/index.d.ts b/scripts/tpl/index.d.ts new file mode 100644 index 0000000..2673333 --- /dev/null +++ b/scripts/tpl/index.d.ts @@ -0,0 +1,3 @@ +// @ts-nocheck + +export { Hasher, deriveKey, hash, hashXof, keyedHash } from './bundler/blake3_wasm_rs'; \ No newline at end of file diff --git a/scripts/tpl/index.js.template b/scripts/tpl/index.js.template new file mode 100644 index 0000000..5c6032a --- /dev/null +++ b/scripts/tpl/index.js.template @@ -0,0 +1,4 @@ +// blake3-wasm-rs. Auto-generated entry point +// Do not edit directly! + +export { Hasher, deriveKey, hash, hashXof, keyedHash } from './bundler/blake3_wasm_rs.js'; diff --git a/src/lib.rs b/src/lib.rs index 59cd17e..b7e0e57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,13 @@ use wasm_bindgen::prelude::*; +#[cfg(all( + not(target_feature = "atomics"), + target_family = "wasm", + feature = "talc" +))] +#[global_allocator] +static TALC: talc::wasm::WasmDynamicTalc = talc::wasm::new_wasm_dynamic_allocator(); + /// Hash data and return a 32-byte BLAKE3 digest. #[wasm_bindgen] pub fn hash(value: &[u8]) -> Vec { @@ -8,7 +16,7 @@ pub fn hash(value: &[u8]) -> Vec { /// Hash data with variable-length output (XOF mode). /// Returns `out_len` bytes of BLAKE3 extended output. -#[wasm_bindgen] +#[wasm_bindgen(js_name = "hashXof")] pub fn hash_xof(data: &[u8], out_len: usize) -> Vec { let mut out = vec![0u8; out_len]; let mut reader = blake3::Hasher::new().update(data).finalize_xof(); @@ -20,7 +28,7 @@ pub fn hash_xof(data: &[u8], out_len: usize) -> Vec { /// Compute a keyed BLAKE3 hash (MAC). Key must be exactly 32 bytes. /// Throws if the key length is wrong. -#[wasm_bindgen] +#[wasm_bindgen(js_name = "keyedHash")] pub fn keyed_hash(data: &[u8], key: &[u8]) -> Result, JsError> { let key: &[u8; 32] = key .try_into() @@ -31,7 +39,7 @@ pub fn keyed_hash(data: &[u8], key: &[u8]) -> Result, JsError> { /// Derive a 32-byte key from a context string and key material. /// Context should be a hardcoded, globally unique, application-specific string. -#[wasm_bindgen] +#[wasm_bindgen(js_name = "deriveKey")] pub fn derive_key(context: &str, key_material: &[u8]) -> Vec { blake3::derive_key(context, key_material).to_vec() } @@ -57,6 +65,7 @@ impl Hasher { /// Create a keyed hasher (MAC mode). Key must be exactly 32 bytes. /// Throws if the key length is wrong. + #[wasm_bindgen(js_name = "newKeyed")] pub fn new_keyed(key: &[u8]) -> Result { let key: &[u8; 32] = key .try_into() @@ -68,6 +77,7 @@ impl Hasher { /// Create a hasher in derive-key mode. /// Context should be a hardcoded, globally unique, application-specific string. /// Feed key material via `update()`, then call `finalize()`. + #[wasm_bindgen(js_name = "newDeriveKey")] pub fn new_derive_key(context: &str) -> Hasher { Hasher(blake3::Hasher::new_derive_key(context)) } @@ -83,6 +93,7 @@ impl Hasher { } /// Return `out_len` bytes of extended output (XOF mode). Non-destructive. + #[wasm_bindgen(js_name = "finalizeXof")] pub fn finalize_xof(&self, out_len: usize) -> Vec { let mut out = vec![0u8; out_len]; self.0.finalize_xof().fill(&mut out); @@ -92,6 +103,7 @@ impl Hasher { /// Finalize the hash and reset the hasher in one call. /// Useful for hashing multiple inputs sequentially without creating new instances. + #[wasm_bindgen(js_name = "finalizeAndReset")] pub fn finalize_and_reset(&mut self) -> Vec { let out = self.0.finalize().as_bytes().to_vec(); self.0.reset();