Guide

Getting Started

Install the package and hash one string. Every algorithm answers with the same result shape
The API and the tool list can still move. Pin exact versions if you build on it now.This code has never had a security audit. Do not use it in production, with real funds or with sensitive data. It is meant for agents, puzzles and local experiments only. It comes as is, without warranty of any kind, and the authors are not liable for any loss, as the MIT license states. Anything that matters wants an audited library.

Why this exists

Ask a model for the SHA-256 of a string. It gives you 64 hex characters, very confidently, and they're wrong. Hashing is the one thing a language model can't fake, so it should call something that actually hashes. @agntn/hashes is that something, and it's the same thing in your TypeScript and in your terminal.

It also has the hashes chains quietly depend on. Keccak-256 with its pre-SHA-3 padding, HASH160, double SHA-256, BLAKE2b cut to 32 or 28 bytes, SHA-512Half. Those are the ones people get subtly wrong, and subtly wrong means a different address.

No HTTP, no keys, no telemetry, no hashing dependency. Every digest is plain TypeScript in src/core/, and the library imports nothing from node:*. That's why this site can run it in your browser.

Install

shell
pnpm add @agntn/hashes

Node.js 26 or newer. It needs Uint8Array with native hex and base64, and 26 is where that stopped being a question.

First call

tsfirst.ts
import { create } from "@agntn/hashes";

const result = create("sha256").hash("abc");
result.digest; // "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
result.digestLength; // 32

create(name) wants the exact registry key and hands back one cached instance. resolveAlgorithm("SHA-256") forgives case and punctuation and takes the label too, then wants an exact match. No prefixes, no fuzzy guess. A typo is an UnknownAlgorithmError, and its message lists every name that does exist. Half a name gets only what it could have meant: sha3 names sha3-256 and sha3-512.

The shape

Every hash() returns this, whatever the algorithm:

ts
interface HashResult {
  digest: string | Uint8Array; // bytes only with encoding "binary"
  algorithm: string; // "sha256"
  operation: "hash" | "hmac";
  encoding: "hex" | "base64" | "base64url" | "binary";
  digestLength: number; // bytes of the raw digest
  options: Record<string, unknown>; // what the digest depends on: encoding, salt, cost, seed
}

options is the part people skip and then regret. For scrypt it holds the salt it drew and the cost it ran with, and without those the digest is a string that looks random and that you can never reproduce.

What ships

algorithms()46 algorithms · listing order
OptionsSecurity
SHA-256sha256Cryptographic256-bitHMAC yesno options128-bit collision resistance
SHA-384sha384Cryptographic384-bitHMAC yesno options192-bit collision resistance
SHA-512sha512Cryptographic512-bitHMAC yesno options256-bit collision resistance
SHA-224sha224Cryptographic224-bitHMAC yesno options112-bit collision resistance
SHA-512/224sha512-224Cryptographic224-bitHMAC yesno options112-bit collision resistance
SHA-512/256sha512-256Cryptographic256-bitHMAC yesno options128-bit collision resistance
SHA-512Halfsha512-halfCryptographic256-bitHMAC nono options128-bit collision resistance
SHA3-256sha3-256Cryptographic256-bitHMAC yesno options128-bit collision resistance
SHA3-512sha3-512Cryptographic512-bitHMAC yesno options256-bit collision resistance
Keccak-256keccak256Cryptographic256-bitHMAC yesno options128-bit collision resistance
BLAKE2bblake2bCryptographic512-bitHMAC yesno options256-bit collision resistance
BLAKE2b-256blake2b-256Cryptographic256-bitHMAC nono options128-bit collision resistance
BLAKE2b-224blake2b-224Cryptographic224-bitHMAC nono options112-bit collision resistance
BLAKE2sblake2sCryptographic256-bitHMAC yesno options128-bit collision resistance
BLAKE3blake3Cryptographic256-bitHMAC nono options128-bit security against collisions and preimages
BLAKE-256blake256Cryptographic256-bitHMAC nono options128-bit collision resistance
RIPEMD-160ripemd160Cryptographic160-bitHMAC yesno options80-bit collision resistance
RIPEMD-320ripemd320Cryptographic320-bitHMAC yesno optionsNo stronger than RIPEMD-160: the wider digest adds no collision resistance
HASH160hash160Cryptographic160-bitHMAC nono options80-bit collision resistance
HASH256 (double SHA-256)hash256Cryptographic256-bitHMAC nono options128-bit collision resistance
WhirlpoolwhirlpoolCryptographic512-bitHMAC yesno optionsNo attack on the full hash
MD5md5Legacy128-bitHMAC yesno optionsBROKEN: collision attacks known since 2004
MD4md4Legacy128-bitHMAC yesno optionsBROKEN: practical collisions since 1996
MD2md2Legacy128-bitHMAC yesno optionsBROKEN: preimages in 2^73 and collisions in 2^63.3 (RFC 6149)
NTLMntlmLegacy128-bitHMAC nono optionsBROKEN as a password hash: no salt and no cost
SHA-1sha1Legacy160-bitHMAC yesno optionsBROKEN: practical collision attack (SHAttered)
SHA-0sha0Legacy160-bitHMAC yesno optionsBROKEN: full collision found in 2004
RIPEMD-128ripemd128Legacy128-bitHMAC yesno options64-bit collision resistance
RIPEMD-256ripemd256Legacy256-bitHMAC yesno optionsNo stronger than RIPEMD-128: the wider digest adds no collision resistance
HAVALhavalLegacy128-bitHMAC yespasses?, bits?BROKEN: practical collisions with 3 passes (2^29
TigertigerLegacy192-bitHMAC yesno optionsNo practical attack on all 24 rounds
CRC-32crc32Non-cryptographic32-bitHMAC novariant?NOT for security: error-detection checksum only
CRC-64crc64Non-cryptographic64-bitHMAC novariant?NOT for security: error-detection checksum only
CRC-24crc24Non-cryptographic24-bitHMAC novariant?NOT for security: error-detection checksum only
CRC-16/XMODEMcrc16-xmodemNon-cryptographic16-bitHMAC nono optionsNOT for security
Adler-32adler32Non-cryptographic32-bitHMAC nono optionsNOT for security: weaker than CRC-32 on short inputs
xxHashxxhashNon-cryptographic64-bitHMAC nobits?, seed?NOT for security: fast hash for hash tables
FNV-1a (64-bit)fnv1aNon-cryptographic64-bitHMAC nono optionsNOT for security: simple hash for hash tables
scryptscryptPasswordvariableHMAC nosalt?, N?, r?, p?, keyLength?Memory-hard KDF
PBKDF2pbkdf2PasswordvariableHMAC nosalt?, iterations?, digest?, keyLength?OWASP Password Storage Cheat Sheet: >=600000 iterations with HMAC-SHA256
HKDFhkdfCryptographicvariableHMAC nosalt?, info?, digest?, keyLength?Not for passwords: HKDF adds no cost
EVP_BytesToKeyevp-bytestokeyPasswordvariableHMAC nosalt?, digest?, iterations?, keyLength?, ivLength?Weak: one fast hash per block
Argon2idargon2idPasswordvariableHMAC nosalt?, memory?, iterations?, parallelism?, keyLength?, secret?, associatedData?The primary variant of RFC 9106
Argon2iargon2iPasswordvariableHMAC nosalt?, memory?, iterations?, parallelism?, keyLength?, secret?, associatedData?Its reads leak nothing to a side channel
Argon2dargon2dPasswordvariableHMAC nosalt?, memory?, iterations?, parallelism?, keyLength?, secret?, associatedData?Its reads follow the data and can leak through a side channel
bcryptbcryptPassword184-bitHMAC nosalt?, cost?A password past 72 bytes is refused
read from the registry in your browser / no networka name with ? is optional

Every row comes from create(name).info(), read in your browser. The HMAC column says which ones take a key. The rest refuse it with an error, they don't quietly hash without it.

Errors

ts
import { HashError, InvalidOptionError, UnknownAlgorithmError, create } from "@agntn/hashes";

try {
  create("scrypt").hash("password", { N: 1000 });
} catch (error) {
  if (error instanceof InvalidOptionError) {
    error.option; // "N"
    error.reason; // "must be a power of 2 and >= 2"
  }
}

A name nobody registered is an UnknownAlgorithmError. A bad value is an InvalidOptionError with the option, the value and the reason. A required one left out is a MissingOptionError. Anything else an algorithm throws gets wrapped by normalizeError into a HashError that names the algorithm. The CLI prints the message on one line and exits 1. No stack trace, the input was wrong, not you.

Next