Home

Awesome

noble-hashes

Audited & minimal JS implementation of SHA, RIPEMD, BLAKE, HMAC, HKDF, PBKDF, Scrypt & Argon2.

The library's initial development was funded by Ethereum Foundation.

For discussions, questions and support, visit GitHub Discussions section of the repository.

This library belongs to noble cryptography

noble cryptography — high-security, easily auditable set of contained cryptographic libraries and tools.

Usage

npm install @noble/hashes

We support all major platforms and runtimes. For Deno, ensure to use npm specifier. For React Native, you may need a polyfill for getRandomValues. A standalone file noble-hashes.js is also available.

// import * from '@noble/hashes'; // Error: use sub-imports, to ensure small app size
import { sha256 } from '@noble/hashes/sha2'; // ECMAScript modules (ESM) and Common.js
// import { sha256 } from 'npm:@noble/hashes@1.3.0/sha2'; // Deno
console.log(sha256(new Uint8Array([1, 2, 3]))); // Uint8Array(32) [3, 144, 88, 198, 242...]
// you could also pass strings that will be UTF8-encoded to Uint8Array
console.log(sha256('abc')); // == sha256(new TextEncoder().encode('abc'))

Implementations

All hash functions:

function hash(message: Uint8Array | string): Uint8Array;
hash(new Uint8Array([1, 3]));
hash('string') == hash(new TextEncoder().encode('string'));

All hash functions can be constructed via hash.create() method:

hash
  .create()
  .update(new Uint8Array([1, 3]))
  .digest();

Some hash functions can also receive options object, which can be either passed as a:

sha2: sha256, sha384, sha512 and others
import { sha256, sha384, sha512, sha224, sha512_256, sha512_384 } from '@noble/hashes/sha2';
// also available as aliases:
// import ... from '@noble/hashes/sha256'
// import ... from '@noble/hashes/sha512'

// Variant A:
const h1a = sha256('abc');

// Variant B:
const h1b = sha256
  .create()
  .update(Uint8Array.from([1, 2, 3]))
  .digest();

for (let hash of [sha384, sha512, sha224, sha512_256, sha512_384]) {
  const res1 = hash('abc');
  const res2 = hash.create().update('def').update(Uint8Array.from([1, 2, 3])).digest();
};

See RFC 4634 and the paper on truncated SHA512/256.

sha3: FIPS, SHAKE, Keccak
import {
  sha3_224,
  sha3_256,
  sha3_384,
  sha3_512,
  keccak_224,
  keccak_256,
  keccak_384,
  keccak_512,
  shake128,
  shake256,
} from '@noble/hashes/sha3';
const h5a = sha3_256('abc');
const h5b = sha3_256
  .create()
  .update(Uint8Array.from([1, 2, 3]))
  .digest();
const h6a = keccak_256('abc');
const h7a = shake128('abc', { dkLen: 512 });
const h7b = shake256('abc', { dkLen: 512 });

See FIPS PUB 202, Website.

Check out the differences between SHA-3 and Keccak

sha3-addons: cSHAKE, KMAC, K12, M14, TurboSHAKE
import {
  cshake128,
  cshake256,
  kmac128,
  kmac256,
  k12,
  m14,
  turboshake128,
  turboshake256,
  tuplehash128,
  tuplehash256,
  parallelhash128,
  parallelhash256,
  keccakprg,
} from '@noble/hashes/sha3-addons';
const h7c = cshake128('abc', { personalization: 'def' });
const h7d = cshake256('abc', { personalization: 'def' });
const h7e = kmac128('key', 'message');
const h7f = kmac256('key', 'message');
const h7h = k12('abc');
const h7g = m14('abc');
const h7t1 = turboshake128('abc');
const h7t2 = turboshake256('def', { D: 0x05 });
const h7i = tuplehash128(['ab', 'c']); // tuplehash(['ab', 'c']) !== tuplehash(['a', 'bc']) !== tuplehash(['abc'])
// Same as k12/blake3, but without reduced number of rounds. Doesn't speedup anything due lack of SIMD and threading,
// added for compatibility.
const h7j = parallelhash128('abc', { blockLen: 8 });
// pseudo-random generator, first argument is capacity. XKCP recommends 254 bits capacity for 128-bit security strength.
// * with a capacity of 254 bits.
const p = keccakprg(254);
p.feed('test');
const rand1b = p.fetch(1);
ripemd160
import { ripemd160 } from '@noble/hashes/ripemd160';
// function ripemd160(data: Uint8Array): Uint8Array;
const hash8 = ripemd160('abc');
const hash9 = ripemd160
  .create()
  .update(Uint8Array.from([1, 2, 3]))
  .digest();

See RFC 2286, Website

blake2b, blake2s, blake3
import { blake2b } from '@noble/hashes/blake2b';
import { blake2s } from '@noble/hashes/blake2s';
import { blake3 } from '@noble/hashes/blake3';

const h10a = blake2s('abc');
const b2params = { key: new Uint8Array([1]), personalization: t, salt: t, dkLen: 32 };
const h10b = blake2s('abc', b2params);
const h10c = blake2s
  .create(b2params)
  .update(Uint8Array.from([1, 2, 3]))
  .digest();

// All params are optional
const h11 = blake3('abc', { dkLen: 256, key: 'def', context: 'fji' });

See RFC 7693, Website.

sha1: legacy hash

SHA1 was cryptographically broken, however, it was not broken for cases like HMAC.

See RFC4226 B.2.

Don't use it for a new protocol.

import { sha1 } from '@noble/hashes/sha1';
const h12 = sha1('def');
hmac
import { hmac } from '@noble/hashes/hmac';
import { sha256 } from '@noble/hashes/sha2';
const mac1 = hmac(sha256, 'key', 'message');
const mac2 = hmac
  .create(sha256, Uint8Array.from([1, 2, 3]))
  .update(Uint8Array.from([4, 5, 6]))
  .digest();

Matches RFC 2104.

hkdf
import { hkdf } from '@noble/hashes/hkdf';
import { sha256 } from '@noble/hashes/sha';
import { randomBytes } from '@noble/hashes/utils';
const inputKey = randomBytes(32);
const salt = randomBytes(32);
const info = 'abc';
const dkLen = 32;
const hk1 = hkdf(sha256, inputKey, salt, info, dkLen);

// == same as
import * as hkdf from '@noble/hashes/hkdf';
import { sha256 } from '@noble/hashes/sha2';
const prk = hkdf.extract(sha256, inputKey, salt);
const hk2 = hkdf.expand(sha256, prk, info, dkLen);

Matches RFC 5869.

pbkdf2
import { pbkdf2, pbkdf2Async } from '@noble/hashes/pbkdf2';
import { sha256 } from '@noble/hashes/sha2';
const pbkey1 = pbkdf2(sha256, 'password', 'salt', { c: 32, dkLen: 32 });
const pbkey2 = await pbkdf2Async(sha256, 'password', 'salt', { c: 32, dkLen: 32 });
const pbkey3 = await pbkdf2Async(sha256, Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6]), {
  c: 32,
  dkLen: 32,
});

Matches RFC 2898.

scrypt
import { scrypt, scryptAsync } from '@noble/hashes/scrypt';
const scr1 = scrypt('password', 'salt', { N: 2 ** 16, r: 8, p: 1, dkLen: 32 });
const scr2 = await scryptAsync('password', 'salt', { N: 2 ** 16, r: 8, p: 1, dkLen: 32 });
const scr3 = await scryptAsync(Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6]), {
  N: 2 ** 22,
  r: 8,
  p: 1,
  dkLen: 32,
  onProgress(percentage) {
    console.log('progress', percentage);
  },
  maxmem: 2 ** 32 + 128 * 8 * 1, // N * r * p * 128 + (128*r*p)
});

Conforms to RFC 7914, Website

Memory usage of scrypt is calculated with the formula N * r * p * 128 + (128 * r * p), which means {N: 2 ** 22, r: 8, p: 1} will use 4GB + 1KB of memory. To prevent DoS, we limit scrypt to 1GB + 1KB of RAM used, which corresponds to {N: 2 ** 20, r: 8, p: 1}. If you want to use higher values, increase maxmem using the formula above.

Note: noble supports 2**22 (4GB RAM) which is the highest amount amongst JS libs. Many other implementations don't support it. We cannot support 2**23, because there is a limitation in JS engines that makes allocating arrays bigger than 4GB impossible, but we're looking into other possible solutions.

argon2

[!WARNING] Experimental: it may be removed at any time.

Argon2 RFC 9106 implementation.

import { argon2d, argon2i, argon2id } from '@noble/hashes/argon2';
const result = argon2id('password', 'salt', { t: 2, m: 65536, p: 1 });
utils
import { bytesToHex as toHex, randomBytes } from '@noble/hashes/utils';
console.log(toHex(randomBytes(32)));
All available imports
import { sha256, sha384, sha512, sha224, sha512_256, sha512_384 } from '@noble/hashes/sha2';
// prettier-ignore
import {
  sha3_224, sha3_256, sha3_384, sha3_512,
  keccak_224, keccak_256, keccak_384, keccak_512,
  shake128, shake256
} from '@noble/hashes/sha3';
// prettier-ignore
import {
  cshake128, cshake256,
  turboshake128, turboshake256,
  kmac128, kmac256,
  tuplehash256, parallelhash256,
  k12, m14, keccakprg
} from '@noble/hashes/sha3-addons';
import { ripemd160 } from '@noble/hashes/ripemd160';
import { blake3 } from '@noble/hashes/blake3';
import { blake2b } from '@noble/hashes/blake2b';
import { blake2s } from '@noble/hashes/blake2s';
import { hmac } from '@noble/hashes/hmac';
import { hkdf } from '@noble/hashes/hkdf';
import { pbkdf2, pbkdf2Async } from '@noble/hashes/pbkdf2';
import { scrypt, scryptAsync } from '@noble/hashes/scrypt';

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

// small utility method that converts bytes to hex
import { bytesToHex as toHex } from '@noble/hashes/utils';
console.log(toHex(sha256('abc'))); // ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

Security

The library has been independently audited:

It is tested against property-based, cross-library and Wycheproof vectors, and has fuzzing by Guido Vranken's cryptofuzz.

If you see anything unusual: investigate and report.

Constant-timeness

JIT-compiler and Garbage Collector make "constant time" extremely hard to achieve timing attack resistance in a scripting language. Which means any other JS library can't have constant-timeness. Even statically typed Rust, a language without GC, makes it harder to achieve constant-time for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.

Memory dumping

The library shares state buffers between hash function calls. The buffers are zeroed-out after each call. However, if an attacker can read application memory, you are doomed in any case:

Supply chain security

Randomness

We're deferring to built-in crypto.getRandomValues which is considered cryptographically secure (CSPRNG).

In the past, browsers had bugs that made it weak: it may happen again. Implementing a userspace CSPRNG to get resilient to the weakness is even worse: there is no reliable userspace source of quality entropy.

Speed

Benchmarks measured on Apple M1 with macOS 12. Note that PBKDF2 and Scrypt are tested with extremely high work factor. To run benchmarks, execute npm run bench:install and then npm run bench

SHA256 32B x 1,219,512 ops/sec @ 820ns/op ± 2.58% (min: 625ns, max: 4ms)
SHA384 32B x 512,032 ops/sec @ 1μs/op
SHA512 32B x 509,943 ops/sec @ 1μs/op
SHA3-256, keccak256, shake256 32B x 199,600 ops/sec @ 5μs/op
Kangaroo12 32B x 336,360 ops/sec @ 2μs/op
Marsupilami14 32B x 298,418 ops/sec @ 3μs/op
BLAKE2b 32B x 379,794 ops/sec @ 2μs/op
BLAKE2s 32B x 515,995 ops/sec @ 1μs/op ± 1.07% (min: 1μs, max: 4ms)
BLAKE3 32B x 588,235 ops/sec @ 1μs/op ± 1.36% (min: 1μs, max: 5ms)
RIPEMD160 32B x 1,140,250 ops/sec @ 877ns/op ± 3.12% (min: 708ns, max: 6ms)
HMAC-SHA256 32B x 377,358 ops/sec @ 2μs/op

HKDF-SHA256 32B x 108,377 ops/sec @ 9μs/op
PBKDF2-HMAC-SHA256 262144 x 3 ops/sec @ 326ms/op
PBKDF2-HMAC-SHA512 262144 x 1 ops/sec @ 970ms/op
Scrypt r: 8, p: 1, n: 262144 x 1 ops/sec @ 616ms/op

Compare to native node.js implementation that uses C bindings instead of pure-js code:

SHA256 32B node x 1,302,083 ops/sec @ 768ns/op ± 10.54% (min: 416ns, max: 7ms)
SHA384 32B node x 975,609 ops/sec @ 1μs/op ± 11.32% (min: 625ns, max: 8ms)
SHA512 32B node x 983,284 ops/sec @ 1μs/op ± 11.24% (min: 625ns, max: 8ms)
SHA3-256 32B node x 910,746 ops/sec @ 1μs/op ± 12.19% (min: 666ns, max: 10ms)
keccak, k12, m14 are not implemented
BLAKE2b 32B node x 967,117 ops/sec @ 1μs/op ± 11.26% (min: 625ns, max: 9ms)
BLAKE2s 32B node x 1,055,966 ops/sec @ 947ns/op ± 11.07% (min: 583ns, max: 7ms)
BLAKE3 is not implemented
RIPEMD160 32B node x 1,002,004 ops/sec @ 998ns/op ± 10.66% (min: 625ns, max: 7ms)
HMAC-SHA256 32B node x 919,963 ops/sec @ 1μs/op ± 6.13% (min: 833ns, max: 5ms)
HKDF-SHA256 32 node x 369,276 ops/sec @ 2μs/op ± 13.59% (min: 1μs, max: 9ms)
PBKDF2-HMAC-SHA256 262144 node x 25 ops/sec @ 39ms/op
PBKDF2-HMAC-SHA512 262144 node x 7 ops/sec @ 132ms/op
Scrypt r: 8, p: 1, n: 262144 node x 1 ops/sec @ 523ms/op

It is possible to make this library 4x+ faster by doing code generation of full loop unrolls. We've decided against it. Reasons:

The current performance is good enough when compared to other projects; SHA256 takes only 900 nanoseconds to run.

Contributing & testing

  1. Clone the repository
  2. npm install to install build dependencies like TypeScript
  3. npm run build to compile TypeScript code
  4. npm run test will execute all main tests. See our approach to testing
  5. npm run test:dos will test against DoS; by measuring function complexity. Takes ~20 minutes
  6. npm run test:big will execute hashing on 4GB inputs, scrypt with 1024 different N, r, p combinations, etc. Takes several hours. Using 8-32+ core CPU helps.

Resources

Check out paulmillr.com/noble for useful resources, articles, documentation and demos related to the library.

License

The MIT License (MIT)

Copyright (c) 2022 Paul Miller (https://paulmillr.com)

See LICENSE file.