diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 2d159d1..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.wasm32-unknown-unknown] -rustflags = ["-C", "target-feature=+simd128"] \ No newline at end of file diff --git a/Makefile b/Makefile index f462aad..983b235 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ build: build-all node-esm build-all: @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 $(WASM_BINDGEN) --target bundler --out-dir $(ROOT)/bundler $(TARGET_DIR)/$(CRATE).wasm $(WASM_BINDGEN) --target web --out-dir $(ROOT)/web $(TARGET_DIR)/$(CRATE).wasm diff --git a/README.md b/README.md index 3063d08..e7fa6b5 100644 --- a/README.md +++ b/README.md @@ -12,18 +12,11 @@ ```sh # lets build it! -# For NodeJS -wasm-pack build --target nodejs --release +# normal +make build -# 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"] +# clean +make ``` ## Usage @@ -171,11 +164,11 @@ 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 | 84 MB/s | -| 1 KB | 56 MB/s | 499 MB/s | 526 MB/s | 851 MB/s | -| 64 KB | 52 MB/s | 1,729 MB/s | 1,684 MB/s | 2,014 MB/s | -| 1 MB | 51 MB/s | 1,550 MB/s | 4,036 MB/s | 1,787 MB/s | -| 10 MB | 50 MB/s | 1,497 MB/s | 4,946 MB/s | 1,899 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 | 919 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,848 MB/s | +| 10 MB | 50 MB/s | 1,497 MB/s | 4,946 MB/s | 1,776 MB/s | ## Security diff --git a/src/lib.rs b/src/lib.rs index b7e0e57..2484ec7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,16 @@ use wasm_bindgen::prelude::*; #[global_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 { + 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. #[wasm_bindgen] pub fn hash(value: &[u8]) -> Vec { @@ -17,13 +27,14 @@ pub fn hash(value: &[u8]) -> Vec { /// Hash data with variable-length output (XOF mode). /// Returns `out_len` bytes of BLAKE3 extended output. #[wasm_bindgen(js_name = "hashXof")] -pub fn hash_xof(data: &[u8], out_len: usize) -> Vec { +pub fn hash_xof(data: &[u8], out_len: usize) -> Result, JsError> { + let out_len = checked_xof_len(out_len)?; let mut out = vec![0u8; out_len]; let mut reader = blake3::Hasher::new().update(data).finalize_xof(); reader.fill(&mut out); - out + Ok(out) } /// 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. #[wasm_bindgen(js_name = "finalizeXof")] - pub fn finalize_xof(&self, out_len: usize) -> Vec { + pub fn finalize_xof(&self, out_len: usize) -> Result, JsError> { + let out_len = checked_xof_len(out_len)?; let mut out = vec![0u8; out_len]; self.0.finalize_xof().fill(&mut out); - out + Ok(out) } /// Finalize the hash and reset the hasher in one call. @@ -135,8 +147,8 @@ mod tests { #[test] fn test_hash_xof_length() { - assert_eq!(hash_xof(b"hello", 64).len(), 64); - assert_eq!(hash_xof(b"hello", 16).len(), 16); + assert_eq!(hash_xof(b"hello", 64).unwrap().len(), 64); + assert_eq!(hash_xof(b"hello", 16).unwrap().len(), 16); } #[test] @@ -199,17 +211,17 @@ mod tests { #[test] 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(); h.update(b"hello"); - assert_eq!(h.finalize_xof(64), oneshot); + assert_eq!(h.finalize_xof(64).unwrap(), oneshot); } #[test] fn test_hasher_finalize_xof_prefix_matches_hash() { // First 32 bytes of XOF output should equal the standard hash 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()); } @@ -291,4 +303,26 @@ mod wasm_tests { fn test_hasher_keyed_bad_key() { 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()); + } +} \ No newline at end of file