- Notifications
You must be signed in to change notification settings - Fork50
Audited & minimal JS implementation of hash functions, MACs and KDFs.
License
paulmillr/noble-hashes
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Audited & minimal JS implementation of hash functions, MACs and KDFs.
- 🔒Audited 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 tests and fuzzing ensure correctness
- 🔁 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 & KangarooTwelve
- 🪶 47KB for everything, 5KB (2.5KB gzipped) for single-hash build
Take a glance atGitHub Discussions for questions and support.The library's initial development was funded byEthereum Foundation.
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,curves,hashes,post-quantum,4kbsecp256k1 /ed25519
- Check out homepagefor reading resources, documentation and apps built with noble
npm install @noble/hashes
deno add jsr:@noble/hashes
deno doc jsr:@noble/hashes
# command-line documentation
We support all major platforms and runtimes.For React Native, you may need apolyfill for getRandomValues.A standalone filenoble-hashes.js is also available.
// import * from '@noble/hashes'; // Error: use sub-imports, to ensure small app sizeimport{sha256}from'@noble/hashes/sha2';// ESM & Common.jssha256(newUint8Array([1,2,3]);// returns Uint8Arraysha256('abc');// == sha256(new TextEncoder().encode('abc'))// Available modulesimport{sha256,sha384,sha512,sha224,sha512_256,sha512_384}from'@noble/hashes/sha2';import{sha3_256,sha3_512,keccak_256,keccak_512,shake128,shake256}from'@noble/hashes/sha3';import{cshake256,turboshake256,kmac256,tuplehash256,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{blake256,blake512}from'@noble/hashes/blake1';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*asutilsfrom'@noble/hashes/utils';// bytesToHex, hexToBytes, etc
- sha2: sha256, sha384, sha512
- sha3: FIPS, SHAKE, Keccak
- sha3-addons: cSHAKE, KMAC, K12, M14, TurboSHAKE
- ripemd160 |blake, blake2b, blake2s, blake3 |legacy: sha1, md5
- MACs:hmac |sha3-addons kmac |blake3 key mode
- KDFs:hkdf |pbkdf2 |scrypt |argon2
- utils
- Security |Speed |Contributing & testing |License
Hash functions:
- receive & return
Uint8Array
- may receive
string
(not hex), which is automatically utf8-encoded toUint8Array
- support little-endian architecture; also experimentally big-endian
- can hash up to 4GB per chunk, with any amount of chunks
- can be constructed via
hash.create()
method- the result is
Hash
subclass instance, which hasupdate()
anddigest()
methods digest()
finalizes the hash and makes it no longer usable
- the result is
- some of them can receive
options
:- second argument to hash function:
blake3('abc', { key: 'd', dkLen: 32 })
- first argument to class initializer:
blake3.create({ context: 'e', dkLen: 32 })
- second argument to hash function:
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:consth1a=sha256('abc');// Variant B:consth1b=sha256.create().update(Uint8Array.from([1,2,3])).digest();for(lethashof[sha384,sha512,sha224,sha512_256,sha512_384]){constres1=hash('abc');constres2=hash.create().update('def').update(Uint8Array.from([1,2,3])).digest();}
SeeRFC 4634 andthe paper on truncated SHA512/256.
// prettier-ignoreimport{sha3_224,sha3_256,sha3_384,sha3_512,keccak_224,keccak_256,keccak_384,keccak_512,shake128,shake256,}from'@noble/hashes/sha3';consth5a=sha3_256('abc');consth5b=sha3_256.create().update(Uint8Array.from([1,2,3])).digest();consth6a=keccak_256('abc');consth7a=shake128('abc',{dkLen:512});consth7b=shake256('abc',{dkLen:512});
Check outthe differences between SHA-3 and Keccak
// prettier-ignoreimport{cshake128,cshake256,turboshake128,turboshake256,kmac128,kmac256,tuplehash256,parallelhash256,k12,m14,keccakprg}from'@noble/hashes/sha3-addons';consth7c=cshake128('abc',{personalization:'def'});consth7d=cshake256('abc',{personalization:'def'});consth7e=kmac128('key','message');consth7f=kmac256('key','message');consth7h=k12('abc');consth7g=m14('abc');consth7t1=turboshake128('abc');consth7t2=turboshake256('def',{D:0x05});consth7i=tuplehash256(['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.consth7j=parallelhash256('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.constp=keccakprg(254);p.feed('test');constrand1b=p.fetch(1);
- FullNIST SP 800-185:cSHAKE, KMAC, TupleHash, ParallelHash + XOF variants
- Reduced-round Keccak:
- 🦘 K12 aka KangarooTwelve
- M14 aka MarsupilamiFourteen
- TurboSHAKE
- KeccakPRG: Pseudo-random generator based on Keccak
import{ripemd160}from'@noble/hashes/ripemd160';consthash8=ripemd160('abc');consthash9=ripemd160.create().update(Uint8Array.from([1,2,3])).digest();
import{blake224,blake256,blake384,blake512}from'@noble/hashes/blake1';import{blake2b}from'@noble/hashes/blake2b';import{blake2s}from'@noble/hashes/blake2s';import{blake3}from'@noble/hashes/blake3';consth_b1_224=blake224('abc');consth_b1_256=blake256('abc');consth_b1_384=blake384('abc');consth_b1_512=blake512('abc');consth10a=blake2s('abc');constb2params={key:newUint8Array([1]),personalization:t,salt:t,dkLen:32};consth10b=blake2s('abc',b2params);consth10c=blake2s.create(b2params).update(Uint8Array.from([1,2,3])).digest();// All params are optionalconsth11=blake3('abc',{dkLen:256});consth11_mac=blake3('abc',{key:newUint8Array(32)});consth11_kdf=blake3('abc',{context:'application name'});
- Blake1 is legacy hash, one of SHA3 proposals. It is rarely used anywhere. Seepdf.
- Blake2 is popular fast hash. blake2b focuses on 64-bit platforms while blake2s is for 8-bit to 32-bit ones. SeeRFC 7693,Website
- Blake3 is faster, reduced-round blake2. SeeWebsite & specs
SHA1 (RFC 3174) and MD5 (RFC 1321) legacy, broken hash functions.Don't use them in a new protocol. What "broken" means:
- Collisions can be made with 2^18 effort in MD5, 2^60 in SHA1.
- No practical pre-image attacks (only theoretical, 2^123.4)
- HMAC seems kinda ok:https://datatracker.ietf.org/doc/html/rfc6151
import{sha1,md5}from'@noble/hashes/legacy';consth12s=sha1('def');consth12m=md5('xcb');
import{hmac}from'@noble/hashes/hmac';import{sha256}from'@noble/hashes/sha2';constmac1=hmac(sha256,'key','message');constmac2=hmac.create(sha256,Uint8Array.from([1,2,3])).update(Uint8Array.from([4,5,6])).digest();
MatchesRFC 2104.
import{hkdf}from'@noble/hashes/hkdf';import{sha256}from'@noble/hashes/sha2';import{randomBytes}from'@noble/hashes/utils';constinputKey=randomBytes(32);constsalt=randomBytes(32);constinfo='application-key';consthk1=hkdf(sha256,inputKey,salt,info,32);// == same asimport*ashkdffrom'@noble/hashes/hkdf';import{sha256}from'@noble/hashes/sha2';constprk=hkdf.extract(sha256,inputKey,salt);consthk2=hkdf.expand(sha256,prk,info,dkLen);
MatchesRFC 5869.
import{pbkdf2,pbkdf2Async}from'@noble/hashes/pbkdf2';import{sha256}from'@noble/hashes/sha2';constpbkey1=pbkdf2(sha256,'password','salt',{c:32,dkLen:32});constpbkey2=awaitpbkdf2Async(sha256,'password','salt',{c:32,dkLen:32});constpbkey3=awaitpbkdf2Async(sha256,Uint8Array.from([1,2,3]),Uint8Array.from([4,5,6]),{c:32,dkLen:32,});
MatchesRFC 2898.
import{scrypt,scryptAsync}from'@noble/hashes/scrypt';constscr1=scrypt('password','salt',{N:2**16,r:8,p:1,dkLen:32});constscr2=awaitscryptAsync('password','salt',{N:2**16,r:8,p:1,dkLen:32});constscr3=awaitscryptAsync(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)});
N, r, p
are work factors. To understand them, seethe blog post.r: 8, p: 1
are common. JS doesn't support parallelization, making increasing p meaningless.dkLen
is the length of output bytes e.g.32
or64
onProgress
can be used with async version of the function to report progress to a user.maxmem
prevents DoS and is limited to1GB + 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 M2 (mobile phones can be 1x-4x slower):
N pow | Time |
---|---|
16 | 0.17s |
17 | 0.35s |
18 | 0.7s |
19 | 1.4s |
20 | 2.9s |
21 | 5.6s |
22 | 11s |
23 | 26s |
24 | 56s |
Note
We support N larger than2**20
where available, however,not all JS engines support >= 2GB ArrayBuffer-s.When using such N, you'll need to manually adjustmaxmem
, using formula above.Other JS implementations don't support large N-s.
import{argon2d,argon2i,argon2id}from'@noble/hashes/argon2';constresult=argon2id('password','saltsalt',{t:2,m:65536,p:1,maxmem:2**32-1});
Argon2RFC 9106 implementation.
Warning
Argon2 can't be fast in JS, because there is no fast Uint64Array.It is suggested to useScrypt instead.Being 5x slower than native code means brute-forcing attackers have bigger advantage.
import{bytesToHexastoHex,randomBytes}from'@noble/hashes/utils';console.log(toHex(randomBytes(32)));
bytesToHex
will convertUint8Array
to a hex stringrandomBytes(bytes)
will produce cryptographically secure randomUint8Array
of lengthbytes
The library has been independently audited:
- at version 1.0.0, in Jan 2022, byCure53
- PDFs:website,in-repo
- Changes since audit.
- Scope: everything, besides
blake3
,sha3-addons
,sha1
andargon2
, which have not been audited - The audit has been funded byEthereum Foundation with help ofNomic Labs
It is tested against property-based, cross-library and Wycheproof vectors,and is being fuzzed inthe separate repo.
If you see anything unusual: investigate and report.
We're targetting algorithmic constant time.JIT-compiler andGarbage Collector make "constant time"extremely hard to achievetiming attack resistancein a scripting language. Which meansany other JS library can't haveconstant-timeness. Even statically typed Rust, a language without GC,makes it harder to achieve constant-timefor some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones.Use low-level libraries & languages.
The library shares state buffers between hashfunction calls. The buffers are zeroed-out after each call. However, if an attackercan 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: derivingkey 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 codechunk 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 withoutcomplex tests-suite which will dump process memory and verify that there isno 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 sametest-suite in the actual application that consumes the library
- Commits are signed with PGP keys, to prevent forgery. Make sure to verify commit signatures
- Releases are transparent and built on GitHub CI. Make sure to verifyprovenance logs
- Use GitHub CLI to verify single-file builds:
gh attestation verify --owner paulmillr noble-hashes.js
- Use GitHub CLI to verify single-file builds:
- Rare releasing is followed to ensure less re-audit need for end-users
- Dependencies are minimized and locked-down: any dependency could get hacked and users will be downloading malware with every install.
- We make sure to use as few dependencies as possible
- Automatic dep updates are prevented by locking-down version ranges; diffs are checked with
npm-diff
- Dev Dependencies are disabled for end-users; they are only used to develop / build the source code
For this package, there are 0 dependencies; and a few dev dependencies:
- micro-bmark, micro-should and jsbt are used for benchmarking / testing / build tooling and developed by the same author
- prettier, fast-check and typescript are used for code quality / test generation / ts compilation. It's hard to audit their source code thoroughly and fully because of their size
We're deferring to built-incrypto.getRandomValueswhich 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 weaknessis even worse: there is no reliable userspace source of quality entropy.
Cryptographically relevant quantum computer, if built, will allow toutilize 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 hashesafter 2030.
npm run bench
Benchmarks measured on Apple M2 with node v22.
32Bsha256 x 1,377,410 ops/sec @ 726ns/opsha384 x 518,403 ops/sec @ 1μs/opsha512 x 518,941 ops/sec @ 1μs/opsha3_256 x 188,608 ops/sec @ 5μs/opsha3_512 x 190,114 ops/sec @ 5μs/opk12 x 324,254 ops/sec @ 3μs/opm14 x 286,204 ops/sec @ 3μs/opblake2b x 352,236 ops/sec @ 2μs/opblake2s x 586,510 ops/sec @ 1μs/opblake3 x 681,198 ops/sec @ 1μs/opripemd160 x 1,275,510 ops/sec @ 784ns/op1MBsha256 x 197 ops/sec @ 5ms/opsha384 x 86 ops/sec @ 11ms/opsha512 x 86 ops/sec @ 11ms/opsha3_256 x 25 ops/sec @ 39ms/opsha3_512 x 13 ops/sec @ 74ms/opk12 x 58 ops/sec @ 17ms/opm14 x 41 ops/sec @ 24ms/opblake2b x 50 ops/sec @ 19ms/opblake2s x 44 ops/sec @ 22ms/opblake3 x 57 ops/sec @ 17ms/opripemd160 x 193 ops/sec @ 5ms/op# MAChmac(sha256) x 404,203 ops/sec @ 2μs/ophmac(sha512) x 137,136 ops/sec @ 7μs/opkmac256 x 58,799 ops/sec @ 17μs/opblake3(key) x 619,962 ops/sec @ 1μs/op# KDFhkdf(sha256) x 180,538 ops/sec @ 5μs/opblake3(context) x 336,247 ops/sec @ 2μs/oppbkdf2(sha256, c: 2 ** 18) x 3 ops/sec @ 292ms/oppbkdf2(sha512, c: 2 ** 18) x 1 ops/sec @ 920ms/opscrypt(n: 2 ** 18, r: 8, p: 1) x 1 ops/sec @ 605ms/opargon2id(t: 1, m: 256MB) x 0 ops/sec @ 4021ms/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/opSHA384 32B node x 975,609 ops/sec @ 1μs/opSHA512 32B node x 983,284 ops/sec @ 1μs/opSHA3-256 32B node x 910,746 ops/sec @ 1μs/op# keccak, k12, m14 are not implementedBLAKE2b 32B node x 967,117 ops/sec @ 1μs/opBLAKE2s 32B node x 1,055,966 ops/sec @ 947ns/op# BLAKE3 is not implementedRIPEMD160 32B node x 1,002,004 ops/sec @ 998ns/opHMAC-SHA256 32B node x 919,963 ops/sec @ 1μs/opHKDF-SHA256 32 node x 369,276 ops/sec @ 2μs/opPBKDF2-HMAC-SHA256 262144 node x 25 ops/sec @ 39ms/opPBKDF2-HMAC-SHA512 262144 node x 7 ops/sec @ 132ms/opScrypt r: 8, p: 1, n: 262144 node x 1 ops/sec @ 523ms/op
It is possible tomake this library 4x+ faster bydoing code generation of full loop unrolls. We've decided against it. Reasons:
- the library must be auditable, with minimum amount of code, and zero dependencies
- most method invocations with the lib are going to be something like hashing 32b to 64kb of data
- hashing big inputs is 10x faster with low-level languages, which means you should probably pick 'em instead
The current performance is good enough when compared to other projects; SHA256 takes only 900 nanoseconds to run.
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, which may need their deps first (npm run bench:install
)npm run build:release
will build single file- There isadditional 20-min DoS test
npm run test:dos
and 2-hour "big" multicore testnpm run test:big
.Seeour approach to testing
Check outgithub.com/paulmillr/guidelinesfor general coding practices and rules.
Seepaulmillr.com/noblefor useful resources, articles, documentation and demosrelated to the library.
The MIT License (MIT)
Copyright (c) 2022 Paul Miller(https://paulmillr.com)
See LICENSE file.
About
Audited & minimal JS implementation of hash functions, MACs and KDFs.