CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-noble--hashes

Audited & minimal 0-dependency JS implementation of SHA, RIPEMD, BLAKE, HMAC, HKDF, PBKDF & Scrypt

Overview
Eval results
Files

legacy.mddocs/

Legacy Hash Functions: SHA-1, MD5, and RIPEMD-160

Legacy cryptographic hash functions that are cryptographically weak and should not be used in new protocols. These implementations are provided for compatibility with legacy systems only.

Security Warning

These hash functions have known vulnerabilities and should not be used for security-critical applications. Use SHA-2 or SHA-3 instead.

  • MD5: Broken, collisions are trivial to generate (2^18 operations)
  • SHA-1: Weak, collisions possible with significant but practical effort (2^60 operations)
  • RIPEMD-160: Weak, theoretical attacks reduce security to 2^80

Only use these functions when required for compatibility with existing systems that cannot be upgraded.

Imports

import { sha1, md5, ripemd160 } from '@noble/hashes/legacy.js';

Capabilities

SHA-1

SHA-1 produces a 20-byte hash. It is cryptographically broken with practical collision attacks demonstrated. Defined in RFC 3174.

/**
 * Computes SHA-1 hash of input data
 * WARNING: SHA-1 is cryptographically broken. Use SHA-256 or better.
 * @param msg - Input data as Uint8Array
 * @returns 20-byte hash digest
 */
function sha1(msg: Uint8Array): Uint8Array;

// Properties
sha1.outputLen: 20; // Output length in bytes
sha1.blockLen: 64;  // Block length in bytes

// Create incremental hasher
function create(): _SHA1;

Vulnerabilities:

  • Collision attacks possible with 2^60 operations (demonstrated in 2017)
  • Not suitable for digital signatures
  • Not suitable for certificate signing
  • Should not be used in TLS/SSL

Legacy Use Cases:

  • Git commit hashes (being phased out)
  • Some old protocols and file formats
  • Verification of old checksums

Usage:

import { sha1 } from '@noble/hashes/legacy.js';
import { bytesToHex } from '@noble/hashes/utils.js';

// Only use for legacy compatibility
const hash = sha1(data);
console.log(bytesToHex(hash)); // 20-byte hex string

// Incremental hashing
const hasher = sha1.create();
hasher.update(chunk1);
hasher.update(chunk2);
const result = hasher.digest();

MD5

MD5 produces a 16-byte hash. It is completely broken with trivial collision generation. Defined in RFC 1321.

/**
 * Computes MD5 hash of input data
 * WARNING: MD5 is completely broken. Use SHA-256 or better.
 * @param msg - Input data as Uint8Array
 * @returns 16-byte hash digest
 */
function md5(msg: Uint8Array): Uint8Array;

// Properties
md5.outputLen: 16; // Output length in bytes
md5.blockLen: 64;  // Block length in bytes

// Create incremental hasher
function create(): _MD5;

Vulnerabilities:

  • Collision attacks trivial with 2^18 operations
  • Preimage attacks theoretically possible
  • Completely unsuitable for any security purpose
  • Should not be used for password hashing
  • Should not be used for integrity verification in adversarial contexts

Legacy Use Cases:

  • Non-cryptographic checksums (file corruption detection)
  • Legacy database lookups
  • Cache keys in non-adversarial contexts
  • Verification of old checksums from trusted sources

Usage:

import { md5 } from '@noble/hashes/legacy.js';
import { bytesToHex } from '@noble/hashes/utils.js';

// Only use for non-cryptographic checksums
const hash = md5(data);
console.log(bytesToHex(hash)); // 16-byte hex string

// Incremental hashing
const hasher = md5.create();
hasher.update(chunk1);
hasher.update(chunk2);
const result = hasher.digest();

RIPEMD-160

RIPEMD-160 produces a 20-byte hash. It has reduced security margins and theoretical weaknesses. Defined in RFC 2286.

/**
 * Computes RIPEMD-160 hash of input data
 * WARNING: RIPEMD-160 has reduced security. Use SHA-256 or better.
 * @param msg - Input data as Uint8Array
 * @returns 20-byte hash digest
 */
function ripemd160(msg: Uint8Array): Uint8Array;

// Properties
ripemd160.outputLen: 20; // Output length in bytes
ripemd160.blockLen: 64;  // Block length in bytes

// Create incremental hasher
function create(): _RIPEMD160;

Vulnerabilities:

  • Theoretical attacks reduce security to 2^80 (below modern 128-bit minimum)
  • Less analyzed than SHA family
  • Smaller security margin than SHA-256

Legacy Use Cases:

  • Bitcoin addresses (P2PKH format, being phased out)
  • Some old cryptographic protocols
  • Legacy compatibility

Usage:

import { ripemd160 } from '@noble/hashes/legacy.js';
import { bytesToHex } from '@noble/hashes/utils.js';

// Only use for legacy compatibility
const hash = ripemd160(data);
console.log(bytesToHex(hash)); // 20-byte hex string

// Incremental hashing
const hasher = ripemd160.create();
hasher.update(chunk1);
hasher.update(chunk2);
const result = hasher.digest();

Common Hash Interface

All legacy hash functions implement the standard Hash interface:

interface Hash<T> {
  blockLen: number;
  outputLen: number;

  /**
   * Updates hash with new data
   * @param buf - Data to hash
   * @returns this for chaining
   */
  update(buf: Uint8Array): this;

  /**
   * Finalizes hash and returns digest
   * @returns Hash digest as Uint8Array
   */
  digest(): Uint8Array;

  /**
   * Finalizes hash and writes digest to provided buffer
   * @param out - Output buffer (must be >= outputLen)
   */
  digestInto(out: Uint8Array): void;

  /**
   * Destroys internal state (zeros memory)
   */
  destroy(): void;

  /**
   * Creates independent copy of current hash state
   * @returns Cloned hash instance
   */
  clone(): T;
}

Usage Examples

Git Commit Hash Verification

import { sha1 } from '@noble/hashes/legacy.js';
import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';

// Git uses SHA-1 for commit hashes (legacy, being replaced with SHA-256)
function computeGitCommitHash(content: string): string {
  const data = utf8ToBytes(content);
  const hash = sha1(data);
  return bytesToHex(hash);
}

// Note: Git is migrating to SHA-256 for new repositories

Bitcoin Address Generation (Legacy P2PKH)

import { sha256 } from '@noble/hashes/sha2.js';
import { ripemd160 } from '@noble/hashes/legacy.js';
import { bytesToHex } from '@noble/hashes/utils.js';

// Bitcoin legacy address uses SHA-256 followed by RIPEMD-160
function createLegacyBitcoinAddress(publicKey: Uint8Array): Uint8Array {
  // Double hash: SHA-256 then RIPEMD-160
  const sha256Hash = sha256(publicKey);
  const ripemd160Hash = ripemd160(sha256Hash);
  return ripemd160Hash; // 20 bytes (+ network byte + checksum for full address)
}

// Note: Modern Bitcoin prefers native SegWit addresses without RIPEMD-160

Non-Cryptographic File Checksum with MD5

import { md5 } from '@noble/hashes/legacy.js';
import { bytesToHex } from '@noble/hashes/utils.js';

// MD5 for non-security checksums (e.g., detecting file corruption)
// DO NOT use for security purposes
function computeFileChecksum(fileChunks: Uint8Array[]): string {
  const hasher = md5.create();

  for (const chunk of fileChunks) {
    hasher.update(chunk);
  }

  return bytesToHex(hasher.digest());
}

// Use case: Detecting accidental corruption, not malicious tampering

Legacy Database Compatibility

import { md5 } from '@noble/hashes/legacy.js';
import { utf8ToBytes, bytesToHex } from '@noble/hashes/utils.js';

// Some legacy databases use MD5 for lookups (not for security)
function generateLegacyId(key: string): string {
  const hash = md5(utf8ToBytes(key));
  return bytesToHex(hash);
}

// Note: For new systems, use SHA-256 or BLAKE2

Migration Guide

From SHA-1 to SHA-256

import { sha1 } from '@noble/hashes/legacy.js';
import { sha256 } from '@noble/hashes/sha2.js';

// Old (insecure)
const oldHash = sha1(data); // 20 bytes

// New (secure)
const newHash = sha256(data); // 32 bytes

// If you need 20 bytes for compatibility, truncate (not recommended)
const truncated = sha256(data).slice(0, 20);

From MD5 to BLAKE2

import { md5 } from '@noble/hashes/legacy.js';
import { blake2b } from '@noble/hashes/blake2.js';

// Old (broken)
const oldHash = md5(data); // 16 bytes

// New (secure and fast)
const newHash = blake2b(data, { dkLen: 16 }); // 16 bytes, compatible size

From RIPEMD-160 to SHA-256

import { ripemd160 } from '@noble/hashes/legacy.js';
import { sha256 } from '@noble/hashes/sha2.js';

// Old (weak)
const oldHash = ripemd160(data); // 20 bytes

// New (secure)
const newHash = sha256(data); // 32 bytes

// Or truncate if size matters (still secure)
const truncated = sha256(data).slice(0, 20);

Technical Details

Why These Are Weak

MD5:

  • 128-bit output (too small for collision resistance)
  • Design flaws allow collision attacks
  • Prefix collision attacks possible
  • Rainbow tables widely available

SHA-1:

  • 160-bit output (marginally better but still insufficient)
  • Practical collision attacks demonstrated (SHAttered attack, 2017)
  • Should be considered broken for all cryptographic purposes
  • NIST deprecated SHA-1 in 2011

RIPEMD-160:

  • 160-bit output (same as SHA-1)
  • Less cryptanalysis than SHA family
  • Theoretical attacks reduce security margin
  • Not recommended for new designs

HMAC with Legacy Hashes

Even with weak hash functions, HMAC constructions remain relatively secure:

import { hmac } from '@noble/hashes/hmac.js';
import { sha1 } from '@noble/hashes/legacy.js';

// HMAC-SHA1 is still considered secure for MAC purposes
// (but not recommended for new protocols)
const mac = hmac(sha1, key, message);

According to RFC 6151, HMAC-SHA1 and HMAC-MD5 are still acceptable for message authentication, though not recommended for new designs.

Performance

Legacy hashes are generally fast but this is not a reason to use them:

  • MD5: ~496ns per 32-byte input (but insecure)
  • SHA-1: ~500ns per 32-byte input (but broken)
  • RIPEMD-160: Similar to SHA-1

Modern alternatives like BLAKE2 are both faster and more secure.

When Legacy Hashes Are Acceptable

  1. Non-adversarial contexts:

    • Checksums for detecting accidental corruption
    • Hash tables and cache keys in trusted environments
    • Database sharding where security is not a concern
  2. Mandatory compatibility:

    • Verifying old signatures (not creating new ones)
    • Working with legacy protocols that cannot be updated
    • Reading old file formats
  3. With additional protections:

    • HMAC-SHA1 or HMAC-MD5 for MACs (not recommended but acceptable)
    • In combination with other security layers

Recommended Alternatives

Instead of SHA-1:

  • Use SHA-256 (widely supported, secure)
  • Use SHA3-256 (NIST standard, different design)
  • Use BLAKE2b (fast and secure)

Instead of MD5:

  • Use BLAKE2b (faster and secure)
  • Use SHA-256 (standard and secure)
  • For checksums: Use CRC32 or xxHash (non-cryptographic but honest about it)

Instead of RIPEMD-160:

  • Use SHA-256 (better security margin)
  • Use BLAKE2b (fast and flexible output length)

References

  • RFC 3174: SHA-1 Specification
  • RFC 1321: MD5 Specification
  • RFC 2286: RIPEMD-160 Specification
  • RFC 6151: HMAC-SHA1 and HMAC-MD5 Security
  • SHAttered Attack: Practical SHA-1 Collision (2017)
  • MD5 Collision Demo: MD5 Weakness Examples

Install with Tessl CLI

npx tessl i tessl/npm-noble--hashes

docs

argon2.md

blake.md

eskdf.md

hkdf.md

hmac.md

index.md

legacy.md

pbkdf2.md

scrypt.md

sha2.md

sha3-addons.md

sha3.md

utils.md

webcrypto.md

tile.json