Skip to main content
The Deno 2 Release Candidate is here
Learn more

Morax

WebAssembly port of SHA-1, SHA-256, Keccak-256, RIPEMD-160, and CRC-32 hashing algorithms

npm i @hazae41/morax

Node Package πŸ“¦ β€’ Deno Module πŸ¦– β€’ Next.js CodeSandbox πŸͺ£

Algorithms

  • Incremental SHA-1 from RustCrypto (sha1)
  • Incremental SHA-256 from RustCrypto (sha2)
  • Incremental Keccak-256 from RustCrypto (sha3)
  • Incremental RIPEMD-160 from RustCrypto (ripemd)
  • Incremental CRC-32 from Sam Rijs (crc32fast)

Features

  • Reproducible building
  • Pre-bundled and streamed
  • Zero-copy memory slices

Benchmarks

SHA-1

Deno

cpu: Apple M1 Max
runtime: deno 1.30.3 (arm64-darwin)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ (idx)                  β”‚ average         β”‚ minimum    β”‚ maximum     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ wasm sha1              β”‚ "9.41 ΞΌs/iter"  β”‚ "6.88 ΞΌs"  β”‚ "199.92 ΞΌs" β”‚
β”‚ webcrypto sha1         β”‚ "24.46 ΞΌs/iter" β”‚ "14.92 ΞΌs" β”‚ "360.38 ΞΌs" β”‚
β”‚ node:crypto sha1       β”‚ "20.75 ΞΌs/iter" β”‚ "13.08 ΞΌs" β”‚ "3.56 ms"   β”‚
β”‚ npm:@noble/hashes/sha1 β”‚ "19.92 ΞΌs/iter" β”‚ "15.87 ΞΌs" β”‚ "759.75 ΞΌs" β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Summary
- wasm sha1 is 2.60x faster than WebCrypto
- wasm sha1 is 2.21x faster than node:crypto
- wasm sha1 is 2.12x faster than npm:@noble/hashes

Node

cpu: Apple M1 Max
runtime: node v18.12.1 (arm64-darwin)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        (index)         β”‚     average     β”‚  minimum  β”‚   maximum   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚       wasm sha1        β”‚ '6.07 ΞΌs/iter'  β”‚ '5.37 ΞΌs' β”‚ '23.71 ΞΌs'  β”‚
β”‚     webcrypto sha1     β”‚ '12.34 ΞΌs/iter' β”‚ '9.04 ΞΌs' β”‚ '852.58 ΞΌs' β”‚
β”‚    node:crypto sha1    β”‚ '3.48 ΞΌs/iter'  β”‚ '2.83 ΞΌs' β”‚ '88.50 ΞΌs'  β”‚
β”‚ npm:@noble/hashes/sha1 β”‚ '7.34 ΞΌs/iter'  β”‚ '6.37 ΞΌs' β”‚ '746.42 ΞΌs' β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Summary
- wasm sha1 is 2.03x faster than WebCrypto
- wasm sha1 is 0.57x faster than node:crypto
- wasm sha1 is 1.21x faster than npm:@noble/hashes

Keccak-256

Deno

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ (idx)             β”‚ average         β”‚ minimum    β”‚ maximum     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ wasm keccak256    β”‚ "4.91 ΞΌs/iter"  β”‚ "4.37 ΞΌs"  β”‚ "25.37 ΞΌs"  β”‚
β”‚ npm:@noble/hashes β”‚ "60.01 ΞΌs/iter" β”‚ "57.83 ΞΌs" β”‚ "195.50 ΞΌs" β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Summary
- wasm keccak256 is 12.22x faster than npm:@noble/hashes

Node

cpu: Apple M1 Max
runtime: node v20.3.1 (arm64-darwin)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      (index)      β”‚     average     β”‚  minimum   β”‚   maximum   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  wasm keccak256   β”‚ '3.10 ΞΌs/iter'  β”‚ '2.92 ΞΌs'  β”‚ '104.00 ΞΌs' β”‚
β”‚ npm:@noble/hashes β”‚ '62.28 ΞΌs/iter' β”‚ '60.21 ΞΌs' β”‚ '166.33 ΞΌs' β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Summary
- wasm keccak256 is 20.08x faster than npm:@noble/hashes

CRC32

Deno

cpu: Apple M1 Max
runtime: deno 1.30.3 (arm64-darwin)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ (idx)      β”‚ average        β”‚ minimum   β”‚ maximum     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ wasm crc32 β”‚ "3.04 ΞΌs/iter" β”‚ "2.17 ΞΌs" β”‚ "198.04 ΞΌs" β”‚
β”‚ npm:crc-32 β”‚ "4.36 ΞΌs/iter" β”‚ "2.04 ΞΌs" β”‚ "97.96 ΞΌs"  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Summary
- wasm crc32 is 1.44x faster than npm:crc-32

Node

cpu: Apple M1 Max
runtime: node v18.12.1 (arm64-darwin)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  (index)   β”‚    average     β”‚  minimum  β”‚  maximum   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ wasm crc32 β”‚ '2.40 ΞΌs/iter' β”‚ '2.00 ΞΌs' β”‚ '53.88 ΞΌs' β”‚
β”‚ npm:crc-32 β”‚ '3.73 ΞΌs/iter' β”‚ '2.25 ΞΌs' β”‚ '1.11 ms'  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Summary
- wasm crc32 is 1.55x faster than npm:crc-32

Usage

SHA-1 / SHA-256 / Keccak-256 / RIPEMD-160 (direct)

import { Morax, sha1 } from "@hazae41/morax";

// Wait for WASM to load
await Morax.initBundledOnce()

// Data to be hashed
const hello = new TextEncoder().encode("Hello World")

// Grab the digest (Uint8Array)
const digest = sha1(hello).copyAndDispose()

CRC32 (direct)

import { Morax, crc32 } from "@hazae41/morax";

// Wait for WASM to load
await Morax.initBundledOnce()

// Data to be hashed
const hello = new TextEncoder().encode("Hello World")

// Grab the digest (number)
const digest = crc32(hello)

SHA-1 / SHA-256 / Keccak-256 / RIPEMD-160 (incremental)

import { Morax, Sha1Hasher } from "@hazae41/morax";

// Wait for WASM to load
await Morax.initBundledOnce()

// Create a hash
const hasher = new Sha1Hasher()

// Data to be hashed
const hello = new TextEncoder().encode("Hello World")

// Update the hash with your data
hasher.update(hello)

// Grab the digest (Uint8Array)
const digest = hasher.finalize().copyAndDispose()

// Update the hash another time
hasher.update(hello)

// Grab the digest (Uint8Array)
const digest2 = hasher.finalize().copyAndDispose()

// digest !== digest2
console.log(digest)
console.log(digest2)

hasher.free()

CRC32 (incremental)

import { Morax, Crc32Hasher } from "@hazae41/morax";

// Wait for WASM to load
await Morax.initBundledOnce()

// Create a hash
const hasher = new Crc32Hasher()

// Data to be hashed
const hello = new TextEncoder().encode("Hello World")

// Update the hash with your data
hasher.update(hello)

// Grab the checksum (number)
const checksum = hasher.finalize()

// Update the hash another time
hasher.update(hello)

// Grab the checksum (number)
const checksum2 = hasher.finalize()

// checksum !== checksum2
console.log(checksum)
console.log(checksum2)

hasher.free()

Building

Unreproducible building

You need to install Rust

Then, install wasm-pack

cargo install wasm-pack

Finally, do a clean install and build

npm ci && npm run build

Reproducible building

You can build the exact same bytecode using Docker, just be sure you’re on a linux/amd64 host

docker compose up --build

Then check that all the files are the same using git status

git status --porcelain

If the output is empty then the bytecode is the same as the one I commited

Automated checks

Each time I commit to the repository, the GitHub’s CI does the following:

  • Clone the repository
  • Reproduce the build using docker compose up --build
  • Throw an error if the git status --porcelain output is not empty

Each time I release a new version tag on GitHub, the GitHub’s CI does the following:

  • Clone the repository
  • Do not reproduce the build, as it’s already checked by the task above
  • Throw an error if there is a npm diff between the cloned repository and the same version tag on NPM

If a version is present on NPM but not on GitHub, do not use!