Skip to content

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 →