Audited & minimal 0-dependency JS implementation of SHA, RIPEMD, BLAKE, HMAC, HKDF, PBKDF & Scrypt
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.
These hash functions have known vulnerabilities and should not be used for security-critical applications. Use SHA-2 or SHA-3 instead.
Only use these functions when required for compatibility with existing systems that cannot be upgraded.
import { sha1, md5, ripemd160 } from '@noble/hashes/legacy.js';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:
Legacy Use Cases:
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 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:
Legacy Use Cases:
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 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:
Legacy Use Cases:
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();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;
}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 repositoriesimport { 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-160import { 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 tamperingimport { 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 BLAKE2import { 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);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 sizeimport { 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);MD5:
SHA-1:
RIPEMD-160:
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.
Legacy hashes are generally fast but this is not a reason to use them:
Modern alternatives like BLAKE2 are both faster and more secure.
Non-adversarial contexts:
Mandatory compatibility:
With additional protections:
Instead of SHA-1:
Instead of MD5:
Instead of RIPEMD-160: