Project Icon

hash-wasm

快速轻量级的WebAssembly哈希函数库

hash-wasm是一个基于WebAssembly的高性能哈希函数库,支持浏览器和Node.js环境。该库采用优化的WebAssembly实现,计算速度显著优于同类库。支持MD5、SHA系列、Argon2等主流哈希算法,具有轻量级、支持大数据流、模块化设计等特点。适用于要求高效哈希计算的Web和Node.js应用场景。

hash-wasm

npm包 包大小 codecov 构建状态 JSDelivr下载量

Hash-WASM是一个⚡超快速⚡的哈希函数库,适用于浏览器和Node.js。 它使用经过手动优化的WebAssembly二进制文件来计算哈希值,比其他库更快。

支持的算法

名称打包大小(gzip压缩后)
Adler-323 kB
Argon2: Argon2d, Argon2i, Argon2id (v1.3)11 kB
bcrypt11 kB
BLAKE2b6 kB
BLAKE2s5 kB
BLAKE39 kB
CRC32, CRC32C3 kB
HMAC-
MD44 kB
MD54 kB
PBKDF2-
RIPEMD-1605 kB
scrypt10 kB
SHA-15 kB
SHA-2: SHA-224, SHA-2567 kB
SHA-2: SHA-384, SHA-5128 kB
SHA-3: SHA3-224, SHA3-256, SHA3-384, SHA3-5124 kB
Keccak-224, Keccak-256, Keccak-384, Keccak-5124 kB
SM34 kB
Whirlpool6 kB
xxHash323 kB
xxHash644 kB
xxHash37 kB
xxHash1288 kB

特性

  • 比其他JS / WASM实现快得多(见下方基准测试
  • 轻量级。参见上表
  • 从高度优化的C语言算法编译而来
  • 支持所有现代浏览器、Node.js和Deno
  • 支持大型数据流
  • 支持UTF-8字符串和类型化数组
  • 支持分块输入流
  • 模块化架构(算法被编译成独立的WASM二进制文件)
  • WASM模块以base64字符串形式打包(没有链接问题)
  • 支持树摇(Webpack只打包你使用的哈希算法)
  • 无需Webpack或其他打包工具即可工作
  • 包含TypeScript类型定义
  • 可在Web Workers中工作
  • 零依赖
  • 支持使用多个状态进行并发哈希计算
  • 支持保存和加载哈希的内部状态(分段哈希和回溯)
  • 所有算法都有单元测试
  • 100%开源且透明的构建过程
  • 易于使用的基于Promise的API

安装

npm i hash-wasm

也可以直接在HTML中使用(通过jsDelivr):

<!-- 将所有算法加载到全局变量 `hashwasm` 中 -->
<script src="https://cdn.jsdelivr.net/npm/hash-wasm@4"></script>

<!-- 将单个算法加载到全局变量 `hashwasm` 中 -->
<script src="https://cdn.jsdelivr.net/npm/hash-wasm@4/dist/md5.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/hash-wasm@4/dist/hmac.umd.min.js"></script>

示例

演示应用

哈希计算器 - 源代码

使用HTML5 File API的MD5文件哈希计算器

使用简写形式

这是计算哈希的最简单和最快速的方法。当输入缓冲区已经在内存中时使用它。

import { md5, sha1, sha512, sha3 } from 'hash-wasm';

async function run() {
  console.log('MD5:', await md5('demo'));

  const int8Buffer = new Uint8Array([0, 1, 2, 3]);
  console.log('SHA1:', await sha1(int8Buffer));
  console.log('SHA512:', await sha512(int8Buffer));

  const int32Buffer = new Uint32Array([1056, 641]);
  console.log('SHA3-256:', await sha3(int32Buffer, 256));
}

run();

* 参见字符串编码陷阱

** 参见API参考

使用流式输入的高级用法

createXXXX() 函数创建具有单独状态的新WASM实例,可用于并行计算多个哈希值。与重用相同WASM实例和状态进行多次计算的简写函数(如md5())相比,它们的速度较慢。因此,当数据已经在内存中时,始终首选简写形式。

为了获得最佳性能,避免在循环中调用createXXXX()函数。当顺序计算多个哈希值时,可以使用init()函数在运行之间重置内部状态。这比使用createXXXX()创建新实例更快。

import { createSHA1 } from 'hash-wasm';

async function run() {
  const sha1 = await createSHA1();
  sha1.init();

  while (hasMoreData()) {
    const chunk = readChunk();
    sha1.update(chunk);
  }

  const hash = sha1.digest('binary'); // 返回Uint8Array
  console.log('SHA1:', hash);
}

run();

* 参见字符串编码陷阱

** 参见API参考

使用Argon2对密码进行哈希处理

选择参数的推荐过程可以在这里找到:https://tools.ietf.org/html/draft-irtf-cfrg-argon2-04#section-4

import { argon2id, argon2Verify } from 'hash-wasm';

async function run() {
  const salt = new Uint8Array(16);
  window.crypto.getRandomValues(salt);

  const key = await argon2id({
    password: 'pass',
    salt, // salt是包含随机字节的缓冲区
    parallelism: 1,
    iterations: 256,
    memorySize: 512, // 使用512KB内存
    hashLength: 32, // 输出大小 = 32字节
    outputType: 'encoded', // 返回包含验证密钥所需参数的标准编码字符串
  });

  console.log('派生密钥:', key);

  const isValid = await argon2Verify({
    password: 'pass',
    hash: key,
  });

  console.log(isValid ? '密码有效' : '密码无效');
}

run();

* 参见字符串编码陷阱

** 参见API参考

使用bcrypt对密码进行哈希处理

import { bcrypt, bcryptVerify } from 'hash-wasm';

async function run() {
  const salt = new Uint8Array(16);
  window.crypto.getRandomValues(salt);

  const key = await bcrypt({
    password: 'pass',
    salt, // salt是包含16个随机字节的缓冲区
    costFactor: 11,
    outputType: 'encoded', // 返回包含验证密钥所需参数的标准编码字符串
  });

  console.log('派生密钥:', key);

  const isValid = await bcryptVerify({
    password: 'pass',
    hash: key,
  });

  console.log(isValid ? '密码有效' : '密码无效');
}

run();

* 参见字符串编码陷阱

** 参见API参考

计算HMAC

所有支持的哈希函数都可用于计算HMAC。为了获得最佳性能,避免在循环中调用createXXXX()(参见上面的"使用流式输入的高级用法"部分)

import { createHMAC, createSHA3 } from 'hash-wasm';

async function run() {
  const hashFunc = createSHA3(224); // SHA3-224
  const hmac = await createHMAC(hashFunc, 'key');

  const fruits = ['apple', 'raspberry', 'watermelon'];
  console.log('输入:', fruits);

  const codes = fruits.map(data => {
    hmac.init();
    hmac.update(data);
    return hmac.digest();
  });

  console.log('HMAC:', codes);
}

run();

* 参见字符串编码陷阱

** 参见API参考

计算PBKDF2

所有支持的哈希函数都可用于计算PBKDF2。为了获得最佳性能,避免在循环中调用createXXXX()(参见上面的"使用流式输入的高级用法"部分)

import { pbkdf2, createSHA1 } from 'hash-wasm';

async function run() {
  const salt = new Uint8Array(16);
  window.crypto.getRandomValues(salt);

  const key = await pbkdf2({
    password: 'password',
    salt,
    iterations: 1000,
    hashLength: 32,
    hashFunction: createSHA1(),
    outputType: 'hex',
  });

  console.log('派生密钥:', key);
}

run();

* 参见字符串编码陷阱

** 参见API参考

字符串编码陷阱

你应该注意到,一个给定的字符串可能有多种UTF-8表示:

'\u00fc' // 编码ü字符
'u\u0308' // 也编码ü字符

'\u00fc' === 'u\u0308' // false
'ü' === 'ü' // false

此库中定义的所有算法都依赖于输入字符串的二进制表示。因此,强烈建议在将字符串传递给hash-wasm之前对其进行规范化。你可以使用内置的String函数normalize()来实现这一点:

'\u00fc'.normalize() === 'u\u0308'.normalize() // true
const te = new TextEncoder();
te.encode('u\u0308'); // Uint8Array(3) [117, 204, 136]
te.encode('\u00fc'); // Uint8Array(2) [195, 188]

te.encode('u\u0308'.normalize('NFKC')); // Uint8Array(2) [195, 188]
te.encode('\u00fc'.normalize('NFKC')); // Uint8Array(2) [195, 188]

你可以在这里阅读更多关于这个问题的信息:https://en.wikipedia.org/wiki/Unicode_equivalence

可恢复的哈希计算

你可以使用.save()函数保存哈希的当前内部状态。这个状态可以写入磁盘或存储在内存的其他位置。 然后你可以使用.load(state)函数将该状态重新加载到哈希的新实例中,或者加载回同一个实例。

这允许你将哈希文件的工作跨多个进程进行(例如在像AWS Lambda这样执行时间有限的环境中,大型作业需要跨多个调用分割),或者将哈希回退到流中的早期点。例如,第一个进程可以:

// 第一个进程开始哈希计算
const md5 = await createMD5();
md5.init();
md5.update("Hello, ");
const state = md5.save(); // 保存这个状态

// 第二个进程从存储的状态恢复哈希计算
const md5 = await createMD5();
md5.load(state);
md5.update("world!");
console.log(md5.digest()); // 输出 6cd3556deb0da54bca060b4c39479839 = md5("Hello, world!")

注意,保存和加载进程必须运行兼容版本的哈希函数(即在保存和加载进程中使用的hash-wasm版本之间哈希函数没有变化)。如果保存的状态不兼容,load()将抛出异常。

保存的状态可能包含有关输入的信息,包括明文输入字节,因此从安全角度来看,它必须与输入数据本身一样谨慎处理。


浏览器支持


ChromeSafariFirefoxEdgeIENode.jsDeno
57+11+53+16+不支持8+1+

基准测试

你可以在这里进行自己的测量:链接

测量了两种场景:

  • 短格式的吞吐量(输入大小 = 32字节)
  • 长格式的吞吐量(输入大小 = 1MB)

结果:

MD5吞吐量(32字节)吞吐量(1MB)
hash-wasm 4.4.157.43 MB/s596.64 MB/s
spark-md5 3.0.1 (来自npm)28.08 MB/s (慢2.0倍)110.12 MB/s (慢5.4倍)
md5-wasm 2.0.0 (来自npm)16.49 MB/s (慢3.4倍)74.43 MB/s (慢8.0倍)
crypto-js 4.0.0 (来自npm)3.80 MB/s (慢15倍)26.70 MB/s (慢22倍)
node-forge 0.10.0 (来自npm)9.28 MB/s (慢6.2倍)12.27 MB/s (慢49倍)
md5 2.3.0 (来自npm)7.66 MB/s (慢7.5倍)11.42 MB/s (慢52倍)

SHA1吞吐量(32字节)吞吐量(1MB)
hash-wasm 4.4.147.97 MB/s649.13 MB/s
jsSHA 3.2.0 (来自npm)6.15 MB/s (慢7.8倍)46.13 MB/s (慢14倍)
crypto-js 4.0.0 (来自npm)4.10 MB/s (慢12倍)40.36 MB/s (慢16倍)
sha1 1.1.1 (来自npm)6.86 MB/s (慢7.0倍)12.46 MB/s (慢52倍)
node-forge 0.10.0 (来自npm)8.71 MB/s (慢5.5倍)12.86 MB/s (慢50倍)

SHA256吞吐量(32字节)吞吐量(1MB)
hash-wasm 4.4.135.67 MB/s254.40 MB/s
sha256-wasm 2.1.2 (来自npm)17.83 MB/s (慢2倍)164.13 MB/s (慢1.5倍)
jsSHA 3.2.0 (来自npm)5.57 MB/s (慢6.4倍)35.81 MB/s (慢7.1倍)
crypto-js 4.0.0 (来自npm)3.51 MB/s (慢10倍)36.48 MB/s (慢7倍)
node-forge 0.10.0 (来自npm)6.81 MB/s (慢5.2倍)11.91 MB/s (慢21倍)

SHA3-512吞吐量(32字节)吞吐量(1MB)
hash-wasm 4.4.122.91 MB/s177.16 MB/s
sha3-wasm 1.0.0 (来自npm)7.16 MB/s (慢3.2倍)74.75 MB/s (慢2.4倍)
sha3 2.1.4 (来自npm)2.00 MB/s (慢11倍)6.48 MB/s (慢27倍)
jsSHA 3.2.0 (来自npm)0.93 MB/s (慢24倍)2.09 MB/s (慢85倍)

XXHash64吞吐量(32字节)吞吐量(1MB)
hash-wasm 4.4.188.33 MB/s12 012.74 MB/s
xxhash-wasm 0.4.1 (来自npm)28.44 MB/s (慢3.1倍)11 296.84 MB/s
xxhashjs 0.2.2 (来自npm)0.37 MB/s (慢239倍)17.95 MB/s (慢669倍)

PBKDF2-SHA512 - 1000次迭代每秒操作次数(16字节)
hash-wasm 4.4.1348 ops
pbkdf2 3.1.1 (来自npm)55 ops (慢6.3倍)
crypto-js 4.0.0 (来自npm)13 ops (慢27倍)

Argon2id (m=512, t=8, p=1)每秒操作次数(16字节)
hash-wasm 4.4.1256 ops
argon2-browser 1.15.3 (来自npm)104 ops (慢2.5倍)
argon2-wasm 0.9.0 (来自npm)101 ops (慢2.5倍)
argon2-wasm-pro 1.1.0 (来自npm)100 ops (慢2.5倍)

* 这些测量是在Kaby Lake桌面CPU上使用Chrome v89进行的。

API

type IDataType = string | Buffer | Uint8Array | Uint16Array | Uint32Array;

// 所有函数返回十六进制格式的哈希值
adler32(data: IDataType): Promise<string>
blake2b(data: IDataType, bits?: number, key?: IDataType): Promise<string> // 默认为512位
blake2s(data: IDataType, bits?: number, key?: IDataType): Promise<string> // 默认为256位
blake3(data: IDataType, bits?: number, key?: IDataType): Promise<string> // 默认为256位
crc32(data: IDataType): Promise<string>
crc32c(data: IDataType): Promise<string>
keccak(data: IDataType, bits?: 224 | 256 | 384 | 512): Promise<string> // 默认为512位
md4(data: IDataType): Promise<string>
md5(data: IDataType): Promise<string>
ripemd160(data: IDataType): Promise<string>
sha1(data: IDataType): Promise<string>
sha224(data: IDataType): Promise<string>
sha256(data: IDataType): Promise<string>
sha3(data: IDataType, bits?: 224 | 256 | 384 | 512): Promise<string> // 默认为512位
sha384(data: IDataType): Promise<string>
sha512(data: IDataType): Promise<string>
sm3(data: IDataType): Promise<string>
whirlpool(data: IDataType): Promise<string>
xxhash32(data: IDataType, seed?: number): Promise<string>
xxhash64(data: IDataType, seedLow?: number, seedHigh?: number): Promise<string>
xxhash3(data: IDataType, seedLow?: number, seedHigh?: number): Promise<string>
xxhash128(data: IDataType, seedLow?: number, seedHigh?: number): Promise<string>

interface IHasher {
  init: () => IHasher;
  update: (data: IDataType) => IHasher;
  digest: (outputType: 'hex' | 'binary') => string | Uint8Array; // 默认返回十六进制字符串
  save: () => Uint8Array; // 返回内部状态以供后续恢复
  load: (state: Uint8Array) => IHasher; // 加载先前保存的内部状态
  blockSize: number; // 以字节为单位
  digestSize: number; // 以字节为单位
}
createAdler32(): Promise<IHasher>
createBLAKE2b(bits?: number, key?: IDataType): Promise<IHasher> // 默认为512位
createBLAKE2s(bits?: number, key?: IDataType): Promise<IHasher> // 默认为256位
createBLAKE3(bits?: number, key?: IDataType): Promise<IHasher> // 默认为256位
createCRC32(): Promise<IHasher>
createCRC32C(): Promise<IHasher>
createKeccak(bits?: 224 | 256 | 384 | 512): Promise<IHasher> // 默认为512位
createMD4(): Promise<IHasher>
createMD5(): Promise<IHasher>
createRIPEMD160(): Promise<IHasher>
createSHA1(): Promise<IHasher>
createSHA224(): Promise<IHasher>
createSHA256(): Promise<IHasher>
createSHA3(bits?: 224 | 256 | 384 | 512): Promise<IHasher> // 默认为512位
createSHA384(): Promise<IHasher>
createSHA512(): Promise<IHasher>
createSM3(): Promise<IHasher>
createWhirlpool(): Promise<IHasher>
createXXHash32(seed: number): Promise<IHasher>
createXXHash64(seedLow: number, seedHigh: number): Promise<IHasher>
createXXHash3(seedLow: number, seedHigh: number): Promise<IHasher>
createXXHash128(seedLow: number, seedHigh: number): Promise<IHasher>

createHMAC(hashFunction: Promise<IHasher>, key: IDataType): Promise<IHasher>

pbkdf2({
  password: IDataType, // 要哈希的密码(或消息)
  salt: IDataType, // 盐(通常包含随机字节)
  iterations: number, // 执行迭代的次数
  hashLength: number, // 输出大小(字节)
  hashFunction: Promise<IHasher>, // 像createSHA1()这样函数的返回值
  outputType?: 'hex' | 'binary', // 默认返回十六进制字符串
}): Promise<string | Uint8Array>

scrypt({
  password: IDataType, // 要哈希的密码(或消息)
  salt: IDataType, // 盐(通常包含随机字节)
  costFactor: number, // CPU/内存成本 - 必须是2的幂(如1024)
  blockSize: number, // 块大小参数(通常使用8)
  parallelism: number, // 并行度
  hashLength: number, // 输出大小(字节)
  outputType?: 'hex' | 'binary', // 默认返回十六进制字符串
}): Promise<string | Uint8Array>

interface IArgon2Options {
  password: IDataType; // 要哈希的密码(或消息)
  salt: IDataType; // 盐(通常包含随机字节)
  secret?: IDataType; // 用于键控哈希的密钥
  iterations: number; // 执行迭代的次数
  parallelism: number; // 并行度
  memorySize: number; // 要使用的内存量(以kibibytes为单位,1024字节)
  hashLength: number; // 输出大小(字节)
  outputType?: 'hex' | 'binary' | 'encoded'; // 默认返回十六进制字符串
}

argon2i(options: IArgon2Options): Promise<string | Uint8Array>
argon2d(options: IArgon2Options): Promise<string | Uint8Array>
argon2id(options: IArgon2Options): Promise<string | Uint8Array>

argon2Verify({
  password: IDataType, // 密码
  secret?: IDataType, // 哈希创建时使用的密钥
  hash: string, // 编码后的哈希
}): Promise<boolean>

bcrypt({
  password: IDataType, // 密码
  salt: IDataType, // 盐(16字节长 - 通常包含随机字节)
  costFactor: number, // 执行迭代的次数(4 - 31)
  outputType?: 'hex' | 'binary' | 'encoded', // 默认返回编码字符串
}): Promise<string | Uint8Array>

bcryptVerify({
  password: IDataType, // 密码
  hash: string, // 编码后的哈希
}): Promise<boolean>

未来计划
=====

- 添加更多知名算法
- 编写一个多填充程序,保持打包大小小,并支持运行包含较新WASM指令的二进制文件
- 使用WebAssembly批量内存操作
- 使用WebAssembly SIMD指令(预期性能提升10-20%)
- 在可能的情况下启用多线程(如Argon2)
项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号