563 lines
23 KiB
Markdown
563 lines
23 KiB
Markdown
# noble-hashes
|
||
|
||
Audited & minimal JS implementation of hash functions, MACs and KDFs.
|
||
|
||
- 🔒 [**Audited**](#security) by an independent security firm
|
||
- 🔻 Tree-shakeable: unused code is excluded from your builds
|
||
- 🏎 Fast: hand-optimized for caveats of JS engines
|
||
- 🔍 Reliable: chained / sliding window / DoS / ACVP tests and fuzzing
|
||
- 🔁 No unrolled loops: makes it easier to verify and reduces source code size up to 5x
|
||
- 🦘 Includes SHA, RIPEMD, BLAKE, HMAC, HKDF, PBKDF, Scrypt, Argon2
|
||
- 🥈 Optional, friendly wrapper over native WebCrypto
|
||
- 🪶 22KB (gzipped) for everything, 2.4KB for single-hash build
|
||
|
||
Use [awasm-noble](https://github.com/paulmillr/awasm-noble) if you need an even faster (WASM) alternative.
|
||
Check out [Upgrading](#upgrading) for information about upgrading from previous versions.
|
||
Take a glance at [GitHub Discussions](https://github.com/paulmillr/noble-hashes/discussions) for questions and support.
|
||
|
||
The library's initial development was funded by [Ethereum Foundation](https://ethereum.org/).
|
||
|
||
### This library belongs to _noble_ cryptography
|
||
|
||
> **noble cryptography** — high-security, easily auditable set of contained cryptographic libraries and tools.
|
||
|
||
- Zero or minimal dependencies
|
||
- Highly readable TypeScript / JS code
|
||
- PGP-signed releases and transparent NPM builds
|
||
- All libraries:
|
||
[ciphers](https://github.com/paulmillr/noble-ciphers),
|
||
[curves](https://github.com/paulmillr/noble-curves),
|
||
[hashes](https://github.com/paulmillr/noble-hashes),
|
||
[post-quantum](https://github.com/paulmillr/noble-post-quantum),
|
||
5kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) /
|
||
[ed25519](https://github.com/paulmillr/noble-ed25519)
|
||
- [Check out the homepage](https://paulmillr.com/noble/)
|
||
for reading resources, documentation, and apps built with noble
|
||
|
||
## Usage
|
||
|
||
> `npm install @noble/hashes`
|
||
|
||
> `deno add jsr:@noble/hashes`
|
||
|
||
We support all major platforms and runtimes.
|
||
For React Native, you may need a [polyfill for getRandomValues](https://github.com/LinusU/react-native-get-random-values).
|
||
A standalone file [noble-hashes.js](https://github.com/paulmillr/noble-hashes/releases) is also available.
|
||
|
||
```js
|
||
// import * from '@noble/hashes'; // Error: use sub-imports, to ensure small app size
|
||
import { sha256 as noble_sha256 } from '@noble/hashes/sha2.js';
|
||
const hash = noble_sha256(Uint8Array.from([0xca, 0xfe, 0x01, 0x23]));
|
||
|
||
// Available modules
|
||
import { sha256, sha384, sha512, sha224, sha512_224, sha512_256 } from '@noble/hashes/sha2.js';
|
||
import {
|
||
sha3_256, sha3_512,
|
||
keccak_256, keccak_512,
|
||
shake128, shake256,
|
||
} from '@noble/hashes/sha3.js';
|
||
import {
|
||
cshake256, turboshake256, kmac256, tuplehash256,
|
||
kt128, kt256, keccakprg,
|
||
} from '@noble/hashes/sha3-addons.js';
|
||
import { blake3 } from '@noble/hashes/blake3.js';
|
||
import { blake2b, blake2s } from '@noble/hashes/blake2.js';
|
||
import { blake256, blake512 } from '@noble/hashes/blake1.js';
|
||
import { sha1, md5, ripemd160 } from '@noble/hashes/legacy.js';
|
||
import { hmac } from '@noble/hashes/hmac.js';
|
||
import { hkdf } from '@noble/hashes/hkdf.js';
|
||
import { pbkdf2, pbkdf2Async } from '@noble/hashes/pbkdf2.js';
|
||
import { scrypt, scryptAsync } from '@noble/hashes/scrypt.js';
|
||
import { argon2d, argon2i, argon2id } from '@noble/hashes/argon2.js';
|
||
import * as webcrypto from '@noble/hashes/webcrypto.js';
|
||
// const { sha256, sha384, sha512, hmac, hkdf, pbkdf2 } = webcrypto;
|
||
import * as utils from '@noble/hashes/utils.js';
|
||
const { bytesToHex, concatBytes, equalBytes, hexToBytes } = utils;
|
||
```
|
||
|
||
- [sha2: sha256, sha384, sha512](#sha2-sha256-sha384-sha512-and-others)
|
||
- [sha3: FIPS, SHAKE, Keccak](#sha3-fips-shake-keccak)
|
||
- [sha3-addons: cSHAKE, KMAC, KT128, TurboSHAKE](#sha3-addons-cshake-kmac-kt128-turboshake)
|
||
- [blake1, blake2, blake3](#blake1-blake2-blake3)
|
||
- [legacy: sha1, md5, ripemd160](#legacy-sha1-md5-ripemd160)
|
||
- MACs: [hmac](#hmac) | [kmac](#sha3-addons-cshake-kmac-kt128-turboshake) | [blake3 key mode](#blake1-blake2-blake3)
|
||
- KDFs: [hkdf](#hkdf) | [pbkdf2](#pbkdf2) | [scrypt](#scrypt) | [argon2](#argon2)
|
||
- [webcrypto: friendly wrapper](#webcrypto-friendly-wrapper)
|
||
- [utils](#utils)
|
||
- [Security](#security) | [Speed](#speed) | [Contributing & testing](#contributing--testing) | [License](#license)
|
||
|
||
### Implementations
|
||
|
||
Hash functions:
|
||
|
||
- `sha256()`: receive & return `Uint8Array`
|
||
- `sha256.create().update(a).update(b).digest()`: support partial updates
|
||
- `blake3.create({ context: 'e', dkLen: 32 })`: can have options
|
||
- support little-endian architecture; also experimentally big-endian
|
||
- can hash up to 4GB per chunk, with any amount of chunks
|
||
|
||
#### sha2: sha256, sha384, sha512 and others
|
||
|
||
```typescript
|
||
import { sha224, sha256, sha384, sha512, sha512_224, sha512_256 } from '@noble/hashes/sha2.js';
|
||
const res = sha256(Uint8Array.from([0xbc])); // basic
|
||
for (let hash of [sha256, sha384, sha512, sha224, sha512_224, sha512_256]) {
|
||
const arr = Uint8Array.from([0x10, 0x20, 0x30]);
|
||
const a = hash(arr);
|
||
const b = hash.create().update(arr).digest();
|
||
}
|
||
```
|
||
|
||
Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and
|
||
[the paper on truncated SHA512/256](https://eprint.iacr.org/2010/548.pdf).
|
||
|
||
#### sha3: FIPS, SHAKE, Keccak
|
||
|
||
```typescript
|
||
import {
|
||
sha3_224, sha3_256, sha3_384, sha3_512,
|
||
keccak_224, keccak_256, keccak_384, keccak_512,
|
||
shake128, shake256,
|
||
} from '@noble/hashes/sha3.js';
|
||
for (let hash of [
|
||
sha3_224, sha3_256, sha3_384, sha3_512,
|
||
keccak_224, keccak_256, keccak_384, keccak_512,
|
||
]) {
|
||
const arr = Uint8Array.from([0x10, 0x20, 0x30]);
|
||
const a = hash(arr);
|
||
const b = hash.create().update(arr).digest();
|
||
}
|
||
const shka = shake128(Uint8Array.from([0x10]), { dkLen: 512 });
|
||
const shkb = shake256(Uint8Array.from([0x30]), { dkLen: 512 });
|
||
```
|
||
|
||
Check out [FIPS-202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf),
|
||
[Website](https://keccak.team/keccak.html).
|
||
|
||
Check out [the differences between SHA-3 and Keccak](https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub)
|
||
|
||
#### sha3-addons: cSHAKE, KMAC, K12, TurboSHAKE
|
||
|
||
```typescript
|
||
import {
|
||
cshake128, cshake256, kt128, kt256,
|
||
keccakprg, kmac128, kmac256,
|
||
parallelhash256, tuplehash256,
|
||
turboshake128, turboshake256,
|
||
} from '@noble/hashes/sha3-addons.js';
|
||
const data = Uint8Array.from([0x10, 0x20, 0x30]);
|
||
const personalization = new TextEncoder().encode('def');
|
||
const ec1 = cshake128(data, { personalization });
|
||
const ec2 = cshake256(data, { personalization });
|
||
const et1 = turboshake128(data);
|
||
const et2 = turboshake256(data, { D: 0x05 });
|
||
// tuplehash(['ab', 'c']) !== tuplehash(['a', 'bc']) !== tuplehash([data])
|
||
const et3 = tuplehash256([new TextEncoder().encode('ab'), new TextEncoder().encode('c')]);
|
||
// Not parallel in JS (similar to blake3 / kt128), added for compat
|
||
const ep1 = parallelhash256(data, { blockLen: 8 });
|
||
const kk = Uint8Array.from([0xca]);
|
||
const ek10 = kmac128(kk, data);
|
||
const ek11 = kmac256(kk, data);
|
||
const ek12 = kt128(data); // kangarootwelve 128-bit
|
||
const ek13 = kt256(data); // kangarootwelve 256-bit
|
||
// 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.addEntropy(Uint8Array.from([1, 2, 3]));
|
||
const rand1b = p.randomBytes(32);
|
||
```
|
||
|
||
- cSHAKE, KMAC, TupleHash, ParallelHash + XOF are available, matching
|
||
[NIST SP 800-185](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf)
|
||
- Reduced-round Keccak KT128 (KangarooTwelve 🦘, K12) and TurboSHAKE are available, matching
|
||
[RFC 9861](https://datatracker.ietf.org/doc/rfc9861/).
|
||
- [KeccakPRG](https://keccak.team/files/CSF-0.1.pdf): pseudo-random generator based on Keccak
|
||
|
||
#### blake1, blake2, blake3
|
||
|
||
```typescript
|
||
import { blake224, blake256, blake384, blake512 } from '@noble/hashes/blake1.js';
|
||
import { blake2b, blake2s } from '@noble/hashes/blake2.js';
|
||
import { blake3 } from '@noble/hashes/blake3.js';
|
||
|
||
for (let hash of [blake224, blake256, blake384, blake512, blake2b, blake2s, blake3]) {
|
||
const arr = Uint8Array.from([0x10, 0x20, 0x30]);
|
||
const a = hash(arr);
|
||
const b = hash.create().update(arr).digest();
|
||
}
|
||
|
||
// blake2 advanced usage
|
||
const ab = Uint8Array.from([0x01]);
|
||
const txt = new TextEncoder();
|
||
blake2s(ab);
|
||
blake2s(ab, { key: new Uint8Array(32) });
|
||
blake2s(ab, { personalization: txt.encode('pers1234') });
|
||
blake2s(ab, { salt: txt.encode('salt1234') });
|
||
blake2b(ab);
|
||
blake2b(ab, { key: new Uint8Array(64) });
|
||
blake2b(ab, { personalization: txt.encode('pers1234pers1234') });
|
||
blake2b(ab, { salt: txt.encode('salt1234salt1234') });
|
||
|
||
// blake3 advanced usage
|
||
blake3(ab);
|
||
blake3(ab, { dkLen: 256 });
|
||
blake3(ab, { key: new Uint8Array(32) });
|
||
blake3(ab, { context: txt.encode('application-name') });
|
||
```
|
||
|
||
- Blake1 is legacy hash, one of SHA3 proposals. It is rarely used anywhere. See [pdf](https://www.aumasson.jp/blake/blake.pdf).
|
||
- Blake2 is popular fast hash. blake2b focuses on 64-bit platforms while blake2s is for 8-bit to 32-bit ones. See [RFC 7693](https://datatracker.ietf.org/doc/html/rfc7693), [Website](https://www.blake2.net)
|
||
- Blake3 is faster, reduced-round blake2. See [Website & specs](https://blake3.io)
|
||
|
||
#### legacy: sha1, md5, ripemd160
|
||
|
||
SHA1 (RFC 3174), MD5 (RFC 1321) and RIPEMD160 (RFC 2286) legacy, weak hash functions.
|
||
Don't use them in a new protocol. What "weak" means:
|
||
|
||
- Collisions can be made with 2^18 effort in MD5, 2^60 in SHA1, 2^80 in RIPEMD160.
|
||
- No practical pre-image attacks (only theoretical, 2^123.4)
|
||
- HMAC seems kinda ok: https://datatracker.ietf.org/doc/html/rfc6151
|
||
|
||
```typescript
|
||
import { md5, ripemd160, sha1 } from '@noble/hashes/legacy.js';
|
||
for (let hash of [md5, ripemd160, sha1]) {
|
||
const arr = Uint8Array.from([0x10, 0x20, 0x30]);
|
||
const a = hash(arr);
|
||
const b = hash.create().update(arr).digest();
|
||
}
|
||
```
|
||
|
||
#### hmac
|
||
|
||
```typescript
|
||
import { hmac } from '@noble/hashes/hmac.js';
|
||
import { sha256 } from '@noble/hashes/sha2.js';
|
||
const key = new Uint8Array(32).fill(1);
|
||
const msg = new Uint8Array(32).fill(2);
|
||
const mac1 = hmac(sha256, key, msg);
|
||
const mac2 = hmac.create(sha256, key).update(msg).digest();
|
||
```
|
||
|
||
Conforms to [RFC 2104](https://datatracker.ietf.org/doc/html/rfc2104).
|
||
|
||
#### hkdf
|
||
|
||
```typescript
|
||
import { hkdf } from '@noble/hashes/hkdf.js';
|
||
import { randomBytes } from '@noble/hashes/utils.js';
|
||
import { sha256 } from '@noble/hashes/sha2.js';
|
||
const inputKey = randomBytes(32);
|
||
const salt = randomBytes(32);
|
||
const info = new TextEncoder().encode('application-key');
|
||
const hk1 = hkdf(sha256, inputKey, salt, info, 32);
|
||
|
||
// == same as
|
||
import { extract, expand } from '@noble/hashes/hkdf.js';
|
||
const prk = extract(sha256, inputKey, salt);
|
||
const hk2 = expand(sha256, prk, info, 32);
|
||
```
|
||
|
||
Conforms to [RFC 5869](https://datatracker.ietf.org/doc/html/rfc5869).
|
||
|
||
#### pbkdf2
|
||
|
||
```typescript
|
||
import { pbkdf2, pbkdf2Async } from '@noble/hashes/pbkdf2.js';
|
||
import { sha256 } from '@noble/hashes/sha2.js';
|
||
const pbkey1 = pbkdf2(sha256, 'password', 'salt', { c: 524288, dkLen: 32 });
|
||
const pbkey2 = await pbkdf2Async(sha256, 'password', 'salt', { c: 524288, dkLen: 32 });
|
||
const pbkey3 = await pbkdf2Async(sha256, Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6]), {
|
||
c: 524288,
|
||
dkLen: 32,
|
||
});
|
||
```
|
||
|
||
Conforms to [RFC 2898](https://datatracker.ietf.org/doc/html/rfc2898).
|
||
|
||
#### scrypt
|
||
|
||
```typescript
|
||
import { scrypt, scryptAsync } from '@noble/hashes/scrypt.js';
|
||
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 ** 17,
|
||
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](https://datatracker.ietf.org/doc/html/rfc7914),
|
||
[Website](https://www.tarsnap.com/scrypt.html)
|
||
|
||
- `N, r, p` are work factors. It is common to only adjust N, while keeping `r: 8, p: 1`.
|
||
See [the blog post](https://blog.filippo.io/the-scrypt-parameters/).
|
||
JS doesn't support parallelization, making increasing `p` meaningless.
|
||
- `dkLen` is the length of output bytes e.g. `32` or `64`
|
||
- `onProgress` can be used with async version of the function to report progress to a user.
|
||
- `maxmem` prevents DoS and is limited to `1GB + 1KB` (`2**30 + 2**10`), but can be adjusted using formula: `N * r * p * 128 + (128 * r * p)`
|
||
|
||
Time it takes to derive Scrypt key under different values of N (2\*\*N) on Apple M4 (mobile phones can be 1x-4x slower):
|
||
|
||
| N pow | Time | RAM |
|
||
| ----- | ---- | ----- |
|
||
| 16 | 0.1s | 64MB |
|
||
| 17 | 0.2s | 128MB |
|
||
| 18 | 0.4s | 256MB |
|
||
| 19 | 0.8s | 512MB |
|
||
| 20 | 1.5s | 1GB |
|
||
| 21 | 3.1s | 2GB |
|
||
| 22 | 6.2s | 4GB |
|
||
| 23 | 13s | 8GB |
|
||
| 24 | 27s | 16GB |
|
||
|
||
> [!NOTE]
|
||
> We support N larger than `2**20` where available, however,
|
||
> not all JS engines support >= 2GB ArrayBuffer-s.
|
||
> When using such N, you'll need to manually adjust `maxmem`, using formula above.
|
||
> Other JS implementations don't support large N-s.
|
||
|
||
#### argon2
|
||
|
||
```ts
|
||
import { argon2d, argon2i, argon2id } from '@noble/hashes/argon2.js';
|
||
const arg1 = argon2id('password', 'saltsalt', { t: 2, m: 65536, p: 1, maxmem: 2 ** 32 - 1 });
|
||
```
|
||
|
||
Argon2 [RFC 9106](https://datatracker.ietf.org/doc/html/rfc9106) implementation.
|
||
|
||
> [!WARNING]
|
||
> Argon2 can't be fast in JS, because there is no fast Uint64Array.
|
||
> It is suggested to use [Scrypt](#scrypt) instead.
|
||
> Being 5x slower than native code means brute-forcing attackers have bigger advantage.
|
||
|
||
#### webcrypto: friendly wrapper
|
||
|
||
```js
|
||
import { sha256, sha384, sha512, hmac, hkdf, pbkdf2 } from '@noble/hashes/webcrypto.js';
|
||
import { randomBytes } from '@noble/hashes/utils.js';
|
||
const whash = await sha256(Uint8Array.from([0xca, 0xfe, 0x01, 0x23]));
|
||
|
||
const key = new Uint8Array(32).fill(1);
|
||
const msg = new Uint8Array(32).fill(2);
|
||
const wmac = await hmac(sha256, key, msg);
|
||
|
||
const inputKey = randomBytes(32);
|
||
const salt = randomBytes(32);
|
||
const info = new TextEncoder().encode('application-key');
|
||
const hk1 = await hkdf(sha256, inputKey, salt, info, 32);
|
||
|
||
const pbkey1 = await pbkdf2(sha256, 'password', 'salt', { c: 524288, dkLen: 32 });
|
||
```
|
||
|
||
Sometimes people want to use built-in `crypto.subtle` instead of pure JS implementation.
|
||
However, it has terrible API.
|
||
|
||
We simplify access to built-ins with API which mirrors noble-hashes.
|
||
The overhead is minimal - just 30+ lines of code, which verify input correctness.
|
||
|
||
> [!NOTE]
|
||
> Webcrypto methods are always async.
|
||
|
||
#### utils
|
||
|
||
```typescript
|
||
import { bytesToHex as toHex, randomBytes } from '@noble/hashes/utils.js';
|
||
console.log(toHex(randomBytes(32)));
|
||
```
|
||
|
||
- `bytesToHex` will convert `Uint8Array` to a hex string
|
||
- `randomBytes(bytes)` will produce cryptographically secure random `Uint8Array` of length `bytes`
|
||
|
||
## Security
|
||
|
||
The library has been audited:
|
||
|
||
- at version 2.2.0, in Apr 2026, by ourselves (self-audited)
|
||
- Scope: everything
|
||
- [Changes since audit](https://github.com/paulmillr/noble-hashes/compare/2.2.0..main)
|
||
- at version 1.0.0, in Jan 2022, independently, by [Cure53](https://cure53.de)
|
||
- PDFs: [website](https://cure53.de/pentest-report_hashing-libs.pdf), [in-repo](./audit/2022-01-05-cure53-audit-nbl2.pdf)
|
||
- Scope: everything, besides `blake3`, `sha3-addons`, `sha1` and `argon2`, which have not been audited
|
||
- The audit has been funded by [Ethereum Foundation](https://ethereum.org/en/) with help of [Nomic Labs](https://nomiclabs.io)
|
||
|
||
It is tested against property-based, cross-library and Wycheproof vectors,
|
||
and is being fuzzed in [the separate repo](https://github.com/paulmillr/fuzzing).
|
||
|
||
If you see anything unusual: investigate and report.
|
||
|
||
### Constant-timeness
|
||
|
||
We're targetting algorithmic constant time. _JIT-compiler_ and _Garbage Collector_ make "constant time"
|
||
extremely hard to achieve [timing attack](https://en.wikipedia.org/wiki/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](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security)
|
||
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.
|
||
|
||
### 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:
|
||
|
||
- At some point, input will be a string and strings are immutable in JS:
|
||
there is no way to overwrite them with zeros. For example: deriving
|
||
key from `scrypt(password, salt)` where password and salt are strings
|
||
- Input from a file will stay in file buffers
|
||
- Input / output will be re-used multiple times in application which means it could stay in memory
|
||
- `await anything()` will always write all internal variables (including numbers)
|
||
to memory. With async functions / Promises there are no guarantees when the code
|
||
chunk would be executed. Which means attacker can have plenty of time to read data from memory
|
||
- There is no way to guarantee anything about zeroing sensitive data without
|
||
complex tests-suite which will dump process memory and verify that there is
|
||
no sensitive data left. For JS it means testing all browsers (incl. mobile),
|
||
which is complex. And of course it will be useless without using the same
|
||
test-suite in the actual application that consumes the library
|
||
|
||
### Supply chain security
|
||
|
||
- **Commits** are signed with PGP keys to prevent forgery. Be sure to verify the commit signatures
|
||
- **Releases** are made transparently through token-less GitHub CI and Trusted Publishing. Be sure to verify the [provenance logs](https://docs.npmjs.com/generating-provenance-statements) for authenticity.
|
||
- **Rare releasing** is practiced to minimize the need for re-audits by end-users.
|
||
- **Dependencies** are minimized and strictly pinned to reduce supply-chain risk.
|
||
- We use as few dependencies as possible.
|
||
- Version ranges are locked, and changes are checked with npm-diff.
|
||
- **Dev dependencies** are excluded from end-user installs; they’re only used for development and build steps.
|
||
|
||
For this package, there are 0 dependencies; and a few dev dependencies:
|
||
|
||
- jsbt contains helpers for building, benchmarking & testing secure JS apps. It is developed by the same author
|
||
- prettier, fast-check and typescript are used for code quality / test generation / ts compilation
|
||
|
||
### Randomness
|
||
|
||
We rely on the built-in
|
||
[`crypto.getRandomValues`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues),
|
||
which is considered a cryptographically secure PRNG.
|
||
|
||
Browsers have had weaknesses in the past - and could again - but implementing a userspace CSPRNG is even worse, as there’s no reliable userspace source of high-quality entropy.
|
||
|
||
### Quantum computers
|
||
|
||
Cryptographically relevant quantum computer, if built, will allow to
|
||
utilize Grover's algorithm to break hashes in 2^n/2 operations, instead of 2^n.
|
||
|
||
This means SHA256 should be replaced with SHA512, SHA3-256 with SHA3-512, SHAKE128 with SHAKE256 etc.
|
||
|
||
Australian ASD prohibits SHA256 and similar hashes [after 2030](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/ism/cyber-security-guidelines/guidelines-cryptography).
|
||
|
||
## Upgrading
|
||
|
||
Supported node.js versions:
|
||
|
||
- v2: v20.19+ (ESM-only)
|
||
- v1: v14.21+ (ESM & CJS)
|
||
|
||
v2.0 changelog:
|
||
|
||
- The package is now ESM-only. ESM can finally be loaded from common.js on node v20.19+
|
||
- `.js` extension must be used for all modules
|
||
- Old: `@noble/hashes/sha3`
|
||
- New: `@noble/hashes/sha3.js`
|
||
- This simplifies working in browsers natively without transpilers
|
||
- Only allow Uint8Array as hash inputs, prohibit `string`
|
||
- Strict validation checks improve security
|
||
- To replicate previous behavior, use `utils.utf8ToBytes`
|
||
- Rename / remove some modules for consistency. Previously, sha384 resided in sha512, which was weird
|
||
- `sha256`, `sha512` => `sha2.js` (consistent with `sha3.js`)
|
||
- `blake2b`, `blake2s` => `blake2.js` (consistent with `blake3.js`, `blake1.js`)
|
||
- `ripemd160`, `sha1`, `md5` => `legacy.js` (all low-security hashes are there)
|
||
- `_assert` => `utils.js`
|
||
- `crypto` internal module got removed: use built-in WebCrypto instead
|
||
- Improve typescript types & option autocomplete
|
||
- Bump compilation target from es2020 to es2022
|
||
|
||
## Contributing & testing
|
||
|
||
`test/misc` directory contains implementations of loop unrolling and md5.
|
||
|
||
- `npm install && npm run build && npm test` will build the code and run tests.
|
||
- `npm run lint` / `npm run format` will run linter / fix linter issues.
|
||
- `npm run bench` will run benchmarks
|
||
- `npm run build:release` will build single file
|
||
- There is **additional** 20-min DoS test `npm run test:dos` and 2-hour multicore test `npm run test:slow`.
|
||
See [our approach to testing](./test/README.md)
|
||
|
||
Some hashes are outside of scope of the library:
|
||
- [Pedersen in micro-zk-proofs](https://github.com/paulmillr/micro-zk-proofs/blob/1ed5ce1253583b2e540eef7f3477fb52bf5344ff/src/pedersen.ts)
|
||
- [Poseidon in noble-curves](https://github.com/paulmillr/noble-curves/blob/3d124dd3ecec8b6634cc0b2ba1c183aded5304f9/src/abstract/poseidon.ts)
|
||
- [Poly1305 & GHash in noble-ciphers](https://github.com/paulmillr/noble-ciphers)
|
||
|
||
See [paulmillr.com/noble](https://paulmillr.com/noble/) for useful resources, articles, documentation and demos related to the library.
|
||
|
||
## Speed
|
||
|
||
```sh
|
||
npm run bench
|
||
```
|
||
|
||
Benchmarks measured on Apple M4. If you need truly exemplar performance, switch to [awasm-noble](https://github.com/paulmillr/awasm-noble).
|
||
|
||
```
|
||
# 32B
|
||
sha256 x 2,016,129 ops/sec @ 496ns/op
|
||
sha512 x 740,740 ops/sec @ 1μs/op
|
||
sha3_256 x 287,686 ops/sec @ 3μs/op
|
||
sha3_512 x 288,267 ops/sec @ 3μs/op
|
||
k12 x 476,190 ops/sec @ 2μs/op
|
||
blake2b x 410,340 ops/sec @ 2μs/op
|
||
blake2s x 942,507 ops/sec @ 1μs/op
|
||
blake3 x 1,006,036 ops/sec @ 994ns/op
|
||
ripemd160 x 1,410,437 ops/sec @ 709ns/op
|
||
md5 x 1,663,893 ops/sec @ 601ns/op
|
||
sha1 x 1,589,825 ops/sec @ 629ns/op
|
||
|
||
# 1MB
|
||
sha256 x 331 ops/sec @ 3ms/op
|
||
sha512 x 128 ops/sec @ 7ms/op
|
||
sha3_256 x 39 ops/sec @ 25ms/op
|
||
sha3_512 x 21 ops/sec @ 46ms/op
|
||
kt128 x 91 ops/sec @ 10ms/op
|
||
kt256 x 75 ops/sec @ 13ms/op
|
||
turboshake128 x 93 ops/sec @ 10ms/op
|
||
blake256 x 57 ops/sec @ 17ms/op
|
||
blake2b x 61 ops/sec @ 16ms/op
|
||
blake2s x 78 ops/sec @ 12ms/op
|
||
blake3 x 95 ops/sec @ 10ms/op
|
||
ripemd160 x 177 ops/sec @ 5ms/op
|
||
md5 x 250 ops/sec @ 3ms/op
|
||
sha1 x 416 ops/sec @ 2ms/op
|
||
|
||
# MAC
|
||
hmac(sha256) x 599,880 ops/sec @ 1μs/op
|
||
hmac(sha512) x 197,122 ops/sec @ 5μs/op
|
||
kmac256 x 87,981 ops/sec @ 11μs/op
|
||
blake3(key) x 796,812 ops/sec @ 1μs/op
|
||
|
||
# KDF
|
||
hkdf(sha256) x 259,942 ops/sec @ 3μs/op
|
||
blake3(context) x 424,808 ops/sec @ 2μs/op
|
||
pbkdf2(sha256, c: 2 ** 18) x 5 ops/sec @ 197ms/op
|
||
pbkdf2(sha512, c: 2 ** 18) x 1 ops/sec @ 630ms/op
|
||
scrypt(n: 2 ** 18, r: 8, p: 1) x 2 ops/sec @ 400ms/op
|
||
argon2id(t: 1, m: 256MB) 2881ms
|
||
```
|
||
|
||
The library could be 3x faster by utilizing loop unrolling. It isn't used because
|
||
unrolling a) would increase bundle size b) make lib un-readable c) current perf is "fast enough"
|
||
for most use-cases.
|
||
|
||
## License
|
||
|
||
The MIT License (MIT)
|
||
|
||
Copyright (c) 2022 Paul Miller [(https://paulmillr.com)](https://paulmillr.com)
|
||
|
||
See LICENSE file.
|