Skip to content

Hash Type Identifier — PHP 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 PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.

<?php
/*
 * Hash-type identifier — PHP port.
 *
 * Language: PHP
 * 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.
 */

declare(strict_types=1);

namespace CosmoDev\HashIdentifier;

/**
 * Hex candidates keyed by hex-string length. Each hex char encodes 4 bits,
 * so a 64-char hex digest implies 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); the patterns carry
 * no end anchor. hex and base64 match the *entire* string, and hex is
 * checked first because any hex digest is also a legal base64 character set.
 *
 * @param string $s
 * @return string one of 'hex', 'base64', 'bcrypt', 'argon2', 'unknown'
 */
function detect_charset(string $s): string
{
    if (preg_match('/^\$2[abxy]?\$/', $s) === 1) {
        return 'bcrypt';
    }
    if (preg_match('/^\$argon2(id|i|d)?\$/', $s) === 1) {
        return 'argon2';
    }
    if (preg_match('/^[0-9a-fA-F]+$/', $s) === 1) {
        return 'hex';
    }
    // `+` inside the class is literal; the escaped `\/` keeps the PCRE
    // delimiter unambiguous. Standard base64 alphabet, 0–2 padding chars.
    if (preg_match('/^[A-Za-z0-9+\/]+={0,2}$/', $s) === 1) {
        return 'base64';
    }
    return 'unknown';
}

/**
 * Identify candidate hash types for an input string.
 *
 * Always returns an info array; never throws. An empty, unrecognised, or
 * wrong-length input simply yields an empty `candidates` list — the caller
 * decides whether "no candidates" means "not a hash".
 *
 * @param string $input
 * @return array{input: string, cleaned: string, length: int, charset: string, candidates: array<array{name: string, bitLength: int}>}
 */
function identify_hash(string $input): array
{
    $cleaned = trim($input);
    $charset = detect_charset($cleaned);
    $length = strlen($cleaned);
    $candidates = [];

    switch ($charset) {
        case 'bcrypt':
            // bcrypt's modular-crypt token encodes a 184-bit effective hash.
            $candidates[] = ['name' => 'bcrypt', 'bitLength' => 184];
            break;
        case 'argon2':
            // Argon2 output length is parameter-driven, so no fixed bit length applies.
            $candidates[] = ['name' => 'Argon2', 'bitLength' => 0];
            break;
        case 'hex':
            // length*4 converts hex-char count to a bit width (4 bits per nibble).
            foreach (HEX_BY_LENGTH[$length] ?? [] as $name) {
                $candidates[] = ['name' => $name, 'bitLength' => $length * 4];
            }
            break;
        case 'base64':
            // Each base64 char carries 6 bits; round to the nearest byte boundary
            // so the reported length lines up with the underlying digest width.
            $bitLength = (int) round($length * 6 / 8) * 8;
            foreach (BASE64_BY_LENGTH[$length] ?? [] as $name) {
                $candidates[] = ['name' => $name, 'bitLength' => $bitLength];
            }
            break;
    }

    return [
        'input' => $input,
        'cleaned' => $cleaned,
        'length' => $length,
        'charset' => $charset,
        'candidates' => $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 →