Skip to main content
Bun 实现了 node:crypto 中的 createHashcreateHmac 函数, 以及下面记录的 Bun 原生 API。

Bun.password

Bun.password 是一组使用多种密码学安全算法进行密码哈希和验证的实用函数集合。
const password = "super-secure-pa$$word";

const hash = await Bun.password.hash(password);
// => $argon2id$v=19$m=65536,t=2,p=1$tFq+9AVr1bfPxQdh6E8DQRhEXg/M/SqYCNu6gVdRRNs$GzJ8PuBi+K+BVojzPfS5mjnC8OpLGtv8KJqF99eP6a4

const isMatch = await Bun.password.verify(password, hash);
// => true
传递给 Bun.password.hash 的第二个参数是一个配置对象,可用于选择和配置哈希算法。
const password = "super-secure-pa$$word";

// 使用 argon2(默认)
const argonHash = await Bun.password.hash(password, {
  algorithm: "argon2id", // "argon2id" | "argon2i" | "argon2d"
  memoryCost: 4, // 内存使用量,单位为 KiB
  timeCost: 3, // 迭代次数
});

// 使用 bcrypt
const bcryptHash = await Bun.password.hash(password, {
  algorithm: "bcrypt",
  cost: 4, // 数值范围 4-31
});
用于生成哈希的算法信息会存储在哈希本身里。使用 bcrypt 时,返回的哈希以 模块化密码格式 (Modular Crypt Format) 编码,以保证与大多数现有的 bcrypt 实现兼容;使用 argon2 时,结果采用较新的 PHC 格式 编码。 verify 函数能基于输入的哈希自动检测算法并使用正确的验证方法,可正确解析 PHC 或 MCF 编码的哈希算法。
const password = "super-secure-pa$$word";

const hash = await Bun.password.hash(password, {
  /* 配置 */
});

const isMatch = await Bun.password.verify(password, hash);
// => true
所有函数也提供同步版本。请注意,这些函数计算量很大,使用阻塞 API 可能导致应用性能下降。
const password = "super-secure-pa$$word";

const hash = Bun.password.hashSync(password, {
  /* 配置 */
});

const isMatch = Bun.password.verifySync(password, hash);
// => true

盐值

使用 Bun.password.hash 时,会自动生成盐值并包含在哈希中。

bcrypt — 模块化密码格式

在下面的 模块化密码格式 哈希示例(bcrypt 使用): 输入:
await Bun.password.hash("hello", {
  algorithm: "bcrypt",
});
输出:
$2b$10$Lyj9kHYZtiyfxh2G60TEfeqs7xkkGiEFFDi3iJGc50ZG/XJ1sxIFi;
格式由以下部分组成:
  • bcrypt 标识符:$2b
  • 轮数:$10 - 轮数的对数(log10)
  • 盐值:$Lyj9kHYZtiyfxh2G60TEfeqs7xkkGiEFFDi3iJGc50ZG/XJ1sxIFi
  • 哈希值:$GzJ8PuBi+K+BVojzPfS5mjnC8OpLGtv8KJqF99eP6a4
默认情况下,bcrypt 库会截断长度超过 72 字节的密码。在 Bun 中,如果你传递给 Bun.password.hash 的密码超过 72 字节并选择使用 bcrypt 算法,则密码会先通过 SHA-512 计算哈希,再传给 bcrypt。
await Bun.password.hash("hello".repeat(100), {
  algorithm: "bcrypt",
});
因此,Bun 不会将一个 500 字节密码默默截断至 72 字节再传给 bcrypt,而是先用 SHA-512 哈希后再传递(仅在超过 72 字节时)。这是更安全的默认行为。

argon2 — PHC 格式

以下为采用 PHC 格式 的哈希 (argon2 使用): 输入:
await Bun.password.hash("hello", {
  algorithm: "argon2id",
});
输出:
$argon2id$v=19$m=65536,t=2,p=1$xXnlSvPh4ym5KYmxKAuuHVlDvy2QGHBNuI6bJJrRDOs$2YY6M48XmHn+s5NoBaL+ficzXajq2Yj8wut3r0vnrwI
格式由以下部分组成:
  • 算法:$argon2id
  • 版本:$v=19
  • 内存成本:65536
  • 迭代次数:t=2
  • 并行度:p=1
  • 盐值:$xXnlSvPh4ym5KYmxKAuuHVlDvy2QGHBNuI6bJJrRDOs
  • 哈希值:$2YY6M48XmHn+s5NoBaL+ficzXajq2Yj8wut3r0vnrwI

Bun.hash

Bun.hash 是一组用于非密码学哈希的实用工具。非密码学哈希算法优化的是计算速度而非抗碰撞性或安全性。 标准的 Bun.hash 函数使用 Wyhash 算法,根据任意长度输入生成64位哈希。
Bun.hash("some data here");
// 11562320457524636935n
输入可以是字符串、TypedArrayDataViewArrayBufferSharedArrayBuffer
const arr = new Uint8Array([1, 2, 3, 4]);

Bun.hash("some data here");
Bun.hash(arr);
Bun.hash(arr.buffer);
Bun.hash(new DataView(arr.buffer));
可选地,第二个参数可以指定整数种子。对于 64 位哈希,种子若超过 Number.MAX_SAFE_INTEGER,需用 BigInt 传入避免精度损失。
Bun.hash("some data here", 1234);
// 15724820720172937558n
更多哈希算法作为 Bun.hash 的属性提供。API 一致,只是返回值类型从 32 位的 number 改为 64 位的 bigint。
Bun.hash.wyhash("data", 1234); // 等同于 Bun.hash()
Bun.hash.crc32("data", 1234);
Bun.hash.adler32("data", 1234);
Bun.hash.cityHash32("data", 1234);
Bun.hash.cityHash64("data", 1234);
Bun.hash.xxHash32("data", 1234);
Bun.hash.xxHash64("data", 1234);
Bun.hash.xxHash3("data", 1234);
Bun.hash.murmur32v3("data", 1234);
Bun.hash.murmur32v2("data", 1234);
Bun.hash.murmur64v2("data", 1234);
Bun.hash.rapidhash("data", 1234);

Bun.CryptoHasher

Bun.CryptoHasher 是一个通用的工具类,支持增量计算字符串或二进制数据的哈希,支持多种密码学哈希算法。支持的算法包括:
  • "blake2b256"
  • "blake2b512"
  • "md4"
  • "md5"
  • "ripemd160"
  • "sha1"
  • "sha224"
  • "sha256"
  • "sha384"
  • "sha512"
  • "sha512-224"
  • "sha512-256"
  • "sha3-224"
  • "sha3-256"
  • "sha3-384"
  • "sha3-512"
  • "shake128"
  • "shake256"
const hasher = new Bun.CryptoHasher("sha256");
hasher.update("hello world");
hasher.digest();
// Uint8Array(32) [ <byte>, <byte>, ... ]
初始化后,可使用 .update() 方法增量输入数据,参数可为 stringTypedArrayArrayBuffer
const hasher = new Bun.CryptoHasher("sha256");

hasher.update("hello world");
hasher.update(new Uint8Array([1, 2, 3]));
hasher.update(new ArrayBuffer(10));
如果传入 string,可选第2个参数指定编码(默认为 'utf-8')。支持的编码如下:
编码类别支持的编码
二进制编码"base64" "base64url" "hex" "binary"
字符编码"utf8" "utf-8" "utf16le" "latin1"
旧版字符编码"ascii" "binary" "ucs2" "ucs-2"
hasher.update("hello world"); // 默认 utf8
hasher.update("hello world", "hex");
hasher.update("hello world", "base64");
hasher.update("hello world", "latin1");
数据输入完成后,调用 .digest() 计算最终哈希。默认情况下,返回的是包含哈希值的 Uint8Array
const hasher = new Bun.CryptoHasher("sha256");
hasher.update("hello world");

hasher.digest();
// => Uint8Array(32) [ 185, 77, 39, 185, 147, ... ]
.digest() 方法也可以返回字符串格式哈希,方法是指定编码:
hasher.digest("base64");
// => "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek="

hasher.digest("hex");
// => "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
或者,将哈希写入预分配的 TypedArray 实例,适合某些对性能敏感的场景。
const arr = new Uint8Array(32);

hasher.digest(arr);

console.log(arr);
// => Uint8Array(32) [ 185, 77, 39, 185, 147, ... ]

Bun.CryptoHasher 中的 HMAC

Bun.CryptoHasher 也支持计算 HMAC。需要在构造函数传入密钥。
const hasher = new Bun.CryptoHasher("sha256", "secret-key");
hasher.update("hello world");
console.log(hasher.digest("hex"));
// => "095d5a21fe6d0646db223fdf3de6436bb8dfb2fab0b51677ecf6441fcf5f2a67"
使用 HMAC 时,支持的算法较少:
  • "blake2b512"
  • "md5"
  • "sha1"
  • "sha224"
  • "sha256"
  • "sha384"
  • "sha512-224"
  • "sha512-256"
  • "sha512"
与非 HMAC 版本不同的是,HMAC 模式中 .digest() 调用后不会重置实例,再次使用同一实例会抛出错误。 支持 .copy().update()(在 .digest() 前),但 .digest() 方法不允许多次调用。
const hasher = new Bun.CryptoHasher("sha256", "secret-key");
hasher.update("hello world");

const copy = hasher.copy();
copy.update("!");
console.log(copy.digest("hex"));
// => "3840176c3d8923f59ac402b7550404b28ab11cb0ef1fa199130a5c37864b5497"

console.log(hasher.digest("hex"));
// => "095d5a21fe6d0646db223fdf3de6436bb8dfb2fab0b51677ecf6441fcf5f2a67"