mirror of
https://github.com/UneBaguette/blake3.wasm.git
synced 2026-08-21 00:00:15 +02:00
fix: enable SIMD in build and cap XOF output length
- Merge target-feature=+simd128 into Makefile RUSTFLAGS (was being overridden by the explicit RUSTFLAGS env var) - Cap hash_xof/finalize_xof to 64KB to prevent unbounded WASM linear memory growth - Add boundary tests for XOF length validation - Update benchmarks with SIMD-enabled build
This commit is contained in:
@@ -1,2 +0,0 @@
|
|||||||
[target.wasm32-unknown-unknown]
|
|
||||||
rustflags = ["-C", "target-feature=+simd128"]
|
|
||||||
@@ -20,7 +20,7 @@ build: build-all node-esm
|
|||||||
|
|
||||||
build-all:
|
build-all:
|
||||||
@echo "Building all target..."
|
@echo "Building all target..."
|
||||||
RUSTFLAGS='-C opt-level=3' $(CARGO) build --target $(WASM_TARGET) --release --features talc
|
RUSTFLAGS='-C opt-level=3 -C target-feature=+simd128' $(CARGO) build --target $(WASM_TARGET) --release --features talc
|
||||||
@mkdir -p $(ROOT)/bundler $(ROOT)/web $(ROOT)/node
|
@mkdir -p $(ROOT)/bundler $(ROOT)/web $(ROOT)/node
|
||||||
$(WASM_BINDGEN) --target bundler --out-dir $(ROOT)/bundler $(TARGET_DIR)/$(CRATE).wasm
|
$(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 web --out-dir $(ROOT)/web $(TARGET_DIR)/$(CRATE).wasm
|
||||||
|
|||||||
@@ -12,18 +12,11 @@
|
|||||||
```sh
|
```sh
|
||||||
# lets build it!
|
# lets build it!
|
||||||
|
|
||||||
# For NodeJS
|
# normal
|
||||||
wasm-pack build --target nodejs --release
|
make build
|
||||||
|
|
||||||
# For the web!
|
# clean
|
||||||
wasm-pack build --target web --release
|
make
|
||||||
```
|
|
||||||
|
|
||||||
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
|
## Usage
|
||||||
@@ -171,11 +164,11 @@ Tested on **Ryzen 7 5800X**, Node.js v24.
|
|||||||
|
|
||||||
| Size | @noble/hashes | awasm-noble | awasm-noble (threads) | blake3-wasm |
|
| Size | @noble/hashes | awasm-noble | awasm-noble (threads) | blake3-wasm |
|
||||||
|-------|---------------|-------------|-----------------------|-------------|
|
|-------|---------------|-------------|-----------------------|-------------|
|
||||||
| 32 B | 11 MB/s | 34 MB/s | 45 MB/s | 84 MB/s |
|
| 32 B | 11 MB/s | 34 MB/s | 45 MB/s | 86 MB/s |
|
||||||
| 1 KB | 56 MB/s | 499 MB/s | 526 MB/s | 851 MB/s |
|
| 1 KB | 56 MB/s | 499 MB/s | 526 MB/s | 919 MB/s |
|
||||||
| 64 KB | 52 MB/s | 1,729 MB/s | 1,684 MB/s | 2,014 MB/s |
|
| 64 KB | 52 MB/s | 1,729 MB/s | 1,684 MB/s | 2,067 MB/s |
|
||||||
| 1 MB | 51 MB/s | 1,550 MB/s | 4,036 MB/s | 1,787 MB/s |
|
| 1 MB | 51 MB/s | 1,550 MB/s | 4,036 MB/s | 1,848 MB/s |
|
||||||
| 10 MB | 50 MB/s | 1,497 MB/s | 4,946 MB/s | 1,899 MB/s |
|
| 10 MB | 50 MB/s | 1,497 MB/s | 4,946 MB/s | 1,776 MB/s |
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
|
|||||||
+43
-9
@@ -8,6 +8,16 @@ use wasm_bindgen::prelude::*;
|
|||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static TALC: talc::wasm::WasmDynamicTalc = talc::wasm::new_wasm_dynamic_allocator();
|
static TALC: talc::wasm::WasmDynamicTalc = talc::wasm::new_wasm_dynamic_allocator();
|
||||||
|
|
||||||
|
const MAX_XOF_LEN: usize = 1 << 16; // 65_536
|
||||||
|
|
||||||
|
fn checked_xof_len(out_len: usize) -> Result<usize, JsError> {
|
||||||
|
if out_len == 0 || out_len > MAX_XOF_LEN {
|
||||||
|
return Err(JsError::new("out_len must be between 1 and 65536"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out_len)
|
||||||
|
}
|
||||||
|
|
||||||
/// Hash data and return a 32-byte BLAKE3 digest.
|
/// Hash data and return a 32-byte BLAKE3 digest.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn hash(value: &[u8]) -> Vec<u8> {
|
pub fn hash(value: &[u8]) -> Vec<u8> {
|
||||||
@@ -17,13 +27,14 @@ pub fn hash(value: &[u8]) -> Vec<u8> {
|
|||||||
/// Hash data with variable-length output (XOF mode).
|
/// Hash data with variable-length output (XOF mode).
|
||||||
/// Returns `out_len` bytes of BLAKE3 extended output.
|
/// Returns `out_len` bytes of BLAKE3 extended output.
|
||||||
#[wasm_bindgen(js_name = "hashXof")]
|
#[wasm_bindgen(js_name = "hashXof")]
|
||||||
pub fn hash_xof(data: &[u8], out_len: usize) -> Vec<u8> {
|
pub fn hash_xof(data: &[u8], out_len: usize) -> Result<Vec<u8>, JsError> {
|
||||||
|
let out_len = checked_xof_len(out_len)?;
|
||||||
let mut out = vec![0u8; out_len];
|
let mut out = vec![0u8; out_len];
|
||||||
let mut reader = blake3::Hasher::new().update(data).finalize_xof();
|
let mut reader = blake3::Hasher::new().update(data).finalize_xof();
|
||||||
|
|
||||||
reader.fill(&mut out);
|
reader.fill(&mut out);
|
||||||
|
|
||||||
out
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a keyed BLAKE3 hash (MAC). Key must be exactly 32 bytes.
|
/// Compute a keyed BLAKE3 hash (MAC). Key must be exactly 32 bytes.
|
||||||
@@ -94,11 +105,12 @@ impl Hasher {
|
|||||||
|
|
||||||
/// Return `out_len` bytes of extended output (XOF mode). Non-destructive.
|
/// Return `out_len` bytes of extended output (XOF mode). Non-destructive.
|
||||||
#[wasm_bindgen(js_name = "finalizeXof")]
|
#[wasm_bindgen(js_name = "finalizeXof")]
|
||||||
pub fn finalize_xof(&self, out_len: usize) -> Vec<u8> {
|
pub fn finalize_xof(&self, out_len: usize) -> Result<Vec<u8>, JsError> {
|
||||||
|
let out_len = checked_xof_len(out_len)?;
|
||||||
let mut out = vec![0u8; out_len];
|
let mut out = vec![0u8; out_len];
|
||||||
self.0.finalize_xof().fill(&mut out);
|
self.0.finalize_xof().fill(&mut out);
|
||||||
|
|
||||||
out
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finalize the hash and reset the hasher in one call.
|
/// Finalize the hash and reset the hasher in one call.
|
||||||
@@ -135,8 +147,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hash_xof_length() {
|
fn test_hash_xof_length() {
|
||||||
assert_eq!(hash_xof(b"hello", 64).len(), 64);
|
assert_eq!(hash_xof(b"hello", 64).unwrap().len(), 64);
|
||||||
assert_eq!(hash_xof(b"hello", 16).len(), 16);
|
assert_eq!(hash_xof(b"hello", 16).unwrap().len(), 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -199,17 +211,17 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hasher_finalize_xof() {
|
fn test_hasher_finalize_xof() {
|
||||||
let oneshot = hash_xof(b"hello", 64);
|
let oneshot = hash_xof(b"hello", 64).unwrap();
|
||||||
let mut h = Hasher::new();
|
let mut h = Hasher::new();
|
||||||
h.update(b"hello");
|
h.update(b"hello");
|
||||||
assert_eq!(h.finalize_xof(64), oneshot);
|
assert_eq!(h.finalize_xof(64).unwrap(), oneshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hasher_finalize_xof_prefix_matches_hash() {
|
fn test_hasher_finalize_xof_prefix_matches_hash() {
|
||||||
// First 32 bytes of XOF output should equal the standard hash
|
// First 32 bytes of XOF output should equal the standard hash
|
||||||
let standard = hash(b"test data");
|
let standard = hash(b"test data");
|
||||||
let xof = hash_xof(b"test data", 64);
|
let xof = hash_xof(b"test data", 64).unwrap();
|
||||||
assert_eq!(&xof[..32], standard.as_slice());
|
assert_eq!(&xof[..32], standard.as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,4 +303,26 @@ mod wasm_tests {
|
|||||||
fn test_hasher_keyed_bad_key() {
|
fn test_hasher_keyed_bad_key() {
|
||||||
assert!(Hasher::new_keyed(&[0u8; 10]).is_err());
|
assert!(Hasher::new_keyed(&[0u8; 10]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn test_hash_xof_zero_len() {
|
||||||
|
assert!(hash_xof(b"hello", 0).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn test_hash_xof_over_max() {
|
||||||
|
assert!(hash_xof(b"hello", 65_537).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn test_hasher_finalize_xof_zero_len() {
|
||||||
|
let h = Hasher::new();
|
||||||
|
assert!(h.finalize_xof(0).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn test_hasher_finalize_xof_over_max() {
|
||||||
|
let h = Hasher::new();
|
||||||
|
assert!(h.finalize_xof(65_537).is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user