runtime:hashing

Digests, checksums, MACs and password hashing. crypto.subtle is the WebCrypto standard; this is the rest — the algorithms it has no name for, hashing that runs incrementally, encoded output, and passwords.

Capability: none

Hashing reads nothing and reaches nothing, so every function here works under nothing granted. The exception is password.hash(), which draws a random salt and so needs Entropy; password.verify() needs nothing. Status: Available.

Import

JavaScript
import { hash, Hasher, hashStream, hmac, timingSafeEqual, password } from "runtime:hashing";

// Or the default aggregate:
import hashing from "runtime:hashing";

Exports

ExportTypeDescription
hashfunctionhash(algorithm, data, encoding?) — the digest, in one call.
Hasherclassnew Hasher(algorithm) — a hash across many chunks.
hashStreamasync functionhashStream(algorithm, stream, encoding?).
hmacfunctionhmac(algorithm, key, data, encoding?) — RFC 2104, synchronous.
timingSafeEqualfunctiontimingSafeEqual(a, b) — constant-time comparison.
passwordobjecthash(), verify(), needsRehash().

data, key and the comparands are a string (hashed as UTF-8), an ArrayBuffer, or a view. encoding is "hex", "base64" or "base64url" for a string; omit it for a Uint8Array.

Algorithms

AlgorithmOutputNotes
sha1 sha256 sha384 sha51220 / 32 / 48 / 64 BAlso in crypto.subtle.
sha3-224 sha3-256 sha3-384 sha3-51228 / 32 / 48 / 64 B
blake332 BFast. The usual choice for large content.
md516 BInterop only — S3 ETags, CRAM-MD5.
ripemd16020 B
xxhash64 xxhash38 BNot cryptographic. Cache keys, ETags, sharding.
crc32 crc32c4 BNot cryptographic. Checksums, framing.

Names are case-insensitive, and WebCrypto's spellings work: "SHA-256" and "sha256" are the same algorithm. hmac refuses the two non-cryptographic rows.

JavaScript
hash("sha256", "hello", "hex");   // "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
hash("blake3", "hello", "base64url");
hash("xxhash3", buffer);          // Uint8Array

Hasher

JavaScript
const h = new Hasher("sha256");
for await (const chunk of file.stream()) h.update(chunk);
h.digest("hex");
MemberTypeDescription
algorithmstringRead-only.
update(data)thisChains.
digest(encoding?)Uint8Array | stringEnds the hasher.

digest() releases the host state; calling either method again throws.

JavaScript
// The same, in one line.
await hashStream("sha256", request.body, "hex");

timingSafeEqual

For anything an attacker can submit repeatedly. === on hex strings leaks how much of the prefix was right, one request at a time.

JavaScript
const expected = hmac("sha256", secret, body, "hex");
if (!timingSafeEqual(request.headers.get("x-signature"), expected)) {
  return new Response("bad signature", { status: 401 });
}

Lengths are compared first, in ordinary time — a digest's length is fixed by its algorithm and public already.

password

Argon2id by default; bcrypt and scrypt for hashes that already exist.

JavaScript
const stored = await password.hash(input);       // "$argon2id$v=19$m=19456,t=2,p=1$…"
await password.verify(input, stored);
OptionApplies toDefault
algorithmall"argon2id" · "argon2i" "argon2d" "bcrypt" "scrypt"
memoryCostargon219456 KiB
timeCostargon22 passes
parallelismargon2, scrypt1
costbcrypt, scrypt12 rounds log₂ / 17 N log₂
blockSizescrypt8
saltall16 random bytes

Defaults follow the OWASP Password Storage Cheat Sheet.

MethodTypeCapability
hash(input, options?)Promise<string>Entropy
verify(input, stored)Promise<boolean>
needsRehash(stored, options?)boolean

The stored string carries the algorithm, parameters and salt, and verification reads them from it — so raising the cost never invalidates existing hashes.

JavaScript
if (await password.verify(input, user.hash)) {
  if (password.needsRehash(user.hash)) user.hash = await password.hash(input);
}
Two things to know

These are slow on the thread that calls them — that is the mechanism. Put a queue in front of a login endpoint. And bcrypt refuses a password past 71 bytes rather than truncating it; verification still truncates, since a stored hash may have been written by an implementation that did.

Last updated on
Edit this page