Gathering detailed insights and metrics for @noble/ciphers
Gathering detailed insights and metrics for @noble/ciphers
Gathering detailed insights and metrics for @noble/ciphers
Gathering detailed insights and metrics for @noble/ciphers
@noble/hashes
Audited & minimal 0-dependency JS implementation of SHA, RIPEMD, BLAKE, HMAC, HKDF, PBKDF & Scrypt
@noble/curves
Audited & minimal JS implementation of elliptic curve cryptography
@ecies/ciphers
Node/Pure JavaScript symmetric ciphers adapter
@noble/secp256k1
Fastest 4KB JS implementation of secp256k1 ECDH & ECDSA signatures compliant with RFC6979
Audited & minimal JS implementation of Salsa20, ChaCha and AES
npm install @noble/ciphers
99.9
Supply Chain
100
Quality
90.9
Maintenance
100
Vulnerability
100
License
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
216 Stars
220 Commits
8 Forks
5 Watching
2 Branches
6 Contributors
Updated on 29 Nov 2024
Minified
Minified + Gzipped
TypeScript (54.9%)
JavaScript (45.1%)
Cumulative downloads
Total Downloads
Last day
-16.3%
56,220
Compared to previous day
Last week
2.3%
349,452
Compared to previous week
Last month
34%
1,434,097
Compared to previous month
Last year
2,319.8%
6,018,561
Compared to previous year
Audited & minimal JS implementation of Salsa20, ChaCha and AES.
Take a glance at GitHub Discussions for questions and support.
noble cryptography — high-security, easily auditable set of contained cryptographic libraries and tools.
npm install @noble/ciphers
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-ciphers.js is also available.
1// import * from '@noble/ciphers'; // Error: use sub-imports, to ensure small app size 2import { xchacha20poly1305 } from '@noble/ciphers/chacha'; 3// import { xchacha20poly1305 } from 'npm:@noble/ciphers@1.1.0/chacha'; // Deno
[!NOTE] Use different nonce every time
encrypt()
is done.
1import { xchacha20poly1305 } from '@noble/ciphers/chacha'; 2import { utf8ToBytes } from '@noble/ciphers/utils'; 3import { randomBytes } from '@noble/ciphers/webcrypto'; 4const key = randomBytes(32); // random key 5// const key = new Uint8Array([ // existing key 6// 169, 88, 160, 139, 168, 29, 147, 196, 14, 88, 237, 76, 243, 177, 109, 140, 7// 195, 140, 80, 10, 216, 134, 215, 71, 191, 48, 20, 104, 189, 37, 38, 55, 8// ]); 9// import { hexToBytes } from '@noble/ciphers/utils'; // hex key 10// const key = hexToBytes('4b7f89bac90a1086fef73f5da2cbe93b2fae9dfbf7678ae1f3e75fd118ddf999'); 11const nonce = randomBytes(24); 12const chacha = xchacha20poly1305(key, nonce); 13const data = utf8ToBytes('hello, noble'); 14const ciphertext = chacha.encrypt(data); 15const data_ = chacha.decrypt(ciphertext); // utils.bytesToUtf8(data_) === data
1import { gcm } from '@noble/ciphers/aes'; 2import { utf8ToBytes } from '@noble/ciphers/utils'; 3import { randomBytes } from '@noble/ciphers/webcrypto'; 4const key = randomBytes(32); 5const nonce = randomBytes(24); 6const data = utf8ToBytes('hello, noble'); 7const aes = gcm(key, nonce); 8const ciphertext = aes.encrypt(data); 9const data_ = aes.decrypt(ciphertext); // utils.bytesToUtf8(data_) === data
1import { gcm, siv, ctr, cfb, cbc, ecb } from '@noble/ciphers/aes'; 2import { randomBytes } from '@noble/ciphers/webcrypto'; 3const plaintext = new Uint8Array(32).fill(16); 4for (let cipher of [gcm, siv]) { 5 const key = randomBytes(32); // 24 for AES-192, 16 for AES-128 6 const nonce = randomBytes(12); 7 const ciphertext_ = cipher(key, nonce).encrypt(plaintext); 8 const plaintext_ = cipher(key, nonce).decrypt(ciphertext_); 9} 10for (const cipher of [ctr, cbc, cfb]) { 11 const key = randomBytes(32); // 24 for AES-192, 16 for AES-128 12 const nonce = randomBytes(16); 13 const ciphertext_ = cipher(key, nonce).encrypt(plaintext); 14 const plaintext_ = cipher(key, nonce).decrypt(ciphertext_); 15} 16for (const cipher of [ecb]) { 17 const key = randomBytes(32); // 24 for AES-192, 16 for AES-128 18 const ciphertext_ = cipher(key).encrypt(plaintext); 19 const plaintext_ = cipher(key).decrypt(ciphertext_); 20}
Noble implements AES. Sometimes people want to use built-in crypto.subtle
instead. However, it has terrible API. We simplify access to built-ins.
[!NOTE] Webcrypto methods are always async.
1import { gcm, ctr, cbc, randomBytes } from '@noble/ciphers/webcrypto'; 2const plaintext = new Uint8Array(32).fill(16); 3const key = randomBytes(32); 4for (const cipher of [gcm]) { 5 const nonce = randomBytes(12); 6 const ciphertext_ = await cipher(key, nonce).encrypt(plaintext); 7 const plaintext_ = await cipher(key, nonce).decrypt(ciphertext_); 8} 9for (const cipher of [ctr, cbc]) { 10 const nonce = randomBytes(16); 11 const ciphertext_ = await cipher(key, nonce).encrypt(plaintext); 12 const plaintext_ = await cipher(key, nonce).decrypt(ciphertext_); 13}
1import { aeskw, aeskwp } from '@noble/ciphers/aes'; 2import { hexToBytes } from '@noble/ciphers/utils'; 3 4const kek = hexToBytes('000102030405060708090A0B0C0D0E0F'); 5const keyData = hexToBytes('00112233445566778899AABBCCDDEEFF'); 6const ciphertext = aeskw(kek).encrypt(keyData);
We provide API that manages nonce internally instead of exposing them to library's user.
For encrypt
, a nonceBytes
-length buffer is fetched from CSPRNG and prenended to encrypted ciphertext.
For decrypt
, first nonceBytes
of ciphertext are treated as nonce.
1import { xchacha20poly1305 } from '@noble/ciphers/chacha'; 2import { managedNonce } from '@noble/ciphers/webcrypto'; 3import { hexToBytes, utf8ToBytes } from '@noble/ciphers/utils'; 4const key = hexToBytes('fa686bfdffd3758f6377abbc23bf3d9bdc1a0dda4a6e7f8dbdd579fa1ff6d7e1'); 5const chacha = managedNonce(xchacha20poly1305)(key); // manages nonces for you 6const data = utf8ToBytes('hello, noble'); 7const ciphertext = chacha.encrypt(data); 8const data_ = chacha.decrypt(ciphertext);
To avoid additional allocations, Uint8Array can be reused between encryption and decryption calls.
[!NOTE] Some ciphers don't support unaligned (
byteOffset % 4 !== 0
) Uint8Array as destination. It can decrease performance, making the optimization pointless.
1import { chacha20poly1305 } from '@noble/ciphers/chacha'; 2import { utf8ToBytes } from '@noble/ciphers/utils'; 3import { randomBytes } from '@noble/ciphers/webcrypto'; 4 5const key = randomBytes(32); 6const nonce = randomBytes(12); 7const tagLength = 16; 8 9const input = utf8ToBytes('hello, noble'); // length == 12 10const inputLength = 12; 11 12// plaintext + ciphertext + tag: 28 bytes 13const buf = new Uint8Array(inputLength + inputLength + tagLength); 14buf.set(msg, 0); // first inputLength bytes 15const _start = buf.subarray(0, inputLength); // 0..12 16const _end = buf.subarray(inputLength); // 12..28 17 18const chacha = chacha20poly1305(key, nonce); 19chacha.encrypt(_start, _end); 20chacha.decrypt(_end, _start); // _start now same as msg
1import { gcm, siv } from '@noble/ciphers/aes'; 2import { xsalsa20poly1305 } from '@noble/ciphers/salsa'; 3import { secretbox } from '@noble/ciphers/salsa'; // == xsalsa20poly1305 4import { chacha20poly1305, xchacha20poly1305 } from '@noble/ciphers/chacha'; 5 6// Unauthenticated encryption: make sure to use HMAC or similar 7import { ctr, cfb, cbc, ecb } from '@noble/ciphers/aes'; 8import { salsa20, xsalsa20 } from '@noble/ciphers/salsa'; 9import { chacha20, xchacha20, chacha8, chacha12 } from '@noble/ciphers/chacha'; 10 11// KW 12import { aeskw, aeskwp } from '@noble/ciphers/aes'; 13 14// Utilities 15import { bytesToHex, hexToBytes, bytesToUtf8, utf8ToBytes } from '@noble/ciphers/utils'; 16import { managedNonce, randomBytes } from '@noble/ciphers/webcrypto'; 17import { poly1305 } from '@noble/ciphers/_poly1305'; 18import { ghash, polyval } from '@noble/ciphers/_polyval';
hash(key)
can be included in ciphertext,
however, this would violate ciphertext indistinguishability:
an attacker would know which key was used - so HKDF(key, i)
could be used instead.We suggest to use XChaCha20-Poly1305. If you can't use it, prefer AES-GCM-SIV, or AES-GCM.
Math.random
etc.01, 02...
Most ciphers need a key and a nonce (aka initialization vector / IV) to encrypt a data:
ciphertext = encrypt(plaintext, key, nonce)
Repeating (key, nonce) pair with different plaintexts would allow an attacker to decrypt it:
ciphertext_a = encrypt(plaintext_a, key, nonce)
ciphertext_b = encrypt(plaintext_b, key, nonce)
stream_diff = xor(ciphertext_a, ciphertext_b) # Break encryption
So, you can't repeat nonces. One way of doing so is using counters:
for i in 0..:
ciphertext[i] = encrypt(plaintexts[i], key, i)
Another is generating random nonce every time:
for i in 0..:
rand_nonces[i] = random()
ciphertext[i] = encrypt(plaintexts[i], key, rand_nonces[i])
Counters are OK, but it's not always possible to store current counter value: e.g. in decentralized, unsyncable systems.
Randomness is OK, but there's a catch:
ChaCha20 and AES-GCM use 96-bit / 12-byte nonces, which implies higher chance of collision.
In the example above, random()
can collide and produce repeating nonce.
Chance is even higher for 64-bit nonces, which GCM allows - don't use them.
To safely use random nonces, utilize XSalsa20 or XChaCha: they increased nonce length to 192-bit, minimizing a chance of collision. AES-SIV is also fine. In situations where you can't use eXtended-nonce algorithms, key rotation is advised. hkdf would work great for this case.
A "protected message" would mean a probability of 2**-50
that a passive attacker
successfully distinguishes the ciphertext outputs of the AEAD scheme from the outputs
of a random function. See draft-irtf-cfrg-aead-limits for details.
2**36-256
2**38-64
2**32.5
2**46
, but only integrity is affected, not confidentiality2**72
2**69/B
where B is max blocks encrypted by a key. Meaning
2**59
for 1KB, 2**49
for 1MB, 2**39
for 1GB2**100
cipher = encrypt(block, key)
. Data is split into 128-bit blocks. Encrypted in 10/12/14 rounds (128/192/256bit). Every round does:
For non-deterministic (not ECB) schemes, initialization vector (IV) is mixed to block/key; and each new round either depends on previous block's key, or on some counter.
[i][j]
tweak arguments corresponding to sector i and 16-byte block (part of sector) j.
Lacks MAC.GCM / SIV are not ideal:
2**32
(4B) msgsThe 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.
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.
The library uses T-tables for AES, which leak access timings. This is also done in OpenSSL and Go stdlib for performance reasons.
npm-diff
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.
To summarize, noble is the fastest JS implementation of Salsa, ChaCha and AES.
You can gain additional speed-up and
avoid memory allocations by passing output
uint8array into encrypt / decrypt methods.
Benchmark results on Apple M2 with node v22:
encrypt (64B)
├─xsalsa20poly1305 x 485,908 ops/sec @ 2μs/op
├─chacha20poly1305 x 414,250 ops/sec @ 2μs/op
├─xchacha20poly1305 x 331,674 ops/sec @ 3μs/op
├─aes-256-gcm x 144,237 ops/sec @ 6μs/op
└─aes-256-gcm-siv x 121,373 ops/sec @ 8μs/op
encrypt (1KB)
├─xsalsa20poly1305 x 136,574 ops/sec @ 7μs/op
├─chacha20poly1305 x 136,017 ops/sec @ 7μs/op
├─xchacha20poly1305 x 126,008 ops/sec @ 7μs/op
├─aes-256-gcm x 40,149 ops/sec @ 24μs/op
└─aes-256-gcm-siv x 37,420 ops/sec @ 26μs/op
encrypt (8KB)
├─xsalsa20poly1305 x 22,517 ops/sec @ 44μs/op
├─chacha20poly1305 x 23,187 ops/sec @ 43μs/op
├─xchacha20poly1305 x 22,837 ops/sec @ 43μs/op
├─aes-256-gcm x 7,993 ops/sec @ 125μs/op
└─aes-256-gcm-siv x 7,836 ops/sec @ 127μs/op
encrypt (1MB)
├─xsalsa20poly1305 x 186 ops/sec @ 5ms/op
├─chacha20poly1305 x 191 ops/sec @ 5ms/op
├─xchacha20poly1305 x 191 ops/sec @ 5ms/op
├─aes-256-gcm x 71 ops/sec @ 14ms/op
└─aes-256-gcm-siv x 75 ops/sec @ 13ms/op
Unauthenticated encryption:
encrypt (64B)
├─salsa x 1,221,001 ops/sec @ 819ns/op
├─chacha x 1,373,626 ops/sec @ 728ns/op
├─xsalsa x 1,019,367 ops/sec @ 981ns/op
└─xchacha x 1,019,367 ops/sec @ 981ns/op
encrypt (1KB)
├─salsa x 349,162 ops/sec @ 2μs/op
├─chacha x 372,717 ops/sec @ 2μs/op
├─xsalsa x 327,868 ops/sec @ 3μs/op
└─xchacha x 332,446 ops/sec @ 3μs/op
encrypt (8KB)
├─salsa x 55,178 ops/sec @ 18μs/op
├─chacha x 51,535 ops/sec @ 19μs/op
├─xsalsa x 54,274 ops/sec @ 18μs/op
└─xchacha x 55,645 ops/sec @ 17μs/op
encrypt (1MB)
├─salsa x 451 ops/sec @ 2ms/op
├─chacha x 464 ops/sec @ 2ms/op
├─xsalsa x 455 ops/sec @ 2ms/op
└─xchacha x 462 ops/sec @ 2ms/op
AES
encrypt (64B)
├─ctr-256 x 679,347 ops/sec @ 1μs/op
├─cbc-256 x 699,300 ops/sec @ 1μs/op
└─ecb-256 x 717,875 ops/sec @ 1μs/op
encrypt (1KB)
├─ctr-256 x 93,423 ops/sec @ 10μs/op
├─cbc-256 x 95,721 ops/sec @ 10μs/op
└─ecb-256 x 154,726 ops/sec @ 6μs/op
encrypt (8KB)
├─ctr-256 x 12,908 ops/sec @ 77μs/op
├─cbc-256 x 13,411 ops/sec @ 74μs/op
└─ecb-256 x 22,681 ops/sec @ 44μs/op
encrypt (1MB)
├─ctr-256 x 105 ops/sec @ 9ms/op
├─cbc-256 x 108 ops/sec @ 9ms/op
└─ecb-256 x 181 ops/sec @ 5ms/op
Compare to other implementations:
xsalsa20poly1305 (encrypt, 1MB)
├─tweetnacl x 108 ops/sec @ 9ms/op
└─noble x 190 ops/sec @ 5ms/op
chacha20poly1305 (encrypt, 1MB)
├─node x 1,360 ops/sec @ 735μs/op
├─stablelib x 117 ops/sec @ 8ms/op
└─noble x 193 ops/sec @ 5ms/op
chacha (encrypt, 1MB)
├─node x 2,035 ops/sec @ 491μs/op
├─stablelib x 206 ops/sec @ 4ms/op
└─noble x 474 ops/sec @ 2ms/op
ctr-256 (encrypt, 1MB)
├─node x 3,530 ops/sec @ 283μs/op
├─stablelib x 70 ops/sec @ 14ms/op
├─aesjs x 31 ops/sec @ 32ms/op
├─noble-webcrypto x 4,589 ops/sec @ 217μs/op
└─noble x 107 ops/sec @ 9ms/op
cbc-256 (encrypt, 1MB)
├─node x 993 ops/sec @ 1ms/op
├─stablelib x 63 ops/sec @ 15ms/op
├─aesjs x 29 ops/sec @ 34ms/op
├─noble-webcrypto x 1,087 ops/sec @ 919μs/op
└─noble x 110 ops/sec @ 9ms/op
gcm-256 (encrypt, 1MB)
├─node x 3,196 ops/sec @ 312μs/op
├─stablelib x 27 ops/sec @ 36ms/op
├─noble-webcrypto x 4,059 ops/sec @ 246μs/op
└─noble x 74 ops/sec @ 13ms/op
npm install
to install build dependencies like TypeScriptnpm run build
to compile TypeScript codenpm run test
will execute all main testsCheck out paulmillr.com/noble for useful resources, articles, documentation and demos related to the library.
The MIT License (MIT)
Copyright (c) 2023 Paul Miller (https://paulmillr.com) Copyright (c) 2016 Thomas Pornin pornin@bolet.org
See LICENSE file.
No vulnerabilities found.
No security vulnerabilities found.