Skip to content

Commit 54dfdfd

Browse files
committed
Initial commit
0 parents  commit 54dfdfd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+23008
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
/build/
3+
/lib

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2021 Paul Miller (https://paulmillr.com)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the “Software”), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
# noble-hashes ![Node CI](https://github.com/paulmillr/noble-shashesecp256k1/workflows/Node%20CI/badge.svg) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
2+
3+
Fast, secure & minimal JS implementation of SHA2, SHA3, RIPEMD, BLAKE2, HMAC, HKDF, PBKDF2 & Scrypt.
4+
5+
Matches following specs:
6+
7+
- SHA2 aka SHA256 / SHA512 [(RFC 4634)](https://datatracker.ietf.org/doc/html/rfc4634)
8+
- SHA3 & Keccak ([FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf), [Website](https://keccak.team/keccak.html))
9+
- RIPEMD-160 ([RFC 2286](https://datatracker.ietf.org/doc/html/rfc2286), [Website](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html))
10+
- BLAKE2b, BLAKE2s ([RFC 7693](https://datatracker.ietf.org/doc/html/rfc7693), [Website](https://www.blake2.net))
11+
- HMAC [(RFC 2104)](https://datatracker.ietf.org/doc/html/rfc2104)
12+
- HKDF [(RFC 5869)](https://datatracker.ietf.org/doc/html/rfc5869)
13+
- PBKDF2 [(RFC 2898)](https://datatracker.ietf.org/doc/html/rfc2898)
14+
- Scrypt ([RFC 7914](https://datatracker.ietf.org/doc/html/rfc7914), [Website](https://www.tarsnap.com/scrypt.html))
15+
16+
Overall size of all primitives is ~1800 TypeScript LOC, or 35KB minified (12KB gzipped).
17+
You can select specific functions, SHA256-only would be ~400 LOC / 6.5KB minified (3KB gzipped).
18+
19+
### This library belongs to *noble* crypto
20+
21+
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
22+
23+
- No dependencies, small files
24+
- Easily auditable TypeScript/JS code
25+
- Supported in all major browsers and stable node.js versions
26+
- All releases are signed with PGP keys
27+
- Check out all libraries:
28+
[secp256k1](https://github.com/paulmillr/noble-secp256k1),
29+
[ed25519](https://github.com/paulmillr/noble-ed25519),
30+
[bls12-381](https://github.com/paulmillr/noble-bls12-381),
31+
[hashes](https://github.com/paulmillr/noble-hashes)
32+
33+
## Usage
34+
35+
Use NPM in node.js / browser, or include single file from
36+
[GitHub's releases page](https://github.com/paulmillr/noble-hashes/releases):
37+
38+
> npm install @noble/hashes
39+
40+
The library does not have an entry point. It allows you to select specific primitives and drop everything else. If you only want to use sha256, just use the library with rollup or other bundlers. This is done to make your bundles tiny.
41+
42+
```js
43+
const { sha256 } = require('@noble/hashes/lib/sha256');
44+
console.log(sha256(new Uint8Array([1, 2, 3])));
45+
// Uint8Array(32) [3, 144, 88, 198, 242, 192, 203, 73, ...]
46+
console.log(sha256('abc'))); // you could also pass strings
47+
48+
const { sha512 } = require('@noble/hashes/lib/sha512');
49+
// prettier-ignore
50+
const {
51+
sha3_224, sha3_256, sha3_384, sha3_512,
52+
keccak_224, keccak_256, keccak_384, keccak_512
53+
} = require('@noble/hashes/lib/sha3');
54+
const { ripemd160 } = require('@noble/hashes/lib/ripemd160');
55+
const { blake2b } = require('@noble/hashes/lib/blake2b');
56+
const { blake2s } = require('@noble/hashes/lib/blake2s');
57+
const { hmac } = require('@noble/hashes/lib/hmac');
58+
const { hkdf } = require('@noble/hashes/lib/hkdf');
59+
const { pbkdf2, pbkdf2Async } = require('@noble/hashes/lib/pbkdf2');
60+
const { scrypt, scryptAsync } = require('@noble/hashes/lib/scrypt');
61+
62+
// small utility method that converts bytes to hex
63+
const { toHex } = require('@noble/hashes/lib/utils');
64+
console.log(toHex(sha256('abc')));
65+
// ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
66+
```
67+
68+
## API
69+
70+
Any hash function:
71+
72+
1. Can be called directly, like `sha256(new Uint8Array([1, 3]))`,
73+
or initialized as a class: `sha256.init().update(new Uint8Array([1, 3]).digest()`
74+
2. Can receive either an `Uint8Array`, or a `string` that would be
75+
automatically converted to `Uint8Array` via `new TextEncoder().encode(string)`.
76+
The output is always `Uint8Array`.
77+
3. Can receive an option object as a second argument: `sha256('abc', {cleanup: true})`;
78+
or `sha256.init({cleanup: true}).update('abc').digest()`
79+
80+
##### SHA2 (sha256, sha512)
81+
82+
```typescript
83+
import { sha256 } from '@noble/hashes/lib/sha256.js';
84+
// function sha256(data: Uint8Array): Uint8Array;
85+
const hash1 = sha256('abc');
86+
const hash2 = sha256.init().update(Uint8Array.from([1, 2, 3])).digest();
87+
```
88+
89+
```typescript
90+
import { sha512 } from '@noble/hashes/lib/sha512.js';
91+
// function sha512(data: Uint8Array): Uint8Array;
92+
const hash3 = sha512('abc');
93+
const hash4 = sha512.init().update(Uint8Array.from([1, 2, 3])).digest();
94+
```
95+
96+
##### SHA3 (sha3_256, keccak_256, etc)
97+
98+
```typescript
99+
import {
100+
sha3_224, sha3_256, sha3_384, sha3_512,
101+
keccak_224, keccak_256, keccak_384, keccak_512
102+
} from '@noble/hashes/lib/sha3.js';
103+
const hash5 = sha3_256('abc');
104+
const hash6 = sha3_256.init().update(Uint8Array.from([1, 2, 3])).digest();
105+
const hash7 = keccak_256('abc');
106+
```
107+
108+
##### RIPEMD-160
109+
110+
```typescript
111+
import { ripemd160 } from '@noble/hashes/lib/ripemd160.js';
112+
// function ripemd160(data: Uint8Array): Uint8Array;
113+
const hash8 = ripemd160('abc');
114+
const hash9 = ripemd160().init().update(Uint8Array.from([1, 2, 3])).digest();
115+
```
116+
117+
##### BLAKE2b, BLAKE2s
118+
119+
```typescript
120+
import { blake2b } from '@noble/hashes/lib/blake2b.js';
121+
import { blake2s } from '@noble/hashes/lib/blake2s.js';
122+
const hash10 = blake2s('abc');
123+
const b2params = {key: new Uint8Array([1]), personalization: t, salt: t, dkLen: 32};
124+
const hash11 = blake2s('abc', b2params);
125+
const hash12 = blake2s.init(b2params).update(Uint8Array.from([1, 2, 3])).digest();
126+
```
127+
128+
##### HMAC
129+
130+
```typescript
131+
import { hmac } from '@noble/hashes/lib/mac.js';
132+
import { sha256 } from '@noble/hashes/lib/sha256.js';
133+
const mac1 = hmac(sha256, 'key', 'message');
134+
const mac2 = hmac.init(sha256, Uint8Array.from([1, 2, 3])).update(Uint8Array.from([4, 5, 6]).digest();
135+
```
136+
137+
##### HKDF
138+
139+
```typescript
140+
import { hkdf } from '@noble/hashes/lib/kdf.js';
141+
import { sha256 } from '@noble/hashes/lib/sha256.js';
142+
import { randomBytes } from '@noble/hashes/utils.js';
143+
const inputKey = randomBytes(32);
144+
const salt = randomBytes(32);
145+
const info = 'abc';
146+
const dkLen = 32;
147+
const hk1 = hkdf(sha256, inputKey, salt, info, dkLen);
148+
149+
// == same as
150+
import { hkdf_extract, hkdf_expand } from '@noble/hashes/lib/kdf.js';
151+
import { sha256 } from '@noble/hashes/lib/sha256.js';
152+
const prk = hkdf_extract(sha256, inputKey, salt)
153+
const hk2 = hkdf_expand(sha256, prk, info, dkLen);
154+
```
155+
156+
##### PBKDF2
157+
158+
```typescript
159+
import { pbkdf2, pbkdf2Async } from '@noble/hashes/lib/kdf.js';
160+
import { sha256 } from '@noble/hashes/lib/sha256.js';
161+
const pbkey1 = pbkdf2(sha256, 'password', 'salt', { c: 32, dkLen: 32 });
162+
const pbkey2 = await pbkdf2Async(sha256, 'password', 'salt', { c: 32, dkLen: 32 });
163+
const pbkey3 = await pbkdf2Async(
164+
sha256, Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6]), { c: 32, dkLen: 32 }
165+
);
166+
```
167+
168+
##### Scrypt
169+
170+
```typescript
171+
import { scrypt, scryptAsync } from '@noble/hashes/lib/scrypt.js';
172+
const scr1 = scrypt('password', 'salt', { N: 2 ** 16, r: 8, p: 1, dkLen: 32 });
173+
const scr2 = await scryptAsync('password', 'salt', { N: 2 ** 16, r: 8, p: 1, dkLen: 32 });
174+
const scr3 = await scryptAsync(
175+
Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6]),
176+
{
177+
N: 2 ** 22,
178+
r: 8,
179+
p: 1,
180+
dkLen: 32,
181+
onProgress(percentage) { console.log('progress', percentage); },
182+
maxmem: 2 ** 32 + (128 * 8 * 1) // N * r * p * 128 + (128*r*p)
183+
}
184+
);
185+
```
186+
187+
- `N, r, p` are work factors. To understand them, see [the blog post](https://blog.filippo.io/the-scrypt-parameters/).
188+
- `dkLen` is the length of output bytes
189+
- It is common to use N from `2**10` to `2**22` and `{r: 8, p: 1, dkLen: 32}`
190+
- `onProgress` can be used with async version of the function to report progress to a user.
191+
192+
Memory usage of scrypt is calculated with the formula `N * r * p * 128 + (128 * r * p)`, which means
193+
`{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,
194+
which corresponds to `{N: 2 ** 20, r: 8, p: 1}`. If you want to use higher values, increase `maxmem` using the formula above.
195+
196+
*Note:* noble supports 2**22 (4GB RAM) which is the highest amount amongst JS libs. Many other implementations don't support it.
197+
We cannot support 2**23, because there is a limitation in JS engines that makes allocating
198+
arrays bigger than 4GB impossible, but we're looking into other possible solutions.
199+
200+
##### utils
201+
202+
```typescript
203+
import { bytesToHex as toHex, randomBytes } from '@noble/hashes/lib/scrypt.js';
204+
console.log(toHex(randomBytes(32)));
205+
```
206+
207+
- `bytesToHex` will convert `Uint8Array` to a hex string
208+
- `randomBytes(bytes)` will produce cryptographically secure random `Uint8Array` of length `bytes`
209+
210+
## Security
211+
212+
Noble is production-ready.
213+
214+
The library will be audited by an independent security firm in the next few months.
215+
216+
A note on [timing attacks](https://en.wikipedia.org/wiki/Timing_attack): *JIT-compiler* and *Garbage Collector* make "constant time" extremely hard to achieve 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. Nonetheless we're targetting algorithmic constant time.
217+
218+
We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading rootkits with every `npm install`. Our goal is to minimize this attack vector.
219+
220+
## Speed
221+
222+
Benchmarks measured with Apple M1. Note that PBKDF2 and Scrypt are tested with extremely high
223+
work factor. To run benchmarks, execute `cd test/benchmark && npm i && cd ../../` and then `npm run bench`
224+
225+
```
226+
SHA256 32 B x 954,198 ops/sec @ 1μs/op
227+
SHA512 32 B x 440,722 ops/sec @ 2μs/op
228+
SHA3 32 B x 184,331 ops/sec @ 5μs/op
229+
BLAKE2s 32 B x 464,468 ops/sec @ 2μs/op
230+
BLAKE2b 32 B x 282,965 ops/sec @ 3μs/op
231+
HMAC-SHA256 32 B x 270,343 ops/sec @ 3μs/op
232+
RIPEMD160 32 B x 962,463 ops/sec @ 1μs/op
233+
HKDF-SHA256 32 x 112,688 ops/sec @ 8μs/op
234+
PBKDF2-HMAC-SHA256 262144 x 3 ops/sec @ 319ms/op
235+
PBKDF2-HMAC-SHA512 262144 x 1 ops/sec @ 986ms/op
236+
Scrypt r: 8, p: 1, n: 262144 x 1 ops/sec @ 646ms/op
237+
```
238+
239+
Compare to native node.js implementation that uses C bindings instead of pure-js code:
240+
241+
```
242+
SHA256 32 B node x 569,151 ops/sec @ 1μs/op
243+
SHA512 32 B node x 551,267 ops/sec @ 1μs/op
244+
SHA3 32 B node x 545,553 ops/sec @ 1μs/op
245+
BLAKE2s 32 B node x 545,256 ops/sec @ 1μs/op
246+
BLAKE2b 32 B node x 583,090 ops/sec @ 1μs/op
247+
HMAC-SHA256 32 B node x 500,751 ops/sec @ 1μs/op
248+
RIPEMD160 32 B node x 509,424 ops/sec @ 1μs/op
249+
HKDF-SHA256 32 node x 207,856 ops/sec @ 4μs/op
250+
PBKDF2-256 262144 node x 23 ops/sec @ 42ms/op
251+
Scrypt 262144 node x 1 ops/sec @ 564ms/op
252+
// `scrypt.js` package
253+
Scrypt 262144 scrypt.js x 0 ops/sec @ 1678ms/op
254+
```
255+
256+
It is possible to [make this library 4x+ faster](./test/benchmark/README.md) by
257+
*doing code generation of full loop unrolls*. We've decided against it. Reasons:
258+
259+
- the library must be auditable, with minimum amount of code, and zero dependencies
260+
- most method invocations with the lib are going to be something like hashing 32b to 64kb of data
261+
- hashing big inputs is 10x faster with low-level languages, which means you should probably pick 'em instead
262+
263+
The current performance is good enough when compared to other projects; SHA256 is 1.6x faster than native C bindings.
264+
265+
## Contributing & testing
266+
267+
1. Clone the repository.
268+
2. `npm install` to install build dependencies like TypeScript
269+
3. `npm run build` to compile TypeScript code
270+
4. `npm run test` will execute all main tests
271+
5. `npm run test-dos` will test against DoS; by measuring function complexity. **Takes ~20 minutes**
272+
6. `npm run test-big` will execute hashing on 4GB inputs,
273+
scrypt with 1024 different `N, r, p` combinations, etc. **Takes several hours**. Using 8-32+ core CPU helps.
274+
275+
## License
276+
277+
The MIT License (MIT)
278+
279+
Copyright (c) 2021 Paul Miller [(https://paulmillr.com)](https://paulmillr.com)
280+
281+
See LICENSE file.

package.json

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"name": "@noble/hashes",
3+
"version": "0.1.0",
4+
"description": "Fast 0-dependency JS implementation of SHA2, SHA3, RIPEMD, BLAKE, HMAC, HKDF, PBKDF2, Scrypt",
5+
"main": "lib/index.js",
6+
"directories": {
7+
"lib": "lib",
8+
"test": "test"
9+
},
10+
"files": [
11+
"lib"
12+
],
13+
"scripts": {
14+
"bench": "node test/benchmark/index.js",
15+
"build": "tsc -d",
16+
"build-release": "rollup -c rollup.config.js",
17+
"lint": "prettier --print-width 100 --single-quote --check src",
18+
"test": "node test/index.js",
19+
"test-dos": "node test/slow-dos.test.js",
20+
"test-big": "node test/slow-big.test.js"
21+
},
22+
"author": "Paul Miller (https://paulmillr.com)",
23+
"repository": {
24+
"type": "git",
25+
"url": "https://github.com/paulmillr/noble-secp256k1.git"
26+
},
27+
"license": "MIT",
28+
"browser": {
29+
"crypto": false
30+
},
31+
"devDependencies": {
32+
"@rollup/plugin-commonjs": "^21.0.0",
33+
"@rollup/plugin-node-resolve": "^13.0.5",
34+
"micro-bmark": "^0.1.3",
35+
"micro-should": "^0.2.0",
36+
"prettier": "2.4.1",
37+
"rollup": "~2.58.0",
38+
"typescript": "~4.3.5"
39+
},
40+
"keywords": [
41+
"sha",
42+
"sha2",
43+
"sha3",
44+
"sha256",
45+
"sha512",
46+
"keccak",
47+
"ripemd160",
48+
"blake2",
49+
"hmac",
50+
"hkdf",
51+
"pbkdf2",
52+
"scrypt",
53+
"kdf",
54+
"hash",
55+
"cryptography",
56+
"security",
57+
"noble"
58+
]
59+
}

rollup.config.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import resolve from '@rollup/plugin-node-resolve';
2+
import commonjs from '@rollup/plugin-commonjs';
3+
4+
export default {
5+
input: 'rollup.js',
6+
output: {
7+
file: 'build/noble-hashes.js',
8+
format: 'umd',
9+
name: 'nobleHashes',
10+
exports: 'named',
11+
preferConst: true,
12+
},
13+
plugins: [resolve(), commonjs()],
14+
};

0 commit comments

Comments
 (0)