Hash Type Identifier — JavaScript source
Identify the likely hash algorithm of a hash string by its length and character set - MD5, SHA-1/2/3, BLAKE, CRC32, NTLM, bcrypt, Argon2 and more.
This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
// Hash-type identifier - JavaScript port.
//
// Language: JavaScript
// CosmoDev polyglot showcase port of the `hash-type-identifier` tool.
// Ported from src/lib/hashIdentify.ts.
//
// Display source - part of CosmoDev's polyglot tool pages
// (dev.cosmolabs.org).
//
// Pure string classification: inspect a candidate hash's charset and length
// to suggest likely algorithms. No hashing happens here - this is pattern
// recognition over an already-computed digest. Deterministic; never throws.
// Hex candidates keyed by hex-string length (each hex char encodes 4 bits,
// so a 64-char hex digest is a 256-bit algorithm such as SHA-256).
const HEX_BY_LENGTH = {
8: ['CRC32', 'Adler-32'],
16: ['MySQL 3.x', 'CRC64'],
32: ['MD5', 'MD4', 'NTLM', 'LM', 'MD2', 'RIPEMD-128', 'HAVAL-128'],
40: ['SHA-1', 'RIPEMD-160', 'HAVAL-160', 'MySQL 5.x (SHA1(SHA1))', 'Tiger-160'],
56: ['SHA-224', 'SHA3-224', 'BLAKE2s-224', 'HAVAL-224'],
64: ['SHA-256', 'SHA3-256', 'BLAKE2s-256', 'RIPEMD-256', 'Skein-256'],
96: ['SHA-384', 'SHA3-384', 'BLAKE2b-384'],
128: ['SHA-512', 'SHA3-512', 'BLAKE2b-512', 'Whirlpool', 'Skein-512'],
};
// Base64 candidates keyed by encoded-string length (16-byte MD5 digest ->
// 24 base64 chars including padding, etc.).
const BASE64_BY_LENGTH = {
24: ['MD5 (base64)'],
28: ['SHA-1 (base64)'],
44: ['SHA-256 (base64)'],
88: ['SHA-512 (base64)'],
};
/**
* Classify the charset of a candidate hash string.
*
* bcrypt and argon2 use their `$...$` modular-crypt format, so they are
* matched by prefix (the trailing payload is variable). hex and base64 must
* match the *entire* string, and hex is checked first because any hex digest
* is also a legal base64 character set.
*
* @param {string} s
* @returns {'hex'|'base64'|'bcrypt'|'argon2'|'unknown'}
*/
export function detectCharset(s) {
if (/^\$2[abxy]?\$/.test(s)) return 'bcrypt';
if (/^\$argon2(id|i|d)?\$/.test(s)) return 'argon2';
if (/^[0-9a-fA-F]+$/.test(s)) return 'hex';
if (/^[A-Za-z0-9+/]+={0,2}$/.test(s)) return 'base64';
return 'unknown';
}
/**
* Identify candidate hash types for an input string.
*
* Always returns a HashInfo object; never throws. An empty, unrecognised, or
* wrong-length input simply yields an empty `candidates` list rather than an
* error - the caller decides whether "no candidates" means "not a hash".
*
* @param {string} input
* @returns {{input: string, cleaned: string, length: number, charset: string, candidates: {name: string, bitLength: number}[]}}
*/
export function identifyHash(input) {
const cleaned = (input || '').trim();
const charset = detectCharset(cleaned);
const length = cleaned.length;
let candidates = [];
if (charset === 'bcrypt') {
// bcrypt's modular-crypt token encodes a 184-bit effective hash.
candidates = [{ name: 'bcrypt', bitLength: 184 }];
} else if (charset === 'argon2') {
// Argon2 output length is parameter-driven, so no fixed bit length applies.
candidates = [{ name: 'Argon2', bitLength: 0 }];
} else if (charset === 'hex') {
const names = HEX_BY_LENGTH[length] || [];
candidates = names.map((name) => ({ name, bitLength: length * 4 }));
} else if (charset === 'base64') {
const names = BASE64_BY_LENGTH[length] || [];
// Each base64 char carries 6 bits; round to the nearest byte boundary so
// the reported length lines up with the underlying digest width.
candidates = names.map((name) => ({
name,
bitLength: Math.round((length * 6) / 8) * 8,
}));
}
return { input: input ?? '', cleaned, length, charset, candidates };
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →