Skip to content

Commit

Permalink
Refactor package. Add support for unaligned byte arrays. Fix typescri…
Browse files Browse the repository at this point in the history
…pt paths
  • Loading branch information
paulmillr committed Jun 27, 2024
1 parent 2818188 commit ea2f2d2
Show file tree
Hide file tree
Showing 23 changed files with 391 additions and 285 deletions.
77 changes: 41 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ const data_ = chacha.decrypt(ciphertext);

#### Use same array for input and output

This allows re-use Uint8Array between encryption/decryption calls (works if all plaintext or ciphertext are same size).

**_NOTE_**: some ciphers don't support unaligned (`byteOffset % 4 !== 0`) Uint8Array as destination, since it will significantly
decrease performance and this optimization will become pointless.

```js
import { chacha20poly1305 } from '@noble/ciphers/chacha';
import { utf8ToBytes } from '@noble/ciphers/utils';
Expand Down Expand Up @@ -497,54 +502,54 @@ Benchmark results on Apple M2 with node v20:

```
encrypt (64B)
├─xsalsa20poly1305 x 485,672 ops/sec @ 2μs/op
├─chacha20poly1305 x 466,200 ops/sec @ 2μs/op
├─xchacha20poly1305 x 312,500 ops/sec @ 3μs/op
├─aes-256-gcm x 151,057 ops/sec @ 6μs/op
└─aes-256-gcm-siv x 124,984 ops/sec @ 8μs/op
├─xsalsa20poly1305 x 485,908 ops/sec @ 2μs/op
├─chacha20poly1305 x 419,639 ops/sec @ 2μs/op
├─xchacha20poly1305 x 335,232 ops/sec @ 2μs/op
├─aes-256-gcm x 143,595 ops/sec @ 6μs/op
└─aes-256-gcm-siv x 120,743 ops/sec @ 8μs/op
encrypt (1KB)
├─xsalsa20poly1305 x 146,477 ops/sec @ 6μs/op
├─chacha20poly1305 x 145,518 ops/sec @ 6μs/op
├─xchacha20poly1305 x 126,119 ops/sec @ 7μs/op
├─aes-256-gcm x 43,207 ops/sec @ 23μs/op
└─aes-256-gcm-siv x 39,363 ops/sec @ 25μs/op
├─xsalsa20poly1305 x 135,924 ops/sec @ 7μs/op
├─chacha20poly1305 x 134,843 ops/sec @ 7μs/op
├─xchacha20poly1305 x 124,626 ops/sec @ 8μs/op
├─aes-256-gcm x 39,588 ops/sec @ 25μs/op
└─aes-256-gcm-siv x 36,989 ops/sec @ 27μs/op
encrypt (8KB)
├─xsalsa20poly1305 x 23,773 ops/sec @ 42μs/op
├─chacha20poly1305 x 24,134 ops/sec @ 41μs/op
├─xchacha20poly1305 x 23,520 ops/sec @ 42μs/op
├─aes-256-gcm x 8,420 ops/sec @ 118μs/op
└─aes-256-gcm-siv x 8,126 ops/sec @ 123μs/op
├─xsalsa20poly1305 x 22,178 ops/sec @ 45μs/op
├─chacha20poly1305 x 22,691 ops/sec @ 44μs/op
├─xchacha20poly1305 x 22,463 ops/sec @ 44μs/op
├─aes-256-gcm x 8,082 ops/sec @ 123μs/op
└─aes-256-gcm-siv x 2,376 ops/sec @ 420μs/op
encrypt (1MB)
├─xsalsa20poly1305 x 195 ops/sec @ 5ms/op
├─chacha20poly1305 x 199 ops/sec @ 5ms/op
├─xchacha20poly1305 x 198 ops/sec @ 5ms/op
├─aes-256-gcm x 76 ops/sec @ 13ms/op
└─aes-256-gcm-siv x 78 ops/sec @ 12ms/op
├─xsalsa20poly1305 x 171 ops/sec @ 5ms/op
├─chacha20poly1305 x 186 ops/sec @ 5ms/op
├─xchacha20poly1305 x 189 ops/sec @ 5ms/op
├─aes-256-gcm x 73 ops/sec @ 13ms/op
└─aes-256-gcm-siv x 77 ops/sec @ 12ms/op
```

Unauthenticated encryption:

```
encrypt (64B)
├─salsa x 1,287,001 ops/sec @ 777ns/op
├─chacha x 1,555,209 ops/sec @ 643ns/op
├─xsalsa x 938,086 ops/sec @ 1μs/op
└─xchacha x 920,810 ops/sec @ 1μs/op
├─salsa x 1,245,330 ops/sec @ 803ns/op
├─chacha x 1,468,428 ops/sec @ 681ns/op
├─xsalsa x 995,024 ops/sec @ 1μs/op
└─xchacha x 1,026,694 ops/sec @ 974ns/op
encrypt (1KB)
├─salsa x 353,107 ops/sec @ 2μs/op
├─chacha x 377,216 ops/sec @ 2μs/op
├─xsalsa x 331,674 ops/sec @ 3μs/op
└─xchacha x 336,247 ops/sec @ 2μs/op
├─salsa x 349,283 ops/sec @ 2μs/op
├─chacha x 369,822 ops/sec @ 2μs/op
├─xsalsa x 326,370 ops/sec @ 3μs/op
└─xchacha x 334,001 ops/sec @ 2μs/op
encrypt (8KB)
├─salsa x 57,084 ops/sec @ 17μs/op
├─chacha x 59,520 ops/sec @ 16μs/op
├─xsalsa x 57,097 ops/sec @ 17μs/op
└─xchacha x 58,278 ops/sec @ 17μs/op
├─salsa x 55,050 ops/sec @ 18μs/op
├─chacha x 56,474 ops/sec @ 17μs/op
├─xsalsa x 54,068 ops/sec @ 18μs/op
└─xchacha x 55,469 ops/sec @ 18μs/op
encrypt (1MB)
├─salsa x 479 ops/sec @ 2ms/op
├─chacha x 491 ops/sec @ 2ms/op
├─xsalsa x 483 ops/sec @ 2ms/op
└─xchacha x 492 ops/sec @ 2ms/op
├─salsa x 449 ops/sec @ 2ms/op
├─chacha x 459 ops/sec @ 2ms/op
├─xsalsa x 448 ops/sec @ 2ms/op
└─xchacha x 459 ops/sec @ 2ms/op
AES
encrypt (64B)
Expand Down
52 changes: 20 additions & 32 deletions benchmark/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"dependencies": {
"@chainsafe/as-chacha20poly1305": "0.1.0",
"@devtomio/sodium": "0.3.0",
"@noble/ciphers": "file:..",
"@noble/ciphers": "file:noble-ciphers-0.6.0.tgz",
"@stablelib/aes": "^1.0.1",
"@stablelib/chacha": "1.0.1",
"@stablelib/chacha20poly1305": "1.0.1",
Expand Down
7 changes: 4 additions & 3 deletions build/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion esm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"node:crypto": false
},
"node": {
"./crypto.js": "./esm/cryptoNode.js",
"./crypto": "./esm/cryptoNode.js"
}
}
37 changes: 13 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@noble/ciphers",
"version": "0.5.3",
"version": "0.6.0",
"description": "Auditable & minimal JS implementation of Salsa20, ChaCha and AES",
"files": [
"esm",
Expand Down Expand Up @@ -28,7 +28,6 @@
"url": "git+https://github.com/paulmillr/noble-ciphers.git"
},
"license": "MIT",
"sideEffects": false,
"devDependencies": {
"@paulmillr/jsbt": "0.1.0",
"@scure/base": "1.1.3",
Expand All @@ -41,24 +40,20 @@
"main": "index.js",
"exports": {
".": {
"types": "./index.d.ts",
"import": "./esm/index.js",
"default": "./index.js"
"require": "./index.js"
},
"./_micro": {
"types": "./_micro.d.ts",
"import": "./esm/_micro.js",
"default": "./_micro.js"
"require": "./_micro.js"
},
"./_poly1305": {
"types": "./_poly1305.d.ts",
"import": "./esm/_poly1305.js",
"default": "./_poly1305.js"
"require": "./_poly1305.js"
},
"./_polyval": {
"types": "./_polyval.d.ts",
"import": "./esm/_polyval.js",
"default": "./_polyval.js"
"require": "./_polyval.js"
},
"./crypto": {
"types": "./crypto.d.ts",
Expand All @@ -70,41 +65,35 @@
"default": "./crypto.js"
},
"./aes": {
"types": "./aes.d.ts",
"import": "./esm/aes.js",
"default": "./aes.js"
"require": "./aes.js"
},
"./chacha": {
"types": "./chacha.d.ts",
"import": "./esm/chacha.js",
"default": "./chacha.js"
"require": "./chacha.js"
},
"./salsa": {
"types": "./salsa.d.ts",
"import": "./esm/salsa.js",
"default": "./salsa.js"
"require": "./salsa.js"
},
"./ff1": {
"types": "./ff1.d.ts",
"import": "./esm/ff1.js",
"default": "./ff1.js"
"require": "./ff1.js"
},
"./utils": {
"types": "./utils.d.ts",
"import": "./esm/utils.js",
"default": "./utils.js"
"require": "./utils.js"
},
"./index": {
"types": "./index.d.ts",
"import": "./esm/index.js",
"default": "./index.js"
"require": "./index.js"
},
"./webcrypto": {
"types": "./webcrypto.d.ts",
"import": "./esm/webcrypto.js",
"default": "./webcrypto.js"
"require": "./webcrypto.js"
}
},
"sideEffects": false,
"browser": {
"node:crypto": false,
"./crypto": "./crypto.js"
Expand Down
14 changes: 5 additions & 9 deletions src/_arx.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Basic utils for ARX (add-rotate-xor) salsa and chacha ciphers.
import { number as anumber, bytes as abytes, bool as abool } from './_assert.js';
import { XorStream, checkOpts, u32 } from './utils.js';
import { XorStream, checkOpts, u32, copyBytes } from './utils.js';

/*
RFC8439 requires multi-step cipher stream, where
Expand Down Expand Up @@ -149,7 +149,7 @@ export function createCipher(core: CipherCoreFn, opts: CipherOpts): XorStream {
abytes(nonce);
abytes(data);
const len = data.length;
if (!output) output = new Uint8Array(len);
if (output === undefined) output = new Uint8Array(len);
abytes(output);
anumber(counter);
if (counter < 0 || counter >= MAX_COUNTER) throw new Error('arx: counter overflow');
Expand All @@ -164,8 +164,7 @@ export function createCipher(core: CipherCoreFn, opts: CipherOpts): XorStream {
k: Uint8Array,
sigma: Uint32Array;
if (l === 32) {
k = key.slice();
toClean.push(k);
toClean.push((k = copyBytes(key)));
sigma = sigma32_32;
} else if (l === 16 && allowShortKeys) {
k = new Uint8Array(32);
Expand All @@ -184,10 +183,7 @@ export function createCipher(core: CipherCoreFn, opts: CipherOpts): XorStream {
// xsalsa20: 24 (16 -> hsalsa, 8 -> old nonce)
// xchacha20: 24 (16 -> hchacha, 8 -> old nonce)
// Align nonce to 4 bytes
if (!isAligned32(nonce)) {
nonce = nonce.slice();
toClean.push(nonce);
}
if (!isAligned32(nonce)) toClean.push((nonce = copyBytes(nonce)));

const k32 = u32(k);
// hsalsa & hchacha: handle extended nonce
Expand All @@ -211,7 +207,7 @@ export function createCipher(core: CipherCoreFn, opts: CipherOpts): XorStream {
}
const n32 = u32(nonce);
runCipher(core, sigma, k32, n32, data, output, counter, rounds);
while (toClean.length > 0) toClean.pop()!.fill(0);
for (const i of toClean) i.fill(0);
return output;
};
}
Loading

0 comments on commit ea2f2d2

Please sign in to comment.