remove node_modules and .gitignore them
This commit is contained in:
-22
@@ -1,22 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
|
||||
Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the “Software”), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-424
@@ -1,424 +0,0 @@
|
||||
# noble-ciphers
|
||||
|
||||
Auditable & minimal JS implementation of Salsa20, ChaCha, Poly1305 & AES-SIV
|
||||
|
||||
- 🔒 Auditable
|
||||
- 🔻 Tree-shaking-friendly: use only what's necessary, other code won't be included
|
||||
- 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines
|
||||
- 🔍 Unique tests ensure correctness: property-based, cross-library and Wycheproof vectors
|
||||
- 💼 AES: GCM (Galois Counter Mode), SIV (Nonce Misuse-Resistant encryption)
|
||||
- 💃 Salsa20, ChaCha, XSalsa20, XChaCha, Poly1305, ChaCha8, ChaCha12
|
||||
- ✍️ FF1 format-preserving encryption
|
||||
- 🧂 Compatible with NaCl / libsodium secretbox
|
||||
- 🪶 Just 500 lines / 4KB gzipped for Salsa + ChaCha + Poly build
|
||||
|
||||
### This library belongs to _noble_ crypto
|
||||
|
||||
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
|
||||
|
||||
- No dependencies, protection against supply chain attacks
|
||||
- Auditable TypeScript / JS code
|
||||
- Supported on all major platforms
|
||||
- Releases are signed with PGP keys and built transparently with NPM provenance
|
||||
- Check out [homepage](https://paulmillr.com/noble/) & all libraries:
|
||||
[ciphers](https://github.com/paulmillr/noble-ciphers),
|
||||
[curves](https://github.com/paulmillr/noble-curves),
|
||||
[hashes](https://github.com/paulmillr/noble-hashes),
|
||||
4kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) /
|
||||
[ed25519](https://github.com/paulmillr/noble-ed25519)
|
||||
|
||||
## Usage
|
||||
|
||||
> npm install @noble/ciphers
|
||||
|
||||
We support all major platforms and runtimes.
|
||||
For [Deno](https://deno.land), ensure to use
|
||||
[npm specifier](https://deno.land/manual@v1.28.0/node/npm_specifiers).
|
||||
For React Native, you may need a
|
||||
[polyfill for crypto.getRandomValues](https://github.com/LinusU/react-native-get-random-values).
|
||||
If you don't like NPM, a standalone
|
||||
[noble-ciphers.js](https://github.com/paulmillr/noble-ciphers/releases) is also available.
|
||||
|
||||
```js
|
||||
// import * from '@noble/ciphers'; // Error: use sub-imports, to ensure small app size
|
||||
// Simple API: uses xchacha20poly1305 with random nonce. Abstracts complexity away.
|
||||
import { encrypt, decrypt, utf8ToBytes, randomKey } from '@noble/ciphers/simple';
|
||||
const key = randomKey();
|
||||
const plaintext = utf8ToBytes('hello'); // Library works over Uint8Array-s
|
||||
const ciphertext = encrypt(key, plaintext);
|
||||
const plaintext_ = decrypt(key, ciphertext);
|
||||
|
||||
// Simple AES API: uses aes_256_gcm with random nonce.
|
||||
import { aes_encrypt, aes_decrypt } from '@noble/ciphers/simple';
|
||||
const a_key = randomKey();
|
||||
const a_ciphertext = await aes_encrypt(a_key, plaintext);
|
||||
const a_plaintext = await aes_decrypt(a_key, a_ciphertext);
|
||||
```
|
||||
|
||||
For specific APIs, see [salsa](#salsa), [chacha](#chacha) and [aes](#aes)
|
||||
sections below. All available imports:
|
||||
|
||||
```js
|
||||
// AEADs
|
||||
import { xsalsa20poly1305 } from '@noble/ciphers/salsa'; // aka sodium secretbox
|
||||
import { chacha20poly1305, xchacha20poly1305 } from '@noble/ciphers/chacha';
|
||||
// Pure ciphers
|
||||
import { salsa20, xsalsa20 } from '@noble/ciphers/salsa';
|
||||
import { chacha20, xchacha20, chacha8, chacha12 } from '@noble/ciphers/chacha';
|
||||
// AES webcrypto shortcuts
|
||||
import {
|
||||
aes_128_gcm, aes_128_ctr, aes_128_cbc, aes_256_gcm, aes_256_ctr, aes_256_cbc
|
||||
} from '@noble/ciphers/webcrypto/aes';
|
||||
import { aes_256_gcm_siv } from '@noble/ciphers/webcrypto/siv'; // AES-GCM-SIV
|
||||
import { FF1, BinaryFF1 } from '@noble/ciphers/webcrypto/ff1'; // FF1
|
||||
import { randomBytes } from '@noble/ciphers/webcrypto/utils';
|
||||
import { bytesToHex, hexToBytes, bytesToUtf8, utf8ToBytes, concatBytes } from '@noble/ciphers/utils';
|
||||
import * as c from '@noble/ciphers/_micro'; // Everything, written in minimal, auditable way
|
||||
```
|
||||
|
||||
### How to encrypt properly
|
||||
|
||||
1. Use unpredictable key with enough entropy
|
||||
- Random key must be using cryptographically secure random number generator (CSPRNG), not `Math.random` etc.
|
||||
- Non-random key generated from KDF is fine
|
||||
- Re-using key is fine, but be aware of rules for cryptographic key wear-out and [encryption limits](#encryption-limits)
|
||||
2. Use new nonce every time and [don't repeat it](#nonces)
|
||||
- `simple` module manages nonces for you
|
||||
- chacha and salsa20 are fine for sequential counters that *never* repeat: `01, 02...`
|
||||
- xchacha and xsalsa20 should be used for random nonces instead
|
||||
3. Prefer authenticated encryption (AEAD)
|
||||
- chacha20poly1305 is good, chacha20 without poly1305 is bad
|
||||
- aes-gcm is good, aes-ctr / aes-cbc is bad
|
||||
- Flipping bits or even ciphertext substitution won't be detected in
|
||||
unauthenticated ciphers
|
||||
4. Don't re-use keys between different protocols
|
||||
- For example, using secp256k1 key in AES is bad
|
||||
- Use hkdf or, at least, a hash function to create sub-key instead
|
||||
|
||||
### Salsa
|
||||
|
||||
```js
|
||||
import { xsalsa20poly1305 } from '@noble/ciphers/salsa';
|
||||
import { utf8ToBytes } from '@noble/ciphers/utils';
|
||||
import { randomBytes } from '@noble/ciphers/webcrypto/utils';
|
||||
|
||||
const key = randomBytes(32);
|
||||
const data = utf8ToBytes('hello, noble'); // strings must be converted to Uint8Array
|
||||
|
||||
const nonce = randomBytes(24);
|
||||
const stream_x = xsalsa20poly1305(key, nonce); // === secretbox(key, nonce)
|
||||
const ciphertext = stream_x.encrypt(data); // === secretbox.seal(data)
|
||||
const plaintext = stream_x.decrypt(ciphertext); // === secretbox.open(ciphertext)
|
||||
|
||||
// Avoid memory allocations: re-use same uint8array
|
||||
stream_x.decrypt(ciphertext, ciphertext.subarray(-16));
|
||||
// ciphertext is now plaintext
|
||||
|
||||
// We provide sodium secretbox alias, which is just xsalsa20poly1305
|
||||
import { secretbox } from '@noble/ciphers/simple';
|
||||
const box = secretbox(key, nonce);
|
||||
const ciphertext = box.seal(plaintext);
|
||||
const plaintext = box.open(ciphertext);
|
||||
|
||||
// Standalone salsa is also available
|
||||
import { salsa20, xsalsa20 } from '@noble/ciphers/salsa';
|
||||
const nonce12 = randomBytes(12); // salsa uses 96-bit nonce, xsalsa uses 192-bit
|
||||
const encrypted_s = salsa20(key, nonce12, data);
|
||||
const encrypted_xs = xsalsa20(key, nonce, data);
|
||||
```
|
||||
|
||||
Salsa20 stream cipher ([website](https://cr.yp.to/snuffle.html),
|
||||
[PDF](https://cr.yp.to/snuffle/salsafamily-20071225.pdf),
|
||||
[wiki](https://en.wikipedia.org/wiki/Salsa20)) was released in 2005.
|
||||
Salsa's goal was to implement AES replacement that does not rely on S-Boxes,
|
||||
which are hard to implement in a constant-time manner.
|
||||
Salsa20 is usually faster than AES, a big deal on slow, budget mobile phones.
|
||||
|
||||
[XSalsa20](https://cr.yp.to/snuffle/xsalsa-20110204.pdf), extended-nonce
|
||||
variant was released in 2008. It switched nonces from 96-bit to 192-bit,
|
||||
and became safe to be picked at random.
|
||||
|
||||
Nacl / Libsodium popularized term "secretbox", a simple black-box
|
||||
authenticated encryption. Secretbox is just xsalsa20-poly1305. We provide the
|
||||
alias and corresponding seal / open methods.
|
||||
|
||||
### ChaCha
|
||||
|
||||
```js
|
||||
import { chacha20poly1305, xchacha20poly1305 } from '@noble/ciphers/chacha';
|
||||
import { utf8ToBytes } from '@noble/ciphers/utils';
|
||||
import { randomBytes } from '@noble/ciphers/webcrypto/utils';
|
||||
|
||||
const key = randomBytes(32);
|
||||
const data = utf8ToBytes('hello, noble'); // strings must be converted to Uint8Array
|
||||
|
||||
const nonce12 = randomBytes(12); // chacha uses 96-bit nonce
|
||||
const stream_c = chacha20poly1305(key, nonce12);
|
||||
const ciphertext_c = stream_c.encrypt(data);
|
||||
const plaintext_c = stream_c.decrypt(ciphertext_c); // === data
|
||||
|
||||
// Avoid memory allocations: re-use same uint8array
|
||||
stream_c.decrypt(ciphertext_c, ciphertext_c.subarray(-16));
|
||||
// ciphertext_c is now plaintext_c
|
||||
|
||||
const nonce24 = randomBytes(24); // xchacha uses 192-bit nonce
|
||||
const stream_xc = xchacha20poly1305(key, nonce24);
|
||||
const ciphertext_xc = stream_xc.encrypt(data);
|
||||
const plaintext_xc = stream_xc.decrypt(ciphertext_xc); // === data
|
||||
|
||||
// Standalone chacha is also available
|
||||
import { chacha20, xchacha20, chacha8, chacha12 } from '@noble/ciphers/chacha';
|
||||
const ciphertext_pc = chacha20(key, nonce12, data);
|
||||
const ciphertext_pxc = xchacha20(key, nonce24, data);
|
||||
const ciphertext_8 = chacha8(key, nonce12, data);
|
||||
const ciphertext_12 = chacha12(key, nonce12, data);
|
||||
```
|
||||
|
||||
ChaCha20 stream cipher ([website](https://cr.yp.to/chacha.html),
|
||||
[PDF](http://cr.yp.to/chacha/chacha-20080128.pdf),
|
||||
[wiki](https://en.wikipedia.org/wiki/Salsa20),
|
||||
[blog post](https://loup-vaillant.fr/tutorials/chacha20-design)) was released
|
||||
in 2008. ChaCha aims to increase the diffusion per round, but had slightly less
|
||||
cryptanalysis. It was standardized in
|
||||
[RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439) and is now used in TLS 1.3.
|
||||
|
||||
XChaCha20 ([draft RFC](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha))
|
||||
extended-nonce variant is also provided. Similar to XSalsa, it's safe to use with
|
||||
randomly-generated nonces.
|
||||
|
||||
### Poly1305
|
||||
|
||||
Poly1305 ([website](https://cr.yp.to/mac.html),
|
||||
[PDF](https://cr.yp.to/mac/poly1305-20050329.pdf),
|
||||
[wiki](https://en.wikipedia.org/wiki/Poly1305),
|
||||
[blog post](https://loup-vaillant.fr/tutorials/poly1305-design))
|
||||
is a fast and parallel secret-key message-authentication code suitable for
|
||||
a wide variety of applications. It was standardized in
|
||||
[RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439) and is now used in TLS 1.3.
|
||||
|
||||
Poly1305 is polynomial-evaluation MAC, which is not perfect for every situation:
|
||||
just like GCM, it lacks Random Key Robustness: the tags can be forged, and can't
|
||||
be used in PAKE schemes. See
|
||||
[invisible salamanders attack](https://keymaterial.net/2020/09/07/invisible-salamanders-in-aes-gcm-siv/).
|
||||
To combat invisible salamanders, `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.
|
||||
|
||||
Even though poly1305 can be imported separately from the library, we suggest
|
||||
using chacha-poly or xsalsa-poly.
|
||||
|
||||
### AES
|
||||
|
||||
```js
|
||||
import {
|
||||
aes_128_gcm, aes_128_ctr, aes_128_cbc,
|
||||
aes_256_gcm, aes_256_ctr, aes_256_cbc
|
||||
} from '@noble/ciphers/webcrypto/aes';
|
||||
|
||||
for (let cipher of [aes_256_gcm, aes_256_ctr, aes_256_cbc]) {
|
||||
const stream_new = cipher(key, nonce);
|
||||
const ciphertext_new = await stream_new.encrypt(plaintext);
|
||||
const plaintext_new = await stream_new.decrypt(ciphertext);
|
||||
}
|
||||
|
||||
import { aes_256_gcm_siv } from '@noble/ciphers/webcrypto/siv';
|
||||
const stream_siv = aes_256_gcm_siv(key, nonce)
|
||||
await stream_siv.encrypt(plaintext, AAD);
|
||||
```
|
||||
|
||||
AES ([wiki](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard))
|
||||
is a variant of Rijndael block cipher, standardized by NIST.
|
||||
|
||||
We don't implement AES in pure JS for now: instead, we wrap WebCrypto built-in
|
||||
and provide an improved, simple API. There is a simple reason for this:
|
||||
webcrypto API is terrible: different block modes require different params.
|
||||
|
||||
Optional [AES-GCM-SIV](https://en.wikipedia.org/wiki/AES-GCM-SIV)
|
||||
(synthetic initialization vector) nonce-misuse-resistant mode is also provided.
|
||||
|
||||
##### How AES works
|
||||
|
||||
`cipher = encrypt(block, key)`. Data is split into 128-bit blocks. Encrypted in 10/12/14 rounds (128/192/256bit). Every round does:
|
||||
|
||||
1. **S-box**, table substitution
|
||||
2. **Shift rows**, cyclic shift left of all rows of data array
|
||||
3. **Mix columns**, multiplying every column by fixed polynomial
|
||||
4. **Add round key**, round_key xor i-th column of array
|
||||
|
||||
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.
|
||||
|
||||
##### Block modes
|
||||
|
||||
We only expose GCM & SIV for now.
|
||||
|
||||
- ECB — simple deterministic replacement. Dangerous: always map x to y. See [AES Penguin](https://words.filippo.io/the-ecb-penguin/)
|
||||
- CBC — key is previous round’s block. Hard to use: need proper padding, also needs MAC
|
||||
- CTR — counter, allows to create streaming cipher. Requires good IV. Parallelizable. OK, but no MAC
|
||||
- GCM — modern CTR, parallel, with MAC. Not ideal:
|
||||
- Conservative key wear-out is `2**32` (4B) msgs
|
||||
- MAC can be forged: see Poly1305 section above
|
||||
- SIV — synthetic initialization vector, nonce-misuse-resistant
|
||||
- Can be 1.5-2x slower than GCM by itself
|
||||
- nonce misuse-resistant schemes guarantee that if a
|
||||
nonce repeats, then the only security loss is that identical
|
||||
plaintexts will produce identical ciphertexts
|
||||
- MAC can be forged: see Poly1305 section above
|
||||
- XTS — used in hard drives. Similar to ECB (deterministic), but has `[i][j]`
|
||||
tweak arguments corresponding to sector i and 16-byte block (part of sector) j. Not authenticated!
|
||||
|
||||
### FF1
|
||||
|
||||
Format-preserving encryption algorithm (FPE-FF1) specified in NIST Special Publication 800-38G.
|
||||
|
||||
More info: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf
|
||||
|
||||
## Security
|
||||
|
||||
The library is experimental. Use at your own risk.
|
||||
|
||||
### Nonces
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
### Encryption limits
|
||||
|
||||
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 [RFC draft](https://datatracker.ietf.org/doc/draft-irtf-cfrg-aead-limits/) for details.
|
||||
|
||||
- Max message size:
|
||||
- AES-GCM: ~68GB, `2**36-256`
|
||||
- Salsa, ChaCha, XSalsa, XChaCha: ~256GB, `2**38-64`
|
||||
- Max amount of protected messages, under same key:
|
||||
- AES-GCM: `2**32.5`
|
||||
- Salsa, ChaCha: `2**46`, but only integrity is affected, not confidentiality
|
||||
- XSalsa, XChaCha: `2**72`
|
||||
- Max amount of protected messages, across all keys:
|
||||
- AES-GCM: `2**69/B` where B is max blocks encrypted by a key. Meaning
|
||||
`2**59` for 1KB, `2**49` for 1MB, `2**39` for 1GB
|
||||
- Salsa, ChaCha, XSalsa, XChaCha: `2**100`
|
||||
|
||||
## Speed
|
||||
|
||||
To summarize, noble is the fastest JS implementation.
|
||||
|
||||
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 v20:
|
||||
|
||||
```
|
||||
encrypt (64B)
|
||||
├─xsalsa20poly1305 x 484,966 ops/sec @ 2μs/op
|
||||
├─chacha20poly1305 x 442,282 ops/sec @ 2μs/op
|
||||
└─xchacha20poly1305 x 300,842 ops/sec @ 3μs/op
|
||||
encrypt (1KB)
|
||||
├─xsalsa20poly1305 x 143,905 ops/sec @ 6μs/op
|
||||
├─chacha20poly1305 x 141,663 ops/sec @ 7μs/op
|
||||
└─xchacha20poly1305 x 122,639 ops/sec @ 8μs/op
|
||||
encrypt (8KB)
|
||||
├─xsalsa20poly1305 x 23,373 ops/sec @ 42μs/op
|
||||
├─chacha20poly1305 x 23,683 ops/sec @ 42μs/op
|
||||
└─xchacha20poly1305 x 23,066 ops/sec @ 43μs/op
|
||||
encrypt (1MB)
|
||||
├─xsalsa20poly1305 x 193 ops/sec @ 5ms/op
|
||||
├─chacha20poly1305 x 196 ops/sec @ 5ms/op
|
||||
└─xchacha20poly1305 x 195 ops/sec @ 5ms/op
|
||||
```
|
||||
|
||||
Unauthenticated encryption:
|
||||
|
||||
```
|
||||
encrypt (64B)
|
||||
├─salsa x 1,272,264 ops/sec @ 786ns/op
|
||||
├─chacha x 1,526,717 ops/sec @ 655ns/op
|
||||
├─xsalsa x 847,457 ops/sec @ 1μs/op
|
||||
└─xchacha x 848,896 ops/sec @ 1μs/op
|
||||
encrypt (1KB)
|
||||
├─salsa x 355,492 ops/sec @ 2μs/op
|
||||
├─chacha x 377,358 ops/sec @ 2μs/op
|
||||
├─xsalsa x 311,915 ops/sec @ 3μs/op
|
||||
└─xchacha x 315,457 ops/sec @ 3μs/op
|
||||
encrypt (8KB)
|
||||
├─salsa x 56,063 ops/sec @ 17μs/op
|
||||
├─chacha x 57,359 ops/sec @ 17μs/op
|
||||
├─xsalsa x 54,848 ops/sec @ 18μs/op
|
||||
└─xchacha x 55,475 ops/sec @ 18μs/op
|
||||
encrypt (1MB)
|
||||
├─salsa x 465 ops/sec @ 2ms/op
|
||||
├─chacha x 474 ops/sec @ 2ms/op
|
||||
├─xsalsa x 466 ops/sec @ 2ms/op
|
||||
└─xchacha x 476 ops/sec @ 2ms/op
|
||||
```
|
||||
|
||||
Compare to other implementations:
|
||||
|
||||
```
|
||||
xsalsa20poly1305 (encrypt, 1MB)
|
||||
├─tweetnacl x 108 ops/sec @ 9ms/op
|
||||
├─noble x 190 ops/sec @ 5ms/op
|
||||
└─micro x 21 ops/sec @ 47ms/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
|
||||
└─micro x 19 ops/sec @ 50ms/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
|
||||
└─micro x 61 ops/sec @ 16ms/op
|
||||
```
|
||||
|
||||
## Contributing & testing
|
||||
|
||||
1. Clone the repository
|
||||
2. `npm install` to install build dependencies like TypeScript
|
||||
3. `npm run build` to compile TypeScript code
|
||||
4. `npm run test` will execute all main tests
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2023 Paul Miller [(https://paulmillr.com)](https://paulmillr.com)
|
||||
Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
||||
|
||||
See LICENSE file.
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
declare function number(n: number): void;
|
||||
declare function bool(b: boolean): void;
|
||||
declare function bytes(b: Uint8Array | undefined, ...lengths: number[]): void;
|
||||
export type Hash = {
|
||||
(data: Uint8Array): Uint8Array;
|
||||
blockLen: number;
|
||||
outputLen: number;
|
||||
create: any;
|
||||
};
|
||||
declare function hash(hash: Hash): void;
|
||||
declare function exists(instance: any, checkFinished?: boolean): void;
|
||||
declare function output(out: any, instance: any): void;
|
||||
export { number, bool, bytes, hash, exists, output };
|
||||
declare const assert: {
|
||||
number: typeof number;
|
||||
bool: typeof bool;
|
||||
bytes: typeof bytes;
|
||||
hash: typeof hash;
|
||||
exists: typeof exists;
|
||||
output: typeof output;
|
||||
};
|
||||
export default assert;
|
||||
//# sourceMappingURL=_assert.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"_assert.d.ts","sourceRoot":"","sources":["src/_assert.ts"],"names":[],"mappings":"AAAA,iBAAS,MAAM,CAAC,CAAC,EAAE,MAAM,QAExB;AAED,iBAAS,IAAI,CAAC,CAAC,EAAE,OAAO,QAEvB;AAED,iBAAS,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,QAI7D;AAED,MAAM,MAAM,IAAI,GAAG;IACjB,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC;CACb,CAAC;AACF,iBAAS,IAAI,CAAC,IAAI,EAAE,IAAI,QAKvB;AAED,iBAAS,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,aAAa,UAAO,QAGlD;AACD,iBAAS,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,QAMtC;AAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACrD,QAAA,MAAM,MAAM;;;;;;;CAAgD,CAAC;AAC7D,eAAe,MAAM,CAAC"}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.output = exports.exists = exports.hash = exports.bytes = exports.bool = exports.number = void 0;
|
||||
function number(n) {
|
||||
if (!Number.isSafeInteger(n) || n < 0)
|
||||
throw new Error(`Wrong positive integer: ${n}`);
|
||||
}
|
||||
exports.number = number;
|
||||
function bool(b) {
|
||||
if (typeof b !== 'boolean')
|
||||
throw new Error(`Expected boolean, not ${b}`);
|
||||
}
|
||||
exports.bool = bool;
|
||||
function bytes(b, ...lengths) {
|
||||
if (!(b instanceof Uint8Array))
|
||||
throw new Error('Expected Uint8Array');
|
||||
if (lengths.length > 0 && !lengths.includes(b.length))
|
||||
throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`);
|
||||
}
|
||||
exports.bytes = bytes;
|
||||
function hash(hash) {
|
||||
if (typeof hash !== 'function' || typeof hash.create !== 'function')
|
||||
throw new Error('hash must be wrapped by utils.wrapConstructor');
|
||||
number(hash.outputLen);
|
||||
number(hash.blockLen);
|
||||
}
|
||||
exports.hash = hash;
|
||||
function exists(instance, checkFinished = true) {
|
||||
if (instance.destroyed)
|
||||
throw new Error('Hash instance has been destroyed');
|
||||
if (checkFinished && instance.finished)
|
||||
throw new Error('Hash#digest() has already been called');
|
||||
}
|
||||
exports.exists = exists;
|
||||
function output(out, instance) {
|
||||
bytes(out);
|
||||
const min = instance.outputLen;
|
||||
if (out.length < min) {
|
||||
throw new Error(`digestInto() expects output buffer of length at least ${min}`);
|
||||
}
|
||||
}
|
||||
exports.output = output;
|
||||
const assert = { number, bool, bytes, hash, exists, output };
|
||||
exports.default = assert;
|
||||
//# sourceMappingURL=_assert.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"_assert.js","sourceRoot":"","sources":["src/_assert.ts"],"names":[],"mappings":";;;AAAA,SAAS,MAAM,CAAC,CAAS;IACvB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,EAAE,CAAC,CAAC;AACzF,CAAC;AAqCQ,wBAAM;AAnCf,SAAS,IAAI,CAAC,CAAU;IACtB,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,EAAE,CAAC,CAAC;AAC5E,CAAC;AAiCgB,oBAAI;AA/BrB,SAAS,KAAK,CAAC,CAAyB,EAAE,GAAG,OAAiB;IAC5D,IAAI,CAAC,CAAC,CAAC,YAAY,UAAU,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACvE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,iCAAiC,OAAO,mBAAmB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAC3F,CAAC;AA2BsB,sBAAK;AAnB5B,SAAS,IAAI,CAAC,IAAU;IACtB,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU;QACjE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxB,CAAC;AAc6B,oBAAI;AAZlC,SAAS,MAAM,CAAC,QAAa,EAAE,aAAa,GAAG,IAAI;IACjD,IAAI,QAAQ,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC5E,IAAI,aAAa,IAAI,QAAQ,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;AACnG,CAAC;AASmC,wBAAM;AAR1C,SAAS,MAAM,CAAC,GAAQ,EAAE,QAAa;IACrC,KAAK,CAAC,GAAG,CAAC,CAAC;IACX,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC;IAC/B,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;QACpB,MAAM,IAAI,KAAK,CAAC,yDAAyD,GAAG,EAAE,CAAC,CAAC;KACjF;AACH,CAAC;AAE2C,wBAAM;AAClD,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC7D,kBAAe,MAAM,CAAC"}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
||||
import * as u from './utils.js';
|
||||
export declare function hsalsa(c: Uint32Array, key: Uint8Array, nonce: Uint8Array): Uint8Array;
|
||||
export declare function hchacha(c: Uint32Array, key: Uint8Array, nonce: Uint8Array): Uint8Array;
|
||||
/**
|
||||
* salsa20, 12-byte nonce.
|
||||
*/
|
||||
export declare const salsa20: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* xsalsa20, 24-byte nonce.
|
||||
*/
|
||||
export declare const xsalsa20: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* chacha20 non-RFC, original version by djb. 8-byte nonce, 8-byte counter.
|
||||
*/
|
||||
export declare const chacha20orig: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* chacha20 RFC 8439 (IETF / TLS). 12-byte nonce, 4-byte counter.
|
||||
*/
|
||||
export declare const chacha20: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* xchacha20 eXtended-nonce. https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
*/
|
||||
export declare const xchacha20: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* 8-round chacha from the original paper.
|
||||
*/
|
||||
export declare const chacha8: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* 12-round chacha from the original paper.
|
||||
*/
|
||||
export declare const chacha12: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
export declare function poly1305(msg: Uint8Array, key: Uint8Array): Uint8Array;
|
||||
/**
|
||||
* xsalsa20-poly1305 eXtended-nonce (24 bytes) salsa.
|
||||
*/
|
||||
export declare function xsalsa20poly1305(key: Uint8Array, nonce: Uint8Array): {
|
||||
encrypt: (plaintext: Uint8Array) => Uint8Array;
|
||||
decrypt: (ciphertext: Uint8Array) => Uint8Array;
|
||||
};
|
||||
/**
|
||||
* Alias to xsalsa20-poly1305
|
||||
*/
|
||||
export declare function secretbox(key: Uint8Array, nonce: Uint8Array): {
|
||||
seal: (plaintext: Uint8Array) => Uint8Array;
|
||||
open: (ciphertext: Uint8Array) => Uint8Array;
|
||||
};
|
||||
export declare const _poly1305_aead: (fn: typeof chacha20) => (key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => u.Cipher;
|
||||
/**
|
||||
* chacha20-poly1305 12-byte-nonce chacha.
|
||||
*/
|
||||
export declare const chacha20poly1305: (key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => u.Cipher;
|
||||
/**
|
||||
* xchacha20-poly1305 eXtended-nonce (24 bytes) chacha.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
export declare const xchacha20poly1305: (key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => u.Cipher;
|
||||
//# sourceMappingURL=_micro.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"_micro.d.ts","sourceRoot":"","sources":["src/_micro.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAMvE,OAAO,KAAK,CAAC,MAAM,YAAY,CAAC;AA8EhC,wBAAgB,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,UAAU,CAYrF;AAsBD,wBAAgB,OAAO,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,UAAU,CAYtF;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,yHAAsD,CAAC;AAE3E;;GAEG;AACH,eAAO,MAAM,QAAQ,yHAKnB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,YAAY,yHAAuE,CAAC;AACjG;;GAEG;AACH,eAAO,MAAM,QAAQ,yHAKnB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,yHAMpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,OAAO,yHAKlB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,yHAKnB,CAAC;AAKH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAAG,UAAU,CAcrE;AA4BD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU;yBAI1C,UAAU;0BAST,UAAU;EAUnC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU;;;EAK3D;AAED,eAAO,MAAM,cAAc,OACpB,eAAe,WACd,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,EAAE,MAwBzD,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,gBAAgB,QA7BrB,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,EAAE,MA6BJ,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAnCtB,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,EAAE,MAmCF,CAAC"}
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
"use strict";
|
||||
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.xchacha20poly1305 = exports.chacha20poly1305 = exports._poly1305_aead = exports.secretbox = exports.xsalsa20poly1305 = exports.poly1305 = exports.chacha12 = exports.chacha8 = exports.xchacha20 = exports.chacha20 = exports.chacha20orig = exports.xsalsa20 = exports.salsa20 = exports.hchacha = exports.hsalsa = void 0;
|
||||
// micro-noble-ciphers: more auditable, but slower version of salsa20, chacha & poly1305.
|
||||
// Implements the same algorithms that are present in other files,
|
||||
// but without unrolled loops (https://en.wikipedia.org/wiki/Loop_unrolling).
|
||||
const u = require("./utils.js");
|
||||
const _salsa_js_1 = require("./_salsa.js");
|
||||
// Utils
|
||||
function hexToNumber(hex) {
|
||||
if (typeof hex !== 'string')
|
||||
throw new Error('hex string expected, got ' + typeof hex);
|
||||
// Big Endian
|
||||
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
||||
}
|
||||
function bytesToNumberLE(bytes) {
|
||||
return hexToNumber(u.bytesToHex(Uint8Array.from(bytes).reverse()));
|
||||
}
|
||||
function numberToBytesLE(n, len) {
|
||||
return u.hexToBytes(n.toString(16).padStart(len * 2, '0')).reverse();
|
||||
}
|
||||
const rotl = (a, b) => (a << b) | (a >>> (32 - b));
|
||||
// /Utils
|
||||
function salsaQR(x, a, b, c, d) {
|
||||
x[b] ^= rotl((x[a] + x[d]) | 0, 7);
|
||||
x[c] ^= rotl((x[b] + x[a]) | 0, 9);
|
||||
x[d] ^= rotl((x[c] + x[b]) | 0, 13);
|
||||
x[a] ^= rotl((x[d] + x[c]) | 0, 18);
|
||||
}
|
||||
// prettier-ignore
|
||||
function chachaQR(x, a, b, c, d) {
|
||||
x[a] = (x[a] + x[b]) | 0;
|
||||
x[d] = rotl(x[d] ^ x[a], 16);
|
||||
x[c] = (x[c] + x[d]) | 0;
|
||||
x[b] = rotl(x[b] ^ x[c], 12);
|
||||
x[a] = (x[a] + x[b]) | 0;
|
||||
x[d] = rotl(x[d] ^ x[a], 8);
|
||||
x[c] = (x[c] + x[d]) | 0;
|
||||
x[b] = rotl(x[b] ^ x[c], 7);
|
||||
}
|
||||
function salsaRound(x, rounds = 20) {
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
salsaQR(x, 0, 4, 8, 12);
|
||||
salsaQR(x, 5, 9, 13, 1);
|
||||
salsaQR(x, 10, 14, 2, 6);
|
||||
salsaQR(x, 15, 3, 7, 11);
|
||||
salsaQR(x, 0, 1, 2, 3);
|
||||
salsaQR(x, 5, 6, 7, 4);
|
||||
salsaQR(x, 10, 11, 8, 9);
|
||||
salsaQR(x, 15, 12, 13, 14);
|
||||
}
|
||||
}
|
||||
function chachaRound(x, rounds = 20) {
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
chachaQR(x, 0, 4, 8, 12);
|
||||
chachaQR(x, 1, 5, 9, 13);
|
||||
chachaQR(x, 2, 6, 10, 14);
|
||||
chachaQR(x, 3, 7, 11, 15);
|
||||
chachaQR(x, 0, 5, 10, 15);
|
||||
chachaQR(x, 1, 6, 11, 12);
|
||||
chachaQR(x, 2, 7, 8, 13);
|
||||
chachaQR(x, 3, 4, 9, 14);
|
||||
}
|
||||
}
|
||||
function salsaCore(c, k, n, out, cnt, rounds = 20) {
|
||||
// prettier-ignore
|
||||
const y = new Uint32Array([
|
||||
c[0], k[0], k[1], k[2],
|
||||
k[3], c[1], n[0], n[1],
|
||||
cnt, 0, c[2], k[4],
|
||||
k[5], k[6], k[7], c[3], // Key Key Key "te k"
|
||||
]);
|
||||
const x = y.slice();
|
||||
salsaRound(x, rounds);
|
||||
for (let i = 0; i < 16; i++)
|
||||
out[i] = (y[i] + x[i]) | 0;
|
||||
}
|
||||
function hsalsa(c, key, nonce) {
|
||||
const k = u.u32(key);
|
||||
const i = u.u32(nonce);
|
||||
// prettier-ignore
|
||||
const x = new Uint32Array([
|
||||
c[0], k[0], k[1], k[2],
|
||||
k[3], c[1], i[0], i[1],
|
||||
i[2], i[3], c[2], k[4],
|
||||
k[5], k[6], k[7], c[3]
|
||||
]);
|
||||
salsaRound(x);
|
||||
return u.u8(new Uint32Array([x[0], x[5], x[10], x[15], x[6], x[7], x[8], x[9]]));
|
||||
}
|
||||
exports.hsalsa = hsalsa;
|
||||
function chachaCore(c, k, n, out, cnt, rounds = 20) {
|
||||
// prettier-ignore
|
||||
const y = new Uint32Array([
|
||||
c[0], c[1], c[2], c[3],
|
||||
k[0], k[1], k[2], k[3],
|
||||
k[4], k[5], k[6], k[7],
|
||||
cnt, n[0], n[1], n[2], // Counter Counter Nonce Nonce
|
||||
]);
|
||||
const x = y.slice();
|
||||
chachaRound(x, rounds);
|
||||
for (let i = 0; i < 16; i++)
|
||||
out[i] = (y[i] + x[i]) | 0;
|
||||
}
|
||||
function hchacha(c, key, nonce) {
|
||||
const k = u.u32(key);
|
||||
const i = u.u32(nonce);
|
||||
// prettier-ignore
|
||||
const x = new Uint32Array([
|
||||
c[0], c[1], c[2], c[3],
|
||||
k[0], k[1], k[2], k[3],
|
||||
k[4], k[5], k[6], k[7],
|
||||
i[0], i[1], i[2], i[3],
|
||||
]);
|
||||
chachaRound(x);
|
||||
return u.u8(new Uint32Array([x[0], x[1], x[2], x[3], x[12], x[13], x[14], x[15]]));
|
||||
}
|
||||
exports.hchacha = hchacha;
|
||||
/**
|
||||
* salsa20, 12-byte nonce.
|
||||
*/
|
||||
exports.salsa20 = (0, _salsa_js_1.salsaBasic)({ core: salsaCore, counterRight: true });
|
||||
/**
|
||||
* xsalsa20, 24-byte nonce.
|
||||
*/
|
||||
exports.xsalsa20 = (0, _salsa_js_1.salsaBasic)({
|
||||
core: salsaCore,
|
||||
counterRight: true,
|
||||
extendNonceFn: hsalsa,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* chacha20 non-RFC, original version by djb. 8-byte nonce, 8-byte counter.
|
||||
*/
|
||||
exports.chacha20orig = (0, _salsa_js_1.salsaBasic)({ core: chachaCore, counterRight: false, counterLen: 8 });
|
||||
/**
|
||||
* chacha20 RFC 8439 (IETF / TLS). 12-byte nonce, 4-byte counter.
|
||||
*/
|
||||
exports.chacha20 = (0, _salsa_js_1.salsaBasic)({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* xchacha20 eXtended-nonce. https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
*/
|
||||
exports.xchacha20 = (0, _salsa_js_1.salsaBasic)({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 8,
|
||||
extendNonceFn: hchacha,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* 8-round chacha from the original paper.
|
||||
*/
|
||||
exports.chacha8 = (0, _salsa_js_1.salsaBasic)({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 8,
|
||||
});
|
||||
/**
|
||||
* 12-round chacha from the original paper.
|
||||
*/
|
||||
exports.chacha12 = (0, _salsa_js_1.salsaBasic)({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 12,
|
||||
});
|
||||
const POW_2_130_5 = 2n ** 130n - 5n;
|
||||
const POW_2_128_1 = 2n ** (16n * 8n) - 1n;
|
||||
// Can be speed-up using BigUint64Array, but would be more complicated
|
||||
function poly1305(msg, key) {
|
||||
u.ensureBytes(msg);
|
||||
u.ensureBytes(key);
|
||||
let acc = 0n;
|
||||
const r = bytesToNumberLE(key.subarray(0, 16)) & 0x0ffffffc0ffffffc0ffffffc0fffffffn;
|
||||
const s = bytesToNumberLE(key.subarray(16));
|
||||
// Process by 16 byte chunks
|
||||
for (let i = 0; i < msg.length; i += 16) {
|
||||
const m = msg.subarray(i, i + 16);
|
||||
const n = bytesToNumberLE(m) | (1n << BigInt(8 * m.length));
|
||||
acc = ((acc + n) * r) % POW_2_130_5;
|
||||
}
|
||||
const res = (acc + s) & POW_2_128_1;
|
||||
return numberToBytesLE(res, 16);
|
||||
}
|
||||
exports.poly1305 = poly1305;
|
||||
function computeTag(fn, key, nonce, ciphertext, AAD) {
|
||||
const res = [];
|
||||
if (AAD) {
|
||||
res.push(AAD);
|
||||
const leftover = AAD.length % 16;
|
||||
if (leftover > 0)
|
||||
res.push(new Uint8Array(16 - leftover));
|
||||
}
|
||||
res.push(ciphertext);
|
||||
const leftover = ciphertext.length % 16;
|
||||
if (leftover > 0)
|
||||
res.push(new Uint8Array(16 - leftover));
|
||||
// Lengths
|
||||
const num = new Uint8Array(16);
|
||||
const view = u.createView(num);
|
||||
u.setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
|
||||
u.setBigUint64(view, 8, BigInt(ciphertext.length), true);
|
||||
res.push(num);
|
||||
const authKey = fn(key, nonce, new Uint8Array(32));
|
||||
return poly1305(u.concatBytes(...res), authKey);
|
||||
}
|
||||
/**
|
||||
* xsalsa20-poly1305 eXtended-nonce (24 bytes) salsa.
|
||||
*/
|
||||
function xsalsa20poly1305(key, nonce) {
|
||||
u.ensureBytes(key);
|
||||
u.ensureBytes(nonce);
|
||||
return {
|
||||
encrypt: (plaintext) => {
|
||||
u.ensureBytes(plaintext);
|
||||
const m = u.concatBytes(new Uint8Array(32), plaintext);
|
||||
const c = (0, exports.xsalsa20)(key, nonce, m);
|
||||
const authKey = c.subarray(0, 32);
|
||||
const data = c.subarray(32);
|
||||
const tag = poly1305(data, authKey);
|
||||
return u.concatBytes(tag, data);
|
||||
},
|
||||
decrypt: (ciphertext) => {
|
||||
u.ensureBytes(ciphertext);
|
||||
if (ciphertext.length < 16)
|
||||
throw new Error('encrypted data must be at least 16 bytes');
|
||||
const c = u.concatBytes(new Uint8Array(16), ciphertext);
|
||||
const authKey = (0, exports.xsalsa20)(key, nonce, new Uint8Array(32));
|
||||
const tag = poly1305(c.subarray(32), authKey);
|
||||
if (!u.equalBytes(c.subarray(16, 32), tag))
|
||||
throw new Error('invalid poly1305 tag');
|
||||
return (0, exports.xsalsa20)(key, nonce, c).subarray(32);
|
||||
},
|
||||
};
|
||||
}
|
||||
exports.xsalsa20poly1305 = xsalsa20poly1305;
|
||||
/**
|
||||
* Alias to xsalsa20-poly1305
|
||||
*/
|
||||
function secretbox(key, nonce) {
|
||||
u.ensureBytes(key);
|
||||
u.ensureBytes(nonce);
|
||||
const xs = xsalsa20poly1305(key, nonce);
|
||||
return { seal: xs.encrypt, open: xs.decrypt };
|
||||
}
|
||||
exports.secretbox = secretbox;
|
||||
const _poly1305_aead = (fn) => (key, nonce, AAD) => {
|
||||
const tagLength = 16;
|
||||
const keyLength = 32;
|
||||
u.ensureBytes(key, keyLength);
|
||||
u.ensureBytes(nonce);
|
||||
return {
|
||||
tagLength,
|
||||
encrypt: (plaintext) => {
|
||||
u.ensureBytes(plaintext);
|
||||
const res = fn(key, nonce, plaintext, undefined, 1);
|
||||
const tag = computeTag(fn, key, nonce, res, AAD);
|
||||
return u.concatBytes(res, tag);
|
||||
},
|
||||
decrypt: (ciphertext) => {
|
||||
u.ensureBytes(ciphertext);
|
||||
if (ciphertext.length < tagLength)
|
||||
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
|
||||
const passedTag = ciphertext.subarray(-tagLength);
|
||||
const data = ciphertext.subarray(0, -tagLength);
|
||||
const tag = computeTag(fn, key, nonce, data, AAD);
|
||||
if (!u.equalBytes(passedTag, tag))
|
||||
throw new Error('invalid poly1305 tag');
|
||||
return fn(key, nonce, data, undefined, 1);
|
||||
},
|
||||
};
|
||||
};
|
||||
exports._poly1305_aead = _poly1305_aead;
|
||||
/**
|
||||
* chacha20-poly1305 12-byte-nonce chacha.
|
||||
*/
|
||||
exports.chacha20poly1305 = (0, exports._poly1305_aead)(exports.chacha20);
|
||||
/**
|
||||
* xchacha20-poly1305 eXtended-nonce (24 bytes) chacha.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
exports.xchacha20poly1305 = (0, exports._poly1305_aead)(exports.xchacha20);
|
||||
//# sourceMappingURL=_micro.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-15
@@ -1,15 +0,0 @@
|
||||
import { Input, Hash } from './utils.js';
|
||||
export type CHash = ReturnType<typeof wrapConstructorWithKey>;
|
||||
export declare function wrapConstructorWithKey<H extends Hash<H>>(hashCons: (key: Input) => Hash<H>): {
|
||||
(msg: Input, key: Input): Uint8Array;
|
||||
outputLen: number;
|
||||
blockLen: number;
|
||||
create(key: Input): Hash<H>;
|
||||
};
|
||||
export declare const poly1305: {
|
||||
(msg: Input, key: Input): Uint8Array;
|
||||
outputLen: number;
|
||||
blockLen: number;
|
||||
create(key: Input): Hash<Hash<unknown>>;
|
||||
};
|
||||
//# sourceMappingURL=_poly1305.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"_poly1305.d.ts","sourceRoot":"","sources":["src/_poly1305.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,EAAe,IAAI,EAAE,MAAM,YAAY,CAAC;AAmR/D,MAAM,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC9D,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC;UACrE,KAAK,OAAO,KAAK,GAAG,UAAU;;;gBAI7B,KAAK;EAE3B;AAED,eAAO,MAAM,QAAQ;UARC,KAAK,OAAO,KAAK,GAAG,UAAU;;;gBAI7B,KAAK;CAI8C,CAAC"}
|
||||
-268
@@ -1,268 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.poly1305 = exports.wrapConstructorWithKey = void 0;
|
||||
const utils_js_1 = require("./utils.js");
|
||||
const _assert_js_1 = require("./_assert.js");
|
||||
// Poly1305 is a fast and parallel secret-key message-authentication code.
|
||||
// https://cr.yp.to/mac.html, https://cr.yp.to/mac/poly1305-20050329.pdf
|
||||
// https://datatracker.ietf.org/doc/html/rfc8439
|
||||
// Based on Public Domain poly1305-donna https://github.com/floodyberry/poly1305-donna
|
||||
const u8to16 = (a, i) => (a[i++] & 0xff) | ((a[i++] & 0xff) << 8);
|
||||
class Poly1305 {
|
||||
constructor(key) {
|
||||
this.blockLen = 16;
|
||||
this.outputLen = 16;
|
||||
this.buffer = new Uint8Array(16);
|
||||
this.r = new Uint16Array(10);
|
||||
this.h = new Uint16Array(10);
|
||||
this.pad = new Uint16Array(8);
|
||||
this.pos = 0;
|
||||
this.finished = false;
|
||||
key = (0, utils_js_1.toBytes)(key);
|
||||
(0, utils_js_1.ensureBytes)(key, 32);
|
||||
const t0 = u8to16(key, 0);
|
||||
const t1 = u8to16(key, 2);
|
||||
const t2 = u8to16(key, 4);
|
||||
const t3 = u8to16(key, 6);
|
||||
const t4 = u8to16(key, 8);
|
||||
const t5 = u8to16(key, 10);
|
||||
const t6 = u8to16(key, 12);
|
||||
const t7 = u8to16(key, 14);
|
||||
// https://github.com/floodyberry/poly1305-donna/blob/e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781/poly1305-donna-16.h#L47
|
||||
this.r[0] = t0 & 0x1fff;
|
||||
this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
|
||||
this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
|
||||
this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
|
||||
this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
|
||||
this.r[5] = (t4 >>> 1) & 0x1ffe;
|
||||
this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
|
||||
this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
|
||||
this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
|
||||
this.r[9] = (t7 >>> 5) & 0x007f;
|
||||
for (let i = 0; i < 8; i++)
|
||||
this.pad[i] = u8to16(key, 16 + 2 * i);
|
||||
}
|
||||
process(data, offset, isLast = false) {
|
||||
const hibit = isLast ? 0 : 1 << 11;
|
||||
const { h, r } = this;
|
||||
const r0 = r[0];
|
||||
const r1 = r[1];
|
||||
const r2 = r[2];
|
||||
const r3 = r[3];
|
||||
const r4 = r[4];
|
||||
const r5 = r[5];
|
||||
const r6 = r[6];
|
||||
const r7 = r[7];
|
||||
const r8 = r[8];
|
||||
const r9 = r[9];
|
||||
const t0 = u8to16(data, offset + 0);
|
||||
const t1 = u8to16(data, offset + 2);
|
||||
const t2 = u8to16(data, offset + 4);
|
||||
const t3 = u8to16(data, offset + 6);
|
||||
const t4 = u8to16(data, offset + 8);
|
||||
const t5 = u8to16(data, offset + 10);
|
||||
const t6 = u8to16(data, offset + 12);
|
||||
const t7 = u8to16(data, offset + 14);
|
||||
let h0 = h[0] + (t0 & 0x1fff);
|
||||
let h1 = h[1] + (((t0 >>> 13) | (t1 << 3)) & 0x1fff);
|
||||
let h2 = h[2] + (((t1 >>> 10) | (t2 << 6)) & 0x1fff);
|
||||
let h3 = h[3] + (((t2 >>> 7) | (t3 << 9)) & 0x1fff);
|
||||
let h4 = h[4] + (((t3 >>> 4) | (t4 << 12)) & 0x1fff);
|
||||
let h5 = h[5] + ((t4 >>> 1) & 0x1fff);
|
||||
let h6 = h[6] + (((t4 >>> 14) | (t5 << 2)) & 0x1fff);
|
||||
let h7 = h[7] + (((t5 >>> 11) | (t6 << 5)) & 0x1fff);
|
||||
let h8 = h[8] + (((t6 >>> 8) | (t7 << 8)) & 0x1fff);
|
||||
let h9 = h[9] + ((t7 >>> 5) | hibit);
|
||||
let c = 0;
|
||||
let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);
|
||||
c = d0 >>> 13;
|
||||
d0 &= 0x1fff;
|
||||
d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);
|
||||
c += d0 >>> 13;
|
||||
d0 &= 0x1fff;
|
||||
let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);
|
||||
c = d1 >>> 13;
|
||||
d1 &= 0x1fff;
|
||||
d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);
|
||||
c += d1 >>> 13;
|
||||
d1 &= 0x1fff;
|
||||
let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);
|
||||
c = d2 >>> 13;
|
||||
d2 &= 0x1fff;
|
||||
d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);
|
||||
c += d2 >>> 13;
|
||||
d2 &= 0x1fff;
|
||||
let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);
|
||||
c = d3 >>> 13;
|
||||
d3 &= 0x1fff;
|
||||
d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);
|
||||
c += d3 >>> 13;
|
||||
d3 &= 0x1fff;
|
||||
let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;
|
||||
c = d4 >>> 13;
|
||||
d4 &= 0x1fff;
|
||||
d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);
|
||||
c += d4 >>> 13;
|
||||
d4 &= 0x1fff;
|
||||
let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;
|
||||
c = d5 >>> 13;
|
||||
d5 &= 0x1fff;
|
||||
d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);
|
||||
c += d5 >>> 13;
|
||||
d5 &= 0x1fff;
|
||||
let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;
|
||||
c = d6 >>> 13;
|
||||
d6 &= 0x1fff;
|
||||
d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);
|
||||
c += d6 >>> 13;
|
||||
d6 &= 0x1fff;
|
||||
let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;
|
||||
c = d7 >>> 13;
|
||||
d7 &= 0x1fff;
|
||||
d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);
|
||||
c += d7 >>> 13;
|
||||
d7 &= 0x1fff;
|
||||
let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;
|
||||
c = d8 >>> 13;
|
||||
d8 &= 0x1fff;
|
||||
d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);
|
||||
c += d8 >>> 13;
|
||||
d8 &= 0x1fff;
|
||||
let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;
|
||||
c = d9 >>> 13;
|
||||
d9 &= 0x1fff;
|
||||
d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;
|
||||
c += d9 >>> 13;
|
||||
d9 &= 0x1fff;
|
||||
c = ((c << 2) + c) | 0;
|
||||
c = (c + d0) | 0;
|
||||
d0 = c & 0x1fff;
|
||||
c = c >>> 13;
|
||||
d1 += c;
|
||||
h[0] = d0;
|
||||
h[1] = d1;
|
||||
h[2] = d2;
|
||||
h[3] = d3;
|
||||
h[4] = d4;
|
||||
h[5] = d5;
|
||||
h[6] = d6;
|
||||
h[7] = d7;
|
||||
h[8] = d8;
|
||||
h[9] = d9;
|
||||
}
|
||||
finalize() {
|
||||
const { h, pad } = this;
|
||||
const g = new Uint16Array(10);
|
||||
let c = h[1] >>> 13;
|
||||
h[1] &= 0x1fff;
|
||||
for (let i = 2; i < 10; i++) {
|
||||
h[i] += c;
|
||||
c = h[i] >>> 13;
|
||||
h[i] &= 0x1fff;
|
||||
}
|
||||
h[0] += c * 5;
|
||||
c = h[0] >>> 13;
|
||||
h[0] &= 0x1fff;
|
||||
h[1] += c;
|
||||
c = h[1] >>> 13;
|
||||
h[1] &= 0x1fff;
|
||||
h[2] += c;
|
||||
g[0] = h[0] + 5;
|
||||
c = g[0] >>> 13;
|
||||
g[0] &= 0x1fff;
|
||||
for (let i = 1; i < 10; i++) {
|
||||
g[i] = h[i] + c;
|
||||
c = g[i] >>> 13;
|
||||
g[i] &= 0x1fff;
|
||||
}
|
||||
g[9] -= 1 << 13;
|
||||
let mask = (c ^ 1) - 1;
|
||||
for (let i = 0; i < 10; i++)
|
||||
g[i] &= mask;
|
||||
mask = ~mask;
|
||||
for (let i = 0; i < 10; i++)
|
||||
h[i] = (h[i] & mask) | g[i];
|
||||
h[0] = (h[0] | (h[1] << 13)) & 0xffff;
|
||||
h[1] = ((h[1] >>> 3) | (h[2] << 10)) & 0xffff;
|
||||
h[2] = ((h[2] >>> 6) | (h[3] << 7)) & 0xffff;
|
||||
h[3] = ((h[3] >>> 9) | (h[4] << 4)) & 0xffff;
|
||||
h[4] = ((h[4] >>> 12) | (h[5] << 1) | (h[6] << 14)) & 0xffff;
|
||||
h[5] = ((h[6] >>> 2) | (h[7] << 11)) & 0xffff;
|
||||
h[6] = ((h[7] >>> 5) | (h[8] << 8)) & 0xffff;
|
||||
h[7] = ((h[8] >>> 8) | (h[9] << 5)) & 0xffff;
|
||||
let f = h[0] + pad[0];
|
||||
h[0] = f & 0xffff;
|
||||
for (let i = 1; i < 8; i++) {
|
||||
f = (((h[i] + pad[i]) | 0) + (f >>> 16)) | 0;
|
||||
h[i] = f & 0xffff;
|
||||
}
|
||||
}
|
||||
update(data) {
|
||||
_assert_js_1.default.exists(this);
|
||||
const { buffer, blockLen } = this;
|
||||
data = (0, utils_js_1.toBytes)(data);
|
||||
const len = data.length;
|
||||
for (let pos = 0; pos < len;) {
|
||||
const take = Math.min(blockLen - this.pos, len - pos);
|
||||
// Fast path: we have at least one block in input
|
||||
if (take === blockLen) {
|
||||
for (; blockLen <= len - pos; pos += blockLen)
|
||||
this.process(data, pos);
|
||||
continue;
|
||||
}
|
||||
buffer.set(data.subarray(pos, pos + take), this.pos);
|
||||
this.pos += take;
|
||||
pos += take;
|
||||
if (this.pos === blockLen) {
|
||||
this.process(buffer, 0, false);
|
||||
this.pos = 0;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
destroy() {
|
||||
this.h.fill(0);
|
||||
this.r.fill(0);
|
||||
this.buffer.fill(0);
|
||||
this.pad.fill(0);
|
||||
}
|
||||
digestInto(out) {
|
||||
_assert_js_1.default.exists(this);
|
||||
_assert_js_1.default.output(out, this);
|
||||
this.finished = true;
|
||||
const { buffer, h } = this;
|
||||
let { pos } = this;
|
||||
if (pos) {
|
||||
buffer[pos++] = 1;
|
||||
// buffer.subarray(pos).fill(0);
|
||||
for (; pos < 16; pos++)
|
||||
buffer[pos] = 0;
|
||||
this.process(buffer, 0, true);
|
||||
}
|
||||
this.finalize();
|
||||
let opos = 0;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
out[opos++] = h[i] >>> 0;
|
||||
out[opos++] = h[i] >>> 8;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
digest() {
|
||||
const { buffer, outputLen } = this;
|
||||
this.digestInto(buffer);
|
||||
const res = buffer.slice(0, outputLen);
|
||||
this.destroy();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
function wrapConstructorWithKey(hashCons) {
|
||||
const hashC = (msg, key) => hashCons(key).update((0, utils_js_1.toBytes)(msg)).digest();
|
||||
const tmp = hashCons(new Uint8Array(32));
|
||||
hashC.outputLen = tmp.outputLen;
|
||||
hashC.blockLen = tmp.blockLen;
|
||||
hashC.create = (key) => hashCons(key);
|
||||
return hashC;
|
||||
}
|
||||
exports.wrapConstructorWithKey = wrapConstructorWithKey;
|
||||
exports.poly1305 = wrapConstructorWithKey((key) => new Poly1305(key));
|
||||
//# sourceMappingURL=_poly1305.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-2
@@ -1,2 +0,0 @@
|
||||
export declare function polyval(h: Uint8Array, data: Uint8Array): Uint8Array;
|
||||
//# sourceMappingURL=_polyval.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"_polyval.d.ts","sourceRoot":"","sources":["src/_polyval.ts"],"names":[],"mappings":"AAyDA,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,cAiDtD"}
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.polyval = void 0;
|
||||
const utils_js_1 = require("./utils.js");
|
||||
// AES-SIV polyval, little-endian "mirror image" of AES-GCM GHash
|
||||
// polynomial hash function. Defined in RFC 8452.
|
||||
// Reverse bits in u32, constant-time, precompute will be faster, but non-constant time
|
||||
function rev32(x) {
|
||||
x = ((x & 1431655765) << 1) | ((x >>> 1) & 1431655765);
|
||||
x = ((x & 858993459) << 2) | ((x >>> 2) & 858993459);
|
||||
x = ((x & 252645135) << 4) | ((x >>> 4) & 252645135);
|
||||
x = ((x & 16711935) << 8) | ((x >>> 8) & 16711935);
|
||||
return (x << 16) | (x >>> 16);
|
||||
}
|
||||
// wrapped 32 bit multiplication
|
||||
const wrapMul = (a, b) => Math.imul(a, b) >>> 0;
|
||||
// https://timtaubert.de/blog/2017/06/verified-binary-multiplication-for-ghash/
|
||||
function bmul32(x, y) {
|
||||
const x0 = x & 286331153;
|
||||
const x1 = x & 572662306;
|
||||
const x2 = x & 1145324612;
|
||||
const x3 = x & 2290649224;
|
||||
const y0 = y & 286331153;
|
||||
const y1 = y & 572662306;
|
||||
const y2 = y & 1145324612;
|
||||
const y3 = y & 2290649224;
|
||||
let res = (wrapMul(x0, y0) ^ wrapMul(x1, y3) ^ wrapMul(x2, y2) ^ wrapMul(x3, y1)) & 286331153;
|
||||
res |= (wrapMul(x0, y1) ^ wrapMul(x1, y0) ^ wrapMul(x2, y3) ^ wrapMul(x3, y2)) & 572662306;
|
||||
res |= (wrapMul(x0, y2) ^ wrapMul(x1, y1) ^ wrapMul(x2, y0) ^ wrapMul(x3, y3)) & 1145324612;
|
||||
res |= (wrapMul(x0, y3) ^ wrapMul(x1, y2) ^ wrapMul(x2, y1) ^ wrapMul(x3, y0)) & 2290649224;
|
||||
return res >>> 0;
|
||||
}
|
||||
function mulPart(arr) {
|
||||
const a = new Uint32Array(18);
|
||||
a[0] = arr[0];
|
||||
a[1] = arr[1];
|
||||
a[2] = arr[2];
|
||||
a[3] = arr[3];
|
||||
a[4] = a[0] ^ a[1];
|
||||
a[5] = a[2] ^ a[3];
|
||||
a[6] = a[0] ^ a[2];
|
||||
a[7] = a[1] ^ a[3];
|
||||
a[8] = a[6] ^ a[7];
|
||||
a[9] = rev32(arr[0]);
|
||||
a[10] = rev32(arr[1]);
|
||||
a[11] = rev32(arr[2]);
|
||||
a[12] = rev32(arr[3]);
|
||||
a[13] = a[9] ^ a[10];
|
||||
a[14] = a[11] ^ a[12];
|
||||
a[15] = a[9] ^ a[11];
|
||||
a[16] = a[10] ^ a[12];
|
||||
a[17] = a[15] ^ a[16];
|
||||
return a;
|
||||
}
|
||||
function polyval(h, data) {
|
||||
(0, utils_js_1.ensureBytes)(h);
|
||||
(0, utils_js_1.ensureBytes)(data);
|
||||
const s = new Uint32Array(4);
|
||||
// Precompute for multiplication
|
||||
const a = mulPart((0, utils_js_1.u32)(h));
|
||||
if (data.length % 16)
|
||||
throw new Error('polyval: data must be padded to 16 bytes');
|
||||
const data32 = (0, utils_js_1.u32)(data);
|
||||
for (let i = 0; i < data32.length; i += 4) {
|
||||
// Xor
|
||||
s[0] ^= data32[i + 0];
|
||||
s[1] ^= data32[i + 1];
|
||||
s[2] ^= data32[i + 2];
|
||||
s[3] ^= data32[i + 3];
|
||||
// Dot via Karatsuba multiplication, based on MIT-licensed
|
||||
// https://bearssl.org/gitweb/?p=BearSSL;a=blob;f=src/hash/ghash_ctmul32.c;hb=4b6046412
|
||||
const b = mulPart(s);
|
||||
const c = new Uint32Array(18);
|
||||
for (let i = 0; i < 18; i++)
|
||||
c[i] = bmul32(a[i], b[i]);
|
||||
c[4] ^= c[0] ^ c[1];
|
||||
c[5] ^= c[2] ^ c[3];
|
||||
c[8] ^= c[6] ^ c[7];
|
||||
c[13] ^= c[9] ^ c[10];
|
||||
c[14] ^= c[11] ^ c[12];
|
||||
c[17] ^= c[15] ^ c[16];
|
||||
const zw = new Uint32Array(8);
|
||||
zw[0] = c[0];
|
||||
zw[1] = c[4] ^ (rev32(c[9]) >>> 1);
|
||||
zw[2] = c[1] ^ c[0] ^ c[2] ^ c[6] ^ (rev32(c[13]) >>> 1);
|
||||
zw[3] = c[4] ^ c[5] ^ c[8] ^ (rev32(c[10] ^ c[9] ^ c[11] ^ c[15]) >>> 1);
|
||||
zw[4] = c[2] ^ c[1] ^ c[3] ^ c[7] ^ (rev32(c[13] ^ c[14] ^ c[17]) >>> 1);
|
||||
zw[5] = c[5] ^ (rev32(c[11] ^ c[10] ^ c[12] ^ c[16]) >>> 1);
|
||||
zw[6] = c[3] ^ (rev32(c[14]) >>> 1);
|
||||
zw[7] = rev32(c[12]) >>> 1;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const lw = zw[i];
|
||||
zw[i + 4] ^= lw ^ (lw >>> 1) ^ (lw >>> 2) ^ (lw >>> 7);
|
||||
zw[i + 3] ^= (lw << 31) ^ (lw << 30) ^ (lw << 25);
|
||||
}
|
||||
s[0] = zw[4];
|
||||
s[1] = zw[5];
|
||||
s[2] = zw[6];
|
||||
s[3] = zw[7];
|
||||
}
|
||||
return (0, utils_js_1.u8)(s);
|
||||
}
|
||||
exports.polyval = polyval;
|
||||
//# sourceMappingURL=_polyval.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-11
@@ -1,11 +0,0 @@
|
||||
export type SalsaOpts = {
|
||||
core: (c: Uint32Array, key: Uint32Array, nonce: Uint32Array, out: Uint32Array, counter: number, rounds?: number) => void;
|
||||
rounds?: number;
|
||||
counterRight?: boolean;
|
||||
counterLen?: number;
|
||||
blockLen?: number;
|
||||
allow128bitKeys?: boolean;
|
||||
extendNonceFn?: (c: Uint32Array, key: Uint8Array, src: Uint8Array, dst: Uint8Array) => Uint8Array;
|
||||
};
|
||||
export declare const salsaBasic: (opts: SalsaOpts) => (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array, counter?: number) => Uint8Array;
|
||||
//# sourceMappingURL=_salsa.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"_salsa.d.ts","sourceRoot":"","sources":["src/_salsa.ts"],"names":[],"mappings":"AA0DA,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,CACJ,CAAC,EAAE,WAAW,EACd,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,KACZ,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,KAAK,UAAU,CAAC;CACnG,CAAC;AAKF,eAAO,MAAM,UAAU,SAAU,SAAS,WAcjC,UAAU,SACR,UAAU,QACX,UAAU,WACP,UAAU,uBAElB,UA6EJ,CAAC"}
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.salsaBasic = void 0;
|
||||
// Basic utils for salsa-like ciphers
|
||||
// Check out _micro.ts for descriptive documentation.
|
||||
const _assert_js_1 = require("./_assert.js");
|
||||
const utils_js_1 = require("./utils.js");
|
||||
/*
|
||||
RFC8439 requires multi-step cipher stream, where
|
||||
authKey starts with counter: 0, actual msg with counter: 1.
|
||||
|
||||
For this, we need a way to re-use nonce / counter:
|
||||
|
||||
const counter = new Uint8Array(4);
|
||||
chacha(..., counter, ...); // counter is now 1
|
||||
chacha(..., counter, ...); // counter is now 2
|
||||
|
||||
This is complicated:
|
||||
|
||||
- Original papers don't allow mutating counters
|
||||
- Counter overflow is undefined: https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/
|
||||
- 3rd-party library stablelib implementation uses an approach where you can provide
|
||||
nonce and counter instead of just nonce - and it will re-use it
|
||||
- We could have did something similar, but ChaCha has different counter position
|
||||
(counter | nonce), which is not composable with XChaCha, because full counter
|
||||
is (nonce16 | counter | nonce16). Stablelib doesn't support in-place counter for XChaCha.
|
||||
- We could separate nonce & counter and provide separate API for counter re-use, but
|
||||
there are different counter sizes depending on an algorithm.
|
||||
- Salsa & ChaCha also differ in structures of key / sigma:
|
||||
|
||||
salsa: c0 | k(4) | c1 | nonce(2) | ctr(2) | c2 | k(4) | c4
|
||||
chacha: c(4) | k(8) | ctr(1) | nonce(3)
|
||||
chachaDJB: c(4) | k(8) | ctr(2) | nonce(2)
|
||||
- Creating function such as `setSalsaState(key, nonce, sigma, data)` won't work,
|
||||
because we can't re-use counter array
|
||||
- 32-bit nonce is `2 ** 32 * 64` = 256GB with 32-bit counter
|
||||
- JS does not allow UintArrays bigger than 4GB, so supporting 64-bit counters doesn't matter
|
||||
|
||||
Structure is as following:
|
||||
|
||||
key=16 -> sigma16, k=key|key
|
||||
key=32 -> sigma32, k=key
|
||||
|
||||
nonces:
|
||||
salsa20: 8 (8-byte counter)
|
||||
chacha20djb: 8 (8-byte counter)
|
||||
chacha20tls: 12 (4-byte counter)
|
||||
xsalsa: 24 (16 -> hsalsa, 8 -> old nonce)
|
||||
xchacha: 24 (16 -> hchacha, 8 -> old nonce)
|
||||
|
||||
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2
|
||||
Use the subkey and remaining 8 byte nonce with ChaCha20 as normal
|
||||
(prefixed by 4 NUL bytes, since [RFC8439] specifies a 12-byte nonce).
|
||||
*/
|
||||
const sigma16 = (0, utils_js_1.utf8ToBytes)('expand 16-byte k');
|
||||
const sigma32 = (0, utils_js_1.utf8ToBytes)('expand 32-byte k');
|
||||
const sigma16_32 = (0, utils_js_1.u32)(sigma16);
|
||||
const sigma32_32 = (0, utils_js_1.u32)(sigma32);
|
||||
// Is byte array aligned to 4 byte offset (u32)?
|
||||
const isAligned32 = (b) => !(b.byteOffset % 4);
|
||||
const salsaBasic = (opts) => {
|
||||
const { core, rounds, counterRight, counterLen, allow128bitKeys, extendNonceFn, blockLen } = (0, utils_js_1.checkOpts)({ rounds: 20, counterRight: false, counterLen: 8, allow128bitKeys: true, blockLen: 64 }, opts);
|
||||
_assert_js_1.default.number(counterLen);
|
||||
_assert_js_1.default.number(rounds);
|
||||
_assert_js_1.default.number(blockLen);
|
||||
_assert_js_1.default.bool(counterRight);
|
||||
_assert_js_1.default.bool(allow128bitKeys);
|
||||
const blockLen32 = blockLen / 4;
|
||||
if (blockLen % 4 !== 0)
|
||||
throw new Error('Salsa/ChaCha: blockLen must be aligned to 4 bytes');
|
||||
return (key, nonce, data, output, counter = 0) => {
|
||||
_assert_js_1.default.bytes(key);
|
||||
_assert_js_1.default.bytes(nonce);
|
||||
_assert_js_1.default.bytes(data);
|
||||
if (!output)
|
||||
output = new Uint8Array(data.length);
|
||||
_assert_js_1.default.bytes(output);
|
||||
_assert_js_1.default.number(counter);
|
||||
// > new Uint32Array([2**32])
|
||||
// Uint32Array(1) [ 0 ]
|
||||
// > new Uint32Array([2**32-1])
|
||||
// Uint32Array(1) [ 4294967295 ]
|
||||
if (counter < 0 || counter >= 2 ** 32 - 1)
|
||||
throw new Error('Salsa/ChaCha: counter overflow');
|
||||
if (output.length < data.length) {
|
||||
throw new Error(`Salsa/ChaCha: output (${output.length}) is shorter than data (${data.length})`);
|
||||
}
|
||||
const toClean = [];
|
||||
let k, sigma;
|
||||
// Handle 128 byte keys
|
||||
if (key.length === 32) {
|
||||
k = key;
|
||||
sigma = sigma32_32;
|
||||
}
|
||||
else if (key.length === 16 && allow128bitKeys) {
|
||||
k = new Uint8Array(32);
|
||||
k.set(key);
|
||||
k.set(key, 16);
|
||||
sigma = sigma16_32;
|
||||
toClean.push(k);
|
||||
}
|
||||
else
|
||||
throw new Error(`Salsa/ChaCha: invalid 32-byte key, got length=${key.length}`);
|
||||
// Handle extended nonce (HChaCha/HSalsa)
|
||||
if (extendNonceFn) {
|
||||
if (nonce.length <= 16)
|
||||
throw new Error(`Salsa/ChaCha: extended nonce must be bigger than 16 bytes`);
|
||||
k = extendNonceFn(sigma, k, nonce.subarray(0, 16), new Uint8Array(32));
|
||||
toClean.push(k);
|
||||
nonce = nonce.subarray(16);
|
||||
}
|
||||
// Handle nonce counter
|
||||
const nonceLen = 16 - counterLen;
|
||||
if (nonce.length !== nonceLen)
|
||||
throw new Error(`Salsa/ChaCha: nonce must be ${nonceLen} or 16 bytes`);
|
||||
// Pad counter when nonce is 64 bit
|
||||
if (nonceLen !== 12) {
|
||||
const nc = new Uint8Array(12);
|
||||
nc.set(nonce, counterRight ? 0 : 12 - nonce.length);
|
||||
toClean.push((nonce = nc));
|
||||
}
|
||||
// Counter positions
|
||||
const block = new Uint8Array(blockLen);
|
||||
// Cast to Uint32Array for speed
|
||||
const b32 = (0, utils_js_1.u32)(block);
|
||||
const k32 = (0, utils_js_1.u32)(k);
|
||||
const n32 = (0, utils_js_1.u32)(nonce);
|
||||
// Make sure that buffers aligned to 4 bytes
|
||||
const d32 = isAligned32(data) && (0, utils_js_1.u32)(data);
|
||||
const o32 = isAligned32(output) && (0, utils_js_1.u32)(output);
|
||||
toClean.push(b32);
|
||||
const len = data.length;
|
||||
for (let pos = 0, ctr = counter; pos < len; ctr++) {
|
||||
core(sigma, k32, n32, b32, ctr, rounds);
|
||||
if (ctr >= 2 ** 32 - 1)
|
||||
throw new Error('Salsa/ChaCha: counter overflow');
|
||||
const take = Math.min(blockLen, len - pos);
|
||||
// full block && aligned to 4 bytes
|
||||
if (take === blockLen && o32 && d32) {
|
||||
const pos32 = pos / 4;
|
||||
if (pos % 4 !== 0)
|
||||
throw new Error('Salsa/ChaCha: invalid block position');
|
||||
for (let j = 0; j < blockLen32; j++)
|
||||
o32[pos32 + j] = d32[pos32 + j] ^ b32[j];
|
||||
pos += blockLen;
|
||||
continue;
|
||||
}
|
||||
for (let j = 0; j < take; j++)
|
||||
output[pos + j] = data[pos + j] ^ block[j];
|
||||
pos += take;
|
||||
}
|
||||
for (let i = 0; i < toClean.length; i++)
|
||||
toClean[i].fill(0);
|
||||
return output;
|
||||
};
|
||||
};
|
||||
exports.salsaBasic = salsaBasic;
|
||||
//# sourceMappingURL=_salsa.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"_salsa.js","sourceRoot":"","sources":["src/_salsa.ts"],"names":[],"mappings":";;;AAAA,qCAAqC;AACrC,qDAAqD;AACrD,6CAAkC;AAClC,yCAAyD;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CE;AAEF,MAAM,OAAO,GAAG,IAAA,sBAAW,EAAC,kBAAkB,CAAC,CAAC;AAChD,MAAM,OAAO,GAAG,IAAA,sBAAW,EAAC,kBAAkB,CAAC,CAAC;AAChD,MAAM,UAAU,GAAG,IAAA,cAAG,EAAC,OAAO,CAAC,CAAC;AAChC,MAAM,UAAU,GAAG,IAAA,cAAG,EAAC,OAAO,CAAC,CAAC;AAmBhC,gDAAgD;AAChD,MAAM,WAAW,GAAG,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;AAEpD,MAAM,UAAU,GAAG,CAAC,IAAe,EAAE,EAAE;IAC5C,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,GACxF,IAAA,oBAAS,EACP,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EACvF,IAAI,CACL,CAAC;IACJ,oBAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC1B,oBAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACtB,oBAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxB,oBAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1B,oBAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC7B,MAAM,UAAU,GAAG,QAAQ,GAAG,CAAC,CAAC;IAChC,IAAI,QAAQ,GAAG,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IAC7F,OAAO,CACL,GAAe,EACf,KAAiB,EACjB,IAAgB,EAChB,MAAmB,EACnB,OAAO,GAAG,CAAC,EACC,EAAE;QACd,oBAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClB,oBAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpB,oBAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnB,IAAI,CAAC,MAAM;YAAE,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClD,oBAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACrB,oBAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACvB,6BAA6B;QAC7B,uBAAuB;QACvB,+BAA+B;QAC/B,gCAAgC;QAChC,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC7F,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE;YAC/B,MAAM,IAAI,KAAK,CACb,yBAAyB,MAAM,CAAC,MAAM,2BAA2B,IAAI,CAAC,MAAM,GAAG,CAChF,CAAC;SACH;QACD,MAAM,OAAO,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,EAAE,KAAK,CAAC;QACb,uBAAuB;QACvB,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE;YACrB,CAAC,GAAG,GAAG,CAAC;YACR,KAAK,GAAG,UAAU,CAAC;SACpB;aAAM,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,IAAI,eAAe,EAAE;YAC/C,CAAC,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;YACvB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACX,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACf,KAAK,GAAG,UAAU,CAAC;YACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACjB;;YAAM,MAAM,IAAI,KAAK,CAAC,iDAAiD,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACtF,yCAAyC;QACzC,IAAI,aAAa,EAAE;YACjB,IAAI,KAAK,CAAC,MAAM,IAAI,EAAE;gBACpB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;YAC/E,CAAC,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;YACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChB,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;SAC5B;QACD,uBAAuB;QACvB,MAAM,QAAQ,GAAG,EAAE,GAAG,UAAU,CAAC;QACjC,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ;YAC3B,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,cAAc,CAAC,CAAC;QACzE,mCAAmC;QACnC,IAAI,QAAQ,KAAK,EAAE,EAAE;YACnB,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;YAC9B,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;YACpD,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;SAC5B;QACD,oBAAoB;QACpB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;QACvC,gCAAgC;QAChC,MAAM,GAAG,GAAG,IAAA,cAAG,EAAC,KAAK,CAAC,CAAC;QACvB,MAAM,GAAG,GAAG,IAAA,cAAG,EAAC,CAAC,CAAC,CAAC;QACnB,MAAM,GAAG,GAAG,IAAA,cAAG,EAAC,KAAK,CAAC,CAAC;QACvB,4CAA4C;QAC5C,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,IAAA,cAAG,EAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,IAAA,cAAG,EAAC,MAAM,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC;QACxB,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,EAAE;YACjD,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;YACxC,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;YAC3C,mCAAmC;YACnC,IAAI,IAAI,KAAK,QAAQ,IAAI,GAAG,IAAI,GAAG,EAAE;gBACnC,MAAM,KAAK,GAAG,GAAG,GAAG,CAAC,CAAC;gBACtB,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;gBAC3E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE;oBAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC9E,GAAG,IAAI,QAAQ,CAAC;gBAChB,SAAS;aACV;YACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE;gBAAE,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1E,GAAG,IAAI,IAAI,CAAC;SACb;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5D,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC,CAAC;AAhGW,QAAA,UAAU,cAgGrB"}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
import { Cipher } from './utils.js';
|
||||
/**
|
||||
* hchacha helper method, used primarily in xchacha, to hash
|
||||
* key and nonce into key' and nonce'.
|
||||
* Same as chachaCore, but there doesn't seem to be a way to move the block
|
||||
* out without 25% performance hit.
|
||||
*/
|
||||
export declare function hchacha(c: Uint32Array, key: Uint8Array, src: Uint8Array, out: Uint8Array): Uint8Array;
|
||||
/**
|
||||
* Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.
|
||||
*/
|
||||
export declare const chacha20orig: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
export declare const chacha20: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* XChaCha eXtended-nonce ChaCha. 24-byte nonce.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
*/
|
||||
export declare const xchacha20: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* Reduced 8-round chacha, described in original paper.
|
||||
*/
|
||||
export declare const chacha8: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* Reduced 12-round chacha, described in original paper.
|
||||
*/
|
||||
export declare const chacha12: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* AEAD algorithm from RFC 8439.
|
||||
* Salsa20 and chacha (RFC 8439) use poly1305 differently.
|
||||
* We could have composed them similar to:
|
||||
* https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250
|
||||
* But it's hard because of authKey:
|
||||
* In salsa20, authKey changes position in salsa stream.
|
||||
* In chacha, authKey can't be computed inside computeTag, it modifies the counter.
|
||||
*/
|
||||
export declare const _poly1305_aead: (xorStream: typeof chacha20) => (key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => Cipher;
|
||||
/**
|
||||
* ChaCha20-Poly1305 from RFC 8439.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
export declare const chacha20poly1305: (key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => Cipher;
|
||||
/**
|
||||
* XChaCha20-Poly1305 extended-nonce chacha.
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
export declare const xchacha20poly1305: (key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => Cipher;
|
||||
//# sourceMappingURL=chacha.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"chacha.d.ts","sourceRoot":"","sources":["src/chacha.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA0D,MAAM,YAAY,CAAC;AAgF5F;;;;;GAKG;AAEH,wBAAgB,OAAO,CACrB,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAChE,UAAU,CA0DZ;AACD;;GAEG;AACH,eAAO,MAAM,YAAY,yHAIvB,CAAC;AACH;;;GAGG;AACH,eAAO,MAAM,QAAQ,yHAKnB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,SAAS,yHAMpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,OAAO,yHAKlB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,yHAKnB,CAAC;AA+BH;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,cACb,eAAe,WACrB,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,MAqCvD,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,gBAAgB,QA3CrB,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,MA2Cc,CAAC;AACzE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,QAjDtB,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,MAiDgB,CAAC"}
|
||||
-334
@@ -1,334 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.xchacha20poly1305 = exports.chacha20poly1305 = exports._poly1305_aead = exports.chacha12 = exports.chacha8 = exports.xchacha20 = exports.chacha20 = exports.chacha20orig = exports.hchacha = void 0;
|
||||
const utils_js_1 = require("./utils.js");
|
||||
const _poly1305_js_1 = require("./_poly1305.js");
|
||||
const _salsa_js_1 = require("./_salsa.js");
|
||||
// ChaCha20 stream cipher was released in 2008. ChaCha aims to increase
|
||||
// the diffusion per round, but had slightly less cryptanalysis.
|
||||
// https://cr.yp.to/chacha.html, http://cr.yp.to/chacha/chacha-20080128.pdf
|
||||
// Left rotate for uint32
|
||||
const rotl = (a, b) => (a << b) | (a >>> (32 - b));
|
||||
/**
|
||||
* ChaCha core function.
|
||||
*/
|
||||
// prettier-ignore
|
||||
function chachaCore(c, k, n, out, cnt, rounds = 20) {
|
||||
let y00 = c[0], y01 = c[1], y02 = c[2], y03 = c[3]; // "expa" "nd 3" "2-by" "te k"
|
||||
let y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3]; // Key Key Key Key
|
||||
let y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7]; // Key Key Key Key
|
||||
let y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2]; // Counter Counter Nonce Nonce
|
||||
// Save state to temporary variables
|
||||
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
||||
// Main loop
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
x00 = (x00 + x04) | 0;
|
||||
x12 = rotl(x12 ^ x00, 16);
|
||||
x08 = (x08 + x12) | 0;
|
||||
x04 = rotl(x04 ^ x08, 12);
|
||||
x00 = (x00 + x04) | 0;
|
||||
x12 = rotl(x12 ^ x00, 8);
|
||||
x08 = (x08 + x12) | 0;
|
||||
x04 = rotl(x04 ^ x08, 7);
|
||||
x01 = (x01 + x05) | 0;
|
||||
x13 = rotl(x13 ^ x01, 16);
|
||||
x09 = (x09 + x13) | 0;
|
||||
x05 = rotl(x05 ^ x09, 12);
|
||||
x01 = (x01 + x05) | 0;
|
||||
x13 = rotl(x13 ^ x01, 8);
|
||||
x09 = (x09 + x13) | 0;
|
||||
x05 = rotl(x05 ^ x09, 7);
|
||||
x02 = (x02 + x06) | 0;
|
||||
x14 = rotl(x14 ^ x02, 16);
|
||||
x10 = (x10 + x14) | 0;
|
||||
x06 = rotl(x06 ^ x10, 12);
|
||||
x02 = (x02 + x06) | 0;
|
||||
x14 = rotl(x14 ^ x02, 8);
|
||||
x10 = (x10 + x14) | 0;
|
||||
x06 = rotl(x06 ^ x10, 7);
|
||||
x03 = (x03 + x07) | 0;
|
||||
x15 = rotl(x15 ^ x03, 16);
|
||||
x11 = (x11 + x15) | 0;
|
||||
x07 = rotl(x07 ^ x11, 12);
|
||||
x03 = (x03 + x07) | 0;
|
||||
x15 = rotl(x15 ^ x03, 8);
|
||||
x11 = (x11 + x15) | 0;
|
||||
x07 = rotl(x07 ^ x11, 7);
|
||||
x00 = (x00 + x05) | 0;
|
||||
x15 = rotl(x15 ^ x00, 16);
|
||||
x10 = (x10 + x15) | 0;
|
||||
x05 = rotl(x05 ^ x10, 12);
|
||||
x00 = (x00 + x05) | 0;
|
||||
x15 = rotl(x15 ^ x00, 8);
|
||||
x10 = (x10 + x15) | 0;
|
||||
x05 = rotl(x05 ^ x10, 7);
|
||||
x01 = (x01 + x06) | 0;
|
||||
x12 = rotl(x12 ^ x01, 16);
|
||||
x11 = (x11 + x12) | 0;
|
||||
x06 = rotl(x06 ^ x11, 12);
|
||||
x01 = (x01 + x06) | 0;
|
||||
x12 = rotl(x12 ^ x01, 8);
|
||||
x11 = (x11 + x12) | 0;
|
||||
x06 = rotl(x06 ^ x11, 7);
|
||||
x02 = (x02 + x07) | 0;
|
||||
x13 = rotl(x13 ^ x02, 16);
|
||||
x08 = (x08 + x13) | 0;
|
||||
x07 = rotl(x07 ^ x08, 12);
|
||||
x02 = (x02 + x07) | 0;
|
||||
x13 = rotl(x13 ^ x02, 8);
|
||||
x08 = (x08 + x13) | 0;
|
||||
x07 = rotl(x07 ^ x08, 7);
|
||||
x03 = (x03 + x04) | 0;
|
||||
x14 = rotl(x14 ^ x03, 16);
|
||||
x09 = (x09 + x14) | 0;
|
||||
x04 = rotl(x04 ^ x09, 12);
|
||||
x03 = (x03 + x04) | 0;
|
||||
x14 = rotl(x14 ^ x03, 8);
|
||||
x09 = (x09 + x14) | 0;
|
||||
x04 = rotl(x04 ^ x09, 7);
|
||||
}
|
||||
// Write output
|
||||
let oi = 0;
|
||||
out[oi++] = (y00 + x00) | 0;
|
||||
out[oi++] = (y01 + x01) | 0;
|
||||
out[oi++] = (y02 + x02) | 0;
|
||||
out[oi++] = (y03 + x03) | 0;
|
||||
out[oi++] = (y04 + x04) | 0;
|
||||
out[oi++] = (y05 + x05) | 0;
|
||||
out[oi++] = (y06 + x06) | 0;
|
||||
out[oi++] = (y07 + x07) | 0;
|
||||
out[oi++] = (y08 + x08) | 0;
|
||||
out[oi++] = (y09 + x09) | 0;
|
||||
out[oi++] = (y10 + x10) | 0;
|
||||
out[oi++] = (y11 + x11) | 0;
|
||||
out[oi++] = (y12 + x12) | 0;
|
||||
out[oi++] = (y13 + x13) | 0;
|
||||
out[oi++] = (y14 + x14) | 0;
|
||||
out[oi++] = (y15 + x15) | 0;
|
||||
}
|
||||
/**
|
||||
* hchacha helper method, used primarily in xchacha, to hash
|
||||
* key and nonce into key' and nonce'.
|
||||
* Same as chachaCore, but there doesn't seem to be a way to move the block
|
||||
* out without 25% performance hit.
|
||||
*/
|
||||
// prettier-ignore
|
||||
function hchacha(c, key, src, out) {
|
||||
const k32 = (0, utils_js_1.u32)(key);
|
||||
const i32 = (0, utils_js_1.u32)(src);
|
||||
const o32 = (0, utils_js_1.u32)(out);
|
||||
let x00 = c[0], x01 = c[1], x02 = c[2], x03 = c[3];
|
||||
let x04 = k32[0], x05 = k32[1], x06 = k32[2], x07 = k32[3];
|
||||
let x08 = k32[4], x09 = k32[5], x10 = k32[6], x11 = k32[7];
|
||||
let x12 = i32[0], x13 = i32[1], x14 = i32[2], x15 = i32[3];
|
||||
for (let i = 0; i < 20; i += 2) {
|
||||
x00 = (x00 + x04) | 0;
|
||||
x12 = rotl(x12 ^ x00, 16);
|
||||
x08 = (x08 + x12) | 0;
|
||||
x04 = rotl(x04 ^ x08, 12);
|
||||
x00 = (x00 + x04) | 0;
|
||||
x12 = rotl(x12 ^ x00, 8);
|
||||
x08 = (x08 + x12) | 0;
|
||||
x04 = rotl(x04 ^ x08, 7);
|
||||
x01 = (x01 + x05) | 0;
|
||||
x13 = rotl(x13 ^ x01, 16);
|
||||
x09 = (x09 + x13) | 0;
|
||||
x05 = rotl(x05 ^ x09, 12);
|
||||
x01 = (x01 + x05) | 0;
|
||||
x13 = rotl(x13 ^ x01, 8);
|
||||
x09 = (x09 + x13) | 0;
|
||||
x05 = rotl(x05 ^ x09, 7);
|
||||
x02 = (x02 + x06) | 0;
|
||||
x14 = rotl(x14 ^ x02, 16);
|
||||
x10 = (x10 + x14) | 0;
|
||||
x06 = rotl(x06 ^ x10, 12);
|
||||
x02 = (x02 + x06) | 0;
|
||||
x14 = rotl(x14 ^ x02, 8);
|
||||
x10 = (x10 + x14) | 0;
|
||||
x06 = rotl(x06 ^ x10, 7);
|
||||
x03 = (x03 + x07) | 0;
|
||||
x15 = rotl(x15 ^ x03, 16);
|
||||
x11 = (x11 + x15) | 0;
|
||||
x07 = rotl(x07 ^ x11, 12);
|
||||
x03 = (x03 + x07) | 0;
|
||||
x15 = rotl(x15 ^ x03, 8);
|
||||
x11 = (x11 + x15) | 0;
|
||||
x07 = rotl(x07 ^ x11, 7);
|
||||
x00 = (x00 + x05) | 0;
|
||||
x15 = rotl(x15 ^ x00, 16);
|
||||
x10 = (x10 + x15) | 0;
|
||||
x05 = rotl(x05 ^ x10, 12);
|
||||
x00 = (x00 + x05) | 0;
|
||||
x15 = rotl(x15 ^ x00, 8);
|
||||
x10 = (x10 + x15) | 0;
|
||||
x05 = rotl(x05 ^ x10, 7);
|
||||
x01 = (x01 + x06) | 0;
|
||||
x12 = rotl(x12 ^ x01, 16);
|
||||
x11 = (x11 + x12) | 0;
|
||||
x06 = rotl(x06 ^ x11, 12);
|
||||
x01 = (x01 + x06) | 0;
|
||||
x12 = rotl(x12 ^ x01, 8);
|
||||
x11 = (x11 + x12) | 0;
|
||||
x06 = rotl(x06 ^ x11, 7);
|
||||
x02 = (x02 + x07) | 0;
|
||||
x13 = rotl(x13 ^ x02, 16);
|
||||
x08 = (x08 + x13) | 0;
|
||||
x07 = rotl(x07 ^ x08, 12);
|
||||
x02 = (x02 + x07) | 0;
|
||||
x13 = rotl(x13 ^ x02, 8);
|
||||
x08 = (x08 + x13) | 0;
|
||||
x07 = rotl(x07 ^ x08, 7);
|
||||
x03 = (x03 + x04) | 0;
|
||||
x14 = rotl(x14 ^ x03, 16);
|
||||
x09 = (x09 + x14) | 0;
|
||||
x04 = rotl(x04 ^ x09, 12);
|
||||
x03 = (x03 + x04) | 0;
|
||||
x14 = rotl(x14 ^ x03, 8);
|
||||
x09 = (x09 + x14) | 0;
|
||||
x04 = rotl(x04 ^ x09, 7);
|
||||
}
|
||||
o32[0] = x00;
|
||||
o32[1] = x01;
|
||||
o32[2] = x02;
|
||||
o32[3] = x03;
|
||||
o32[4] = x12;
|
||||
o32[5] = x13;
|
||||
o32[6] = x14;
|
||||
o32[7] = x15;
|
||||
return out;
|
||||
}
|
||||
exports.hchacha = hchacha;
|
||||
/**
|
||||
* Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.
|
||||
*/
|
||||
exports.chacha20orig = (0, _salsa_js_1.salsaBasic)({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 8,
|
||||
});
|
||||
/**
|
||||
* ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
exports.chacha20 = (0, _salsa_js_1.salsaBasic)({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* XChaCha eXtended-nonce ChaCha. 24-byte nonce.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
*/
|
||||
exports.xchacha20 = (0, _salsa_js_1.salsaBasic)({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 8,
|
||||
extendNonceFn: hchacha,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* Reduced 8-round chacha, described in original paper.
|
||||
*/
|
||||
exports.chacha8 = (0, _salsa_js_1.salsaBasic)({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 8,
|
||||
});
|
||||
/**
|
||||
* Reduced 12-round chacha, described in original paper.
|
||||
*/
|
||||
exports.chacha12 = (0, _salsa_js_1.salsaBasic)({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 12,
|
||||
});
|
||||
const ZERO = /* @__PURE__ */ new Uint8Array(16);
|
||||
// Pad to digest size with zeros
|
||||
const updatePadded = (h, msg) => {
|
||||
h.update(msg);
|
||||
const left = msg.length % 16;
|
||||
if (left)
|
||||
h.update(ZERO.subarray(left));
|
||||
};
|
||||
const computeTag = (fn, key, nonce, data, AAD) => {
|
||||
const authKey = fn(key, nonce, new Uint8Array(32));
|
||||
const h = _poly1305_js_1.poly1305.create(authKey);
|
||||
if (AAD)
|
||||
updatePadded(h, AAD);
|
||||
updatePadded(h, data);
|
||||
const num = new Uint8Array(16);
|
||||
const view = (0, utils_js_1.createView)(num);
|
||||
(0, utils_js_1.setBigUint64)(view, 0, BigInt(AAD ? AAD.length : 0), true);
|
||||
(0, utils_js_1.setBigUint64)(view, 8, BigInt(data.length), true);
|
||||
h.update(num);
|
||||
const res = h.digest();
|
||||
authKey.fill(0);
|
||||
return res;
|
||||
};
|
||||
/**
|
||||
* AEAD algorithm from RFC 8439.
|
||||
* Salsa20 and chacha (RFC 8439) use poly1305 differently.
|
||||
* We could have composed them similar to:
|
||||
* https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250
|
||||
* But it's hard because of authKey:
|
||||
* In salsa20, authKey changes position in salsa stream.
|
||||
* In chacha, authKey can't be computed inside computeTag, it modifies the counter.
|
||||
*/
|
||||
const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
||||
const tagLength = 16;
|
||||
(0, utils_js_1.ensureBytes)(key, 32);
|
||||
(0, utils_js_1.ensureBytes)(nonce);
|
||||
return {
|
||||
tagLength,
|
||||
encrypt: (plaintext, output) => {
|
||||
const plength = plaintext.length;
|
||||
const clength = plength + tagLength;
|
||||
if (output) {
|
||||
(0, utils_js_1.ensureBytes)(output, clength);
|
||||
}
|
||||
else {
|
||||
output = new Uint8Array(clength);
|
||||
}
|
||||
xorStream(key, nonce, plaintext, output, 1);
|
||||
const tag = computeTag(xorStream, key, nonce, output.subarray(0, -tagLength), AAD);
|
||||
output.set(tag, plength); // append tag
|
||||
return output;
|
||||
},
|
||||
decrypt: (ciphertext, output) => {
|
||||
const clength = ciphertext.length;
|
||||
const plength = clength - tagLength;
|
||||
if (clength < tagLength)
|
||||
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
|
||||
if (output) {
|
||||
(0, utils_js_1.ensureBytes)(output, plength);
|
||||
}
|
||||
else {
|
||||
output = new Uint8Array(plength);
|
||||
}
|
||||
const data = ciphertext.subarray(0, -tagLength);
|
||||
const passedTag = ciphertext.subarray(-tagLength);
|
||||
const tag = computeTag(xorStream, key, nonce, data, AAD);
|
||||
if (!(0, utils_js_1.equalBytes)(passedTag, tag))
|
||||
throw new Error('invalid tag');
|
||||
xorStream(key, nonce, data, output, 1);
|
||||
return output;
|
||||
},
|
||||
};
|
||||
};
|
||||
exports._poly1305_aead = _poly1305_aead;
|
||||
/**
|
||||
* ChaCha20-Poly1305 from RFC 8439.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
exports.chacha20poly1305 = (0, exports._poly1305_aead)(exports.chacha20);
|
||||
/**
|
||||
* XChaCha20-Poly1305 extended-nonce chacha.
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
exports.xchacha20poly1305 = (0, exports._poly1305_aead)(exports.xchacha20);
|
||||
//# sourceMappingURL=chacha.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-37
@@ -1,37 +0,0 @@
|
||||
function number(n) {
|
||||
if (!Number.isSafeInteger(n) || n < 0)
|
||||
throw new Error(`Wrong positive integer: ${n}`);
|
||||
}
|
||||
function bool(b) {
|
||||
if (typeof b !== 'boolean')
|
||||
throw new Error(`Expected boolean, not ${b}`);
|
||||
}
|
||||
function bytes(b, ...lengths) {
|
||||
if (!(b instanceof Uint8Array))
|
||||
throw new Error('Expected Uint8Array');
|
||||
if (lengths.length > 0 && !lengths.includes(b.length))
|
||||
throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`);
|
||||
}
|
||||
function hash(hash) {
|
||||
if (typeof hash !== 'function' || typeof hash.create !== 'function')
|
||||
throw new Error('hash must be wrapped by utils.wrapConstructor');
|
||||
number(hash.outputLen);
|
||||
number(hash.blockLen);
|
||||
}
|
||||
function exists(instance, checkFinished = true) {
|
||||
if (instance.destroyed)
|
||||
throw new Error('Hash instance has been destroyed');
|
||||
if (checkFinished && instance.finished)
|
||||
throw new Error('Hash#digest() has already been called');
|
||||
}
|
||||
function output(out, instance) {
|
||||
bytes(out);
|
||||
const min = instance.outputLen;
|
||||
if (out.length < min) {
|
||||
throw new Error(`digestInto() expects output buffer of length at least ${min}`);
|
||||
}
|
||||
}
|
||||
export { number, bool, bytes, hash, exists, output };
|
||||
const assert = { number, bool, bytes, hash, exists, output };
|
||||
export default assert;
|
||||
//# sourceMappingURL=_assert.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"_assert.js","sourceRoot":"","sources":["../src/_assert.ts"],"names":[],"mappings":"AAAA,SAAS,MAAM,CAAC,CAAS;IACvB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,EAAE,CAAC,CAAC;AACzF,CAAC;AAED,SAAS,IAAI,CAAC,CAAU;IACtB,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,KAAK,CAAC,CAAyB,EAAE,GAAG,OAAiB;IAC5D,IAAI,CAAC,CAAC,CAAC,YAAY,UAAU,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACvE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,iCAAiC,OAAO,mBAAmB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAC3F,CAAC;AAQD,SAAS,IAAI,CAAC,IAAU;IACtB,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU;QACjE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,MAAM,CAAC,QAAa,EAAE,aAAa,GAAG,IAAI;IACjD,IAAI,QAAQ,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC5E,IAAI,aAAa,IAAI,QAAQ,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;AACnG,CAAC;AACD,SAAS,MAAM,CAAC,GAAQ,EAAE,QAAa;IACrC,KAAK,CAAC,GAAG,CAAC,CAAC;IACX,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC;IAC/B,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;QACpB,MAAM,IAAI,KAAK,CAAC,yDAAyD,GAAG,EAAE,CAAC,CAAC;KACjF;AACH,CAAC;AAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACrD,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC7D,eAAe,MAAM,CAAC"}
|
||||
-281
@@ -1,281 +0,0 @@
|
||||
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
||||
// micro-noble-ciphers: more auditable, but slower version of salsa20, chacha & poly1305.
|
||||
// Implements the same algorithms that are present in other files,
|
||||
// but without unrolled loops (https://en.wikipedia.org/wiki/Loop_unrolling).
|
||||
import * as u from './utils.js';
|
||||
import { salsaBasic } from './_salsa.js';
|
||||
// Utils
|
||||
function hexToNumber(hex) {
|
||||
if (typeof hex !== 'string')
|
||||
throw new Error('hex string expected, got ' + typeof hex);
|
||||
// Big Endian
|
||||
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
||||
}
|
||||
function bytesToNumberLE(bytes) {
|
||||
return hexToNumber(u.bytesToHex(Uint8Array.from(bytes).reverse()));
|
||||
}
|
||||
function numberToBytesLE(n, len) {
|
||||
return u.hexToBytes(n.toString(16).padStart(len * 2, '0')).reverse();
|
||||
}
|
||||
const rotl = (a, b) => (a << b) | (a >>> (32 - b));
|
||||
// /Utils
|
||||
function salsaQR(x, a, b, c, d) {
|
||||
x[b] ^= rotl((x[a] + x[d]) | 0, 7);
|
||||
x[c] ^= rotl((x[b] + x[a]) | 0, 9);
|
||||
x[d] ^= rotl((x[c] + x[b]) | 0, 13);
|
||||
x[a] ^= rotl((x[d] + x[c]) | 0, 18);
|
||||
}
|
||||
// prettier-ignore
|
||||
function chachaQR(x, a, b, c, d) {
|
||||
x[a] = (x[a] + x[b]) | 0;
|
||||
x[d] = rotl(x[d] ^ x[a], 16);
|
||||
x[c] = (x[c] + x[d]) | 0;
|
||||
x[b] = rotl(x[b] ^ x[c], 12);
|
||||
x[a] = (x[a] + x[b]) | 0;
|
||||
x[d] = rotl(x[d] ^ x[a], 8);
|
||||
x[c] = (x[c] + x[d]) | 0;
|
||||
x[b] = rotl(x[b] ^ x[c], 7);
|
||||
}
|
||||
function salsaRound(x, rounds = 20) {
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
salsaQR(x, 0, 4, 8, 12);
|
||||
salsaQR(x, 5, 9, 13, 1);
|
||||
salsaQR(x, 10, 14, 2, 6);
|
||||
salsaQR(x, 15, 3, 7, 11);
|
||||
salsaQR(x, 0, 1, 2, 3);
|
||||
salsaQR(x, 5, 6, 7, 4);
|
||||
salsaQR(x, 10, 11, 8, 9);
|
||||
salsaQR(x, 15, 12, 13, 14);
|
||||
}
|
||||
}
|
||||
function chachaRound(x, rounds = 20) {
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
chachaQR(x, 0, 4, 8, 12);
|
||||
chachaQR(x, 1, 5, 9, 13);
|
||||
chachaQR(x, 2, 6, 10, 14);
|
||||
chachaQR(x, 3, 7, 11, 15);
|
||||
chachaQR(x, 0, 5, 10, 15);
|
||||
chachaQR(x, 1, 6, 11, 12);
|
||||
chachaQR(x, 2, 7, 8, 13);
|
||||
chachaQR(x, 3, 4, 9, 14);
|
||||
}
|
||||
}
|
||||
function salsaCore(c, k, n, out, cnt, rounds = 20) {
|
||||
// prettier-ignore
|
||||
const y = new Uint32Array([
|
||||
c[0], k[0], k[1], k[2],
|
||||
k[3], c[1], n[0], n[1],
|
||||
cnt, 0, c[2], k[4],
|
||||
k[5], k[6], k[7], c[3], // Key Key Key "te k"
|
||||
]);
|
||||
const x = y.slice();
|
||||
salsaRound(x, rounds);
|
||||
for (let i = 0; i < 16; i++)
|
||||
out[i] = (y[i] + x[i]) | 0;
|
||||
}
|
||||
export function hsalsa(c, key, nonce) {
|
||||
const k = u.u32(key);
|
||||
const i = u.u32(nonce);
|
||||
// prettier-ignore
|
||||
const x = new Uint32Array([
|
||||
c[0], k[0], k[1], k[2],
|
||||
k[3], c[1], i[0], i[1],
|
||||
i[2], i[3], c[2], k[4],
|
||||
k[5], k[6], k[7], c[3]
|
||||
]);
|
||||
salsaRound(x);
|
||||
return u.u8(new Uint32Array([x[0], x[5], x[10], x[15], x[6], x[7], x[8], x[9]]));
|
||||
}
|
||||
function chachaCore(c, k, n, out, cnt, rounds = 20) {
|
||||
// prettier-ignore
|
||||
const y = new Uint32Array([
|
||||
c[0], c[1], c[2], c[3],
|
||||
k[0], k[1], k[2], k[3],
|
||||
k[4], k[5], k[6], k[7],
|
||||
cnt, n[0], n[1], n[2], // Counter Counter Nonce Nonce
|
||||
]);
|
||||
const x = y.slice();
|
||||
chachaRound(x, rounds);
|
||||
for (let i = 0; i < 16; i++)
|
||||
out[i] = (y[i] + x[i]) | 0;
|
||||
}
|
||||
export function hchacha(c, key, nonce) {
|
||||
const k = u.u32(key);
|
||||
const i = u.u32(nonce);
|
||||
// prettier-ignore
|
||||
const x = new Uint32Array([
|
||||
c[0], c[1], c[2], c[3],
|
||||
k[0], k[1], k[2], k[3],
|
||||
k[4], k[5], k[6], k[7],
|
||||
i[0], i[1], i[2], i[3],
|
||||
]);
|
||||
chachaRound(x);
|
||||
return u.u8(new Uint32Array([x[0], x[1], x[2], x[3], x[12], x[13], x[14], x[15]]));
|
||||
}
|
||||
/**
|
||||
* salsa20, 12-byte nonce.
|
||||
*/
|
||||
export const salsa20 = salsaBasic({ core: salsaCore, counterRight: true });
|
||||
/**
|
||||
* xsalsa20, 24-byte nonce.
|
||||
*/
|
||||
export const xsalsa20 = salsaBasic({
|
||||
core: salsaCore,
|
||||
counterRight: true,
|
||||
extendNonceFn: hsalsa,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* chacha20 non-RFC, original version by djb. 8-byte nonce, 8-byte counter.
|
||||
*/
|
||||
export const chacha20orig = salsaBasic({ core: chachaCore, counterRight: false, counterLen: 8 });
|
||||
/**
|
||||
* chacha20 RFC 8439 (IETF / TLS). 12-byte nonce, 4-byte counter.
|
||||
*/
|
||||
export const chacha20 = salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* xchacha20 eXtended-nonce. https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
*/
|
||||
export const xchacha20 = salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 8,
|
||||
extendNonceFn: hchacha,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* 8-round chacha from the original paper.
|
||||
*/
|
||||
export const chacha8 = salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 8,
|
||||
});
|
||||
/**
|
||||
* 12-round chacha from the original paper.
|
||||
*/
|
||||
export const chacha12 = salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 12,
|
||||
});
|
||||
const POW_2_130_5 = 2n ** 130n - 5n;
|
||||
const POW_2_128_1 = 2n ** (16n * 8n) - 1n;
|
||||
// Can be speed-up using BigUint64Array, but would be more complicated
|
||||
export function poly1305(msg, key) {
|
||||
u.ensureBytes(msg);
|
||||
u.ensureBytes(key);
|
||||
let acc = 0n;
|
||||
const r = bytesToNumberLE(key.subarray(0, 16)) & 0x0ffffffc0ffffffc0ffffffc0fffffffn;
|
||||
const s = bytesToNumberLE(key.subarray(16));
|
||||
// Process by 16 byte chunks
|
||||
for (let i = 0; i < msg.length; i += 16) {
|
||||
const m = msg.subarray(i, i + 16);
|
||||
const n = bytesToNumberLE(m) | (1n << BigInt(8 * m.length));
|
||||
acc = ((acc + n) * r) % POW_2_130_5;
|
||||
}
|
||||
const res = (acc + s) & POW_2_128_1;
|
||||
return numberToBytesLE(res, 16);
|
||||
}
|
||||
function computeTag(fn, key, nonce, ciphertext, AAD) {
|
||||
const res = [];
|
||||
if (AAD) {
|
||||
res.push(AAD);
|
||||
const leftover = AAD.length % 16;
|
||||
if (leftover > 0)
|
||||
res.push(new Uint8Array(16 - leftover));
|
||||
}
|
||||
res.push(ciphertext);
|
||||
const leftover = ciphertext.length % 16;
|
||||
if (leftover > 0)
|
||||
res.push(new Uint8Array(16 - leftover));
|
||||
// Lengths
|
||||
const num = new Uint8Array(16);
|
||||
const view = u.createView(num);
|
||||
u.setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
|
||||
u.setBigUint64(view, 8, BigInt(ciphertext.length), true);
|
||||
res.push(num);
|
||||
const authKey = fn(key, nonce, new Uint8Array(32));
|
||||
return poly1305(u.concatBytes(...res), authKey);
|
||||
}
|
||||
/**
|
||||
* xsalsa20-poly1305 eXtended-nonce (24 bytes) salsa.
|
||||
*/
|
||||
export function xsalsa20poly1305(key, nonce) {
|
||||
u.ensureBytes(key);
|
||||
u.ensureBytes(nonce);
|
||||
return {
|
||||
encrypt: (plaintext) => {
|
||||
u.ensureBytes(plaintext);
|
||||
const m = u.concatBytes(new Uint8Array(32), plaintext);
|
||||
const c = xsalsa20(key, nonce, m);
|
||||
const authKey = c.subarray(0, 32);
|
||||
const data = c.subarray(32);
|
||||
const tag = poly1305(data, authKey);
|
||||
return u.concatBytes(tag, data);
|
||||
},
|
||||
decrypt: (ciphertext) => {
|
||||
u.ensureBytes(ciphertext);
|
||||
if (ciphertext.length < 16)
|
||||
throw new Error('encrypted data must be at least 16 bytes');
|
||||
const c = u.concatBytes(new Uint8Array(16), ciphertext);
|
||||
const authKey = xsalsa20(key, nonce, new Uint8Array(32));
|
||||
const tag = poly1305(c.subarray(32), authKey);
|
||||
if (!u.equalBytes(c.subarray(16, 32), tag))
|
||||
throw new Error('invalid poly1305 tag');
|
||||
return xsalsa20(key, nonce, c).subarray(32);
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Alias to xsalsa20-poly1305
|
||||
*/
|
||||
export function secretbox(key, nonce) {
|
||||
u.ensureBytes(key);
|
||||
u.ensureBytes(nonce);
|
||||
const xs = xsalsa20poly1305(key, nonce);
|
||||
return { seal: xs.encrypt, open: xs.decrypt };
|
||||
}
|
||||
export const _poly1305_aead = (fn) => (key, nonce, AAD) => {
|
||||
const tagLength = 16;
|
||||
const keyLength = 32;
|
||||
u.ensureBytes(key, keyLength);
|
||||
u.ensureBytes(nonce);
|
||||
return {
|
||||
tagLength,
|
||||
encrypt: (plaintext) => {
|
||||
u.ensureBytes(plaintext);
|
||||
const res = fn(key, nonce, plaintext, undefined, 1);
|
||||
const tag = computeTag(fn, key, nonce, res, AAD);
|
||||
return u.concatBytes(res, tag);
|
||||
},
|
||||
decrypt: (ciphertext) => {
|
||||
u.ensureBytes(ciphertext);
|
||||
if (ciphertext.length < tagLength)
|
||||
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
|
||||
const passedTag = ciphertext.subarray(-tagLength);
|
||||
const data = ciphertext.subarray(0, -tagLength);
|
||||
const tag = computeTag(fn, key, nonce, data, AAD);
|
||||
if (!u.equalBytes(passedTag, tag))
|
||||
throw new Error('invalid poly1305 tag');
|
||||
return fn(key, nonce, data, undefined, 1);
|
||||
},
|
||||
};
|
||||
};
|
||||
/**
|
||||
* chacha20-poly1305 12-byte-nonce chacha.
|
||||
*/
|
||||
export const chacha20poly1305 = _poly1305_aead(chacha20);
|
||||
/**
|
||||
* xchacha20-poly1305 eXtended-nonce (24 bytes) chacha.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
export const xchacha20poly1305 = _poly1305_aead(xchacha20);
|
||||
//# sourceMappingURL=_micro.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-264
@@ -1,264 +0,0 @@
|
||||
import { toBytes, ensureBytes } from './utils.js';
|
||||
import assert from './_assert.js';
|
||||
// Poly1305 is a fast and parallel secret-key message-authentication code.
|
||||
// https://cr.yp.to/mac.html, https://cr.yp.to/mac/poly1305-20050329.pdf
|
||||
// https://datatracker.ietf.org/doc/html/rfc8439
|
||||
// Based on Public Domain poly1305-donna https://github.com/floodyberry/poly1305-donna
|
||||
const u8to16 = (a, i) => (a[i++] & 0xff) | ((a[i++] & 0xff) << 8);
|
||||
class Poly1305 {
|
||||
constructor(key) {
|
||||
this.blockLen = 16;
|
||||
this.outputLen = 16;
|
||||
this.buffer = new Uint8Array(16);
|
||||
this.r = new Uint16Array(10);
|
||||
this.h = new Uint16Array(10);
|
||||
this.pad = new Uint16Array(8);
|
||||
this.pos = 0;
|
||||
this.finished = false;
|
||||
key = toBytes(key);
|
||||
ensureBytes(key, 32);
|
||||
const t0 = u8to16(key, 0);
|
||||
const t1 = u8to16(key, 2);
|
||||
const t2 = u8to16(key, 4);
|
||||
const t3 = u8to16(key, 6);
|
||||
const t4 = u8to16(key, 8);
|
||||
const t5 = u8to16(key, 10);
|
||||
const t6 = u8to16(key, 12);
|
||||
const t7 = u8to16(key, 14);
|
||||
// https://github.com/floodyberry/poly1305-donna/blob/e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781/poly1305-donna-16.h#L47
|
||||
this.r[0] = t0 & 0x1fff;
|
||||
this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
|
||||
this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
|
||||
this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
|
||||
this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
|
||||
this.r[5] = (t4 >>> 1) & 0x1ffe;
|
||||
this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
|
||||
this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
|
||||
this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
|
||||
this.r[9] = (t7 >>> 5) & 0x007f;
|
||||
for (let i = 0; i < 8; i++)
|
||||
this.pad[i] = u8to16(key, 16 + 2 * i);
|
||||
}
|
||||
process(data, offset, isLast = false) {
|
||||
const hibit = isLast ? 0 : 1 << 11;
|
||||
const { h, r } = this;
|
||||
const r0 = r[0];
|
||||
const r1 = r[1];
|
||||
const r2 = r[2];
|
||||
const r3 = r[3];
|
||||
const r4 = r[4];
|
||||
const r5 = r[5];
|
||||
const r6 = r[6];
|
||||
const r7 = r[7];
|
||||
const r8 = r[8];
|
||||
const r9 = r[9];
|
||||
const t0 = u8to16(data, offset + 0);
|
||||
const t1 = u8to16(data, offset + 2);
|
||||
const t2 = u8to16(data, offset + 4);
|
||||
const t3 = u8to16(data, offset + 6);
|
||||
const t4 = u8to16(data, offset + 8);
|
||||
const t5 = u8to16(data, offset + 10);
|
||||
const t6 = u8to16(data, offset + 12);
|
||||
const t7 = u8to16(data, offset + 14);
|
||||
let h0 = h[0] + (t0 & 0x1fff);
|
||||
let h1 = h[1] + (((t0 >>> 13) | (t1 << 3)) & 0x1fff);
|
||||
let h2 = h[2] + (((t1 >>> 10) | (t2 << 6)) & 0x1fff);
|
||||
let h3 = h[3] + (((t2 >>> 7) | (t3 << 9)) & 0x1fff);
|
||||
let h4 = h[4] + (((t3 >>> 4) | (t4 << 12)) & 0x1fff);
|
||||
let h5 = h[5] + ((t4 >>> 1) & 0x1fff);
|
||||
let h6 = h[6] + (((t4 >>> 14) | (t5 << 2)) & 0x1fff);
|
||||
let h7 = h[7] + (((t5 >>> 11) | (t6 << 5)) & 0x1fff);
|
||||
let h8 = h[8] + (((t6 >>> 8) | (t7 << 8)) & 0x1fff);
|
||||
let h9 = h[9] + ((t7 >>> 5) | hibit);
|
||||
let c = 0;
|
||||
let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);
|
||||
c = d0 >>> 13;
|
||||
d0 &= 0x1fff;
|
||||
d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);
|
||||
c += d0 >>> 13;
|
||||
d0 &= 0x1fff;
|
||||
let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);
|
||||
c = d1 >>> 13;
|
||||
d1 &= 0x1fff;
|
||||
d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);
|
||||
c += d1 >>> 13;
|
||||
d1 &= 0x1fff;
|
||||
let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);
|
||||
c = d2 >>> 13;
|
||||
d2 &= 0x1fff;
|
||||
d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);
|
||||
c += d2 >>> 13;
|
||||
d2 &= 0x1fff;
|
||||
let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);
|
||||
c = d3 >>> 13;
|
||||
d3 &= 0x1fff;
|
||||
d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);
|
||||
c += d3 >>> 13;
|
||||
d3 &= 0x1fff;
|
||||
let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;
|
||||
c = d4 >>> 13;
|
||||
d4 &= 0x1fff;
|
||||
d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);
|
||||
c += d4 >>> 13;
|
||||
d4 &= 0x1fff;
|
||||
let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;
|
||||
c = d5 >>> 13;
|
||||
d5 &= 0x1fff;
|
||||
d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);
|
||||
c += d5 >>> 13;
|
||||
d5 &= 0x1fff;
|
||||
let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;
|
||||
c = d6 >>> 13;
|
||||
d6 &= 0x1fff;
|
||||
d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);
|
||||
c += d6 >>> 13;
|
||||
d6 &= 0x1fff;
|
||||
let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;
|
||||
c = d7 >>> 13;
|
||||
d7 &= 0x1fff;
|
||||
d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);
|
||||
c += d7 >>> 13;
|
||||
d7 &= 0x1fff;
|
||||
let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;
|
||||
c = d8 >>> 13;
|
||||
d8 &= 0x1fff;
|
||||
d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);
|
||||
c += d8 >>> 13;
|
||||
d8 &= 0x1fff;
|
||||
let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;
|
||||
c = d9 >>> 13;
|
||||
d9 &= 0x1fff;
|
||||
d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;
|
||||
c += d9 >>> 13;
|
||||
d9 &= 0x1fff;
|
||||
c = ((c << 2) + c) | 0;
|
||||
c = (c + d0) | 0;
|
||||
d0 = c & 0x1fff;
|
||||
c = c >>> 13;
|
||||
d1 += c;
|
||||
h[0] = d0;
|
||||
h[1] = d1;
|
||||
h[2] = d2;
|
||||
h[3] = d3;
|
||||
h[4] = d4;
|
||||
h[5] = d5;
|
||||
h[6] = d6;
|
||||
h[7] = d7;
|
||||
h[8] = d8;
|
||||
h[9] = d9;
|
||||
}
|
||||
finalize() {
|
||||
const { h, pad } = this;
|
||||
const g = new Uint16Array(10);
|
||||
let c = h[1] >>> 13;
|
||||
h[1] &= 0x1fff;
|
||||
for (let i = 2; i < 10; i++) {
|
||||
h[i] += c;
|
||||
c = h[i] >>> 13;
|
||||
h[i] &= 0x1fff;
|
||||
}
|
||||
h[0] += c * 5;
|
||||
c = h[0] >>> 13;
|
||||
h[0] &= 0x1fff;
|
||||
h[1] += c;
|
||||
c = h[1] >>> 13;
|
||||
h[1] &= 0x1fff;
|
||||
h[2] += c;
|
||||
g[0] = h[0] + 5;
|
||||
c = g[0] >>> 13;
|
||||
g[0] &= 0x1fff;
|
||||
for (let i = 1; i < 10; i++) {
|
||||
g[i] = h[i] + c;
|
||||
c = g[i] >>> 13;
|
||||
g[i] &= 0x1fff;
|
||||
}
|
||||
g[9] -= 1 << 13;
|
||||
let mask = (c ^ 1) - 1;
|
||||
for (let i = 0; i < 10; i++)
|
||||
g[i] &= mask;
|
||||
mask = ~mask;
|
||||
for (let i = 0; i < 10; i++)
|
||||
h[i] = (h[i] & mask) | g[i];
|
||||
h[0] = (h[0] | (h[1] << 13)) & 0xffff;
|
||||
h[1] = ((h[1] >>> 3) | (h[2] << 10)) & 0xffff;
|
||||
h[2] = ((h[2] >>> 6) | (h[3] << 7)) & 0xffff;
|
||||
h[3] = ((h[3] >>> 9) | (h[4] << 4)) & 0xffff;
|
||||
h[4] = ((h[4] >>> 12) | (h[5] << 1) | (h[6] << 14)) & 0xffff;
|
||||
h[5] = ((h[6] >>> 2) | (h[7] << 11)) & 0xffff;
|
||||
h[6] = ((h[7] >>> 5) | (h[8] << 8)) & 0xffff;
|
||||
h[7] = ((h[8] >>> 8) | (h[9] << 5)) & 0xffff;
|
||||
let f = h[0] + pad[0];
|
||||
h[0] = f & 0xffff;
|
||||
for (let i = 1; i < 8; i++) {
|
||||
f = (((h[i] + pad[i]) | 0) + (f >>> 16)) | 0;
|
||||
h[i] = f & 0xffff;
|
||||
}
|
||||
}
|
||||
update(data) {
|
||||
assert.exists(this);
|
||||
const { buffer, blockLen } = this;
|
||||
data = toBytes(data);
|
||||
const len = data.length;
|
||||
for (let pos = 0; pos < len;) {
|
||||
const take = Math.min(blockLen - this.pos, len - pos);
|
||||
// Fast path: we have at least one block in input
|
||||
if (take === blockLen) {
|
||||
for (; blockLen <= len - pos; pos += blockLen)
|
||||
this.process(data, pos);
|
||||
continue;
|
||||
}
|
||||
buffer.set(data.subarray(pos, pos + take), this.pos);
|
||||
this.pos += take;
|
||||
pos += take;
|
||||
if (this.pos === blockLen) {
|
||||
this.process(buffer, 0, false);
|
||||
this.pos = 0;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
destroy() {
|
||||
this.h.fill(0);
|
||||
this.r.fill(0);
|
||||
this.buffer.fill(0);
|
||||
this.pad.fill(0);
|
||||
}
|
||||
digestInto(out) {
|
||||
assert.exists(this);
|
||||
assert.output(out, this);
|
||||
this.finished = true;
|
||||
const { buffer, h } = this;
|
||||
let { pos } = this;
|
||||
if (pos) {
|
||||
buffer[pos++] = 1;
|
||||
// buffer.subarray(pos).fill(0);
|
||||
for (; pos < 16; pos++)
|
||||
buffer[pos] = 0;
|
||||
this.process(buffer, 0, true);
|
||||
}
|
||||
this.finalize();
|
||||
let opos = 0;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
out[opos++] = h[i] >>> 0;
|
||||
out[opos++] = h[i] >>> 8;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
digest() {
|
||||
const { buffer, outputLen } = this;
|
||||
this.digestInto(buffer);
|
||||
const res = buffer.slice(0, outputLen);
|
||||
this.destroy();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
export function wrapConstructorWithKey(hashCons) {
|
||||
const hashC = (msg, key) => hashCons(key).update(toBytes(msg)).digest();
|
||||
const tmp = hashCons(new Uint8Array(32));
|
||||
hashC.outputLen = tmp.outputLen;
|
||||
hashC.blockLen = tmp.blockLen;
|
||||
hashC.create = (key) => hashCons(key);
|
||||
return hashC;
|
||||
}
|
||||
export const poly1305 = wrapConstructorWithKey((key) => new Poly1305(key));
|
||||
//# sourceMappingURL=_poly1305.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-100
@@ -1,100 +0,0 @@
|
||||
import { u8, u32, ensureBytes } from './utils.js';
|
||||
// AES-SIV polyval, little-endian "mirror image" of AES-GCM GHash
|
||||
// polynomial hash function. Defined in RFC 8452.
|
||||
// Reverse bits in u32, constant-time, precompute will be faster, but non-constant time
|
||||
function rev32(x) {
|
||||
x = ((x & 1431655765) << 1) | ((x >>> 1) & 1431655765);
|
||||
x = ((x & 858993459) << 2) | ((x >>> 2) & 858993459);
|
||||
x = ((x & 252645135) << 4) | ((x >>> 4) & 252645135);
|
||||
x = ((x & 16711935) << 8) | ((x >>> 8) & 16711935);
|
||||
return (x << 16) | (x >>> 16);
|
||||
}
|
||||
// wrapped 32 bit multiplication
|
||||
const wrapMul = (a, b) => Math.imul(a, b) >>> 0;
|
||||
// https://timtaubert.de/blog/2017/06/verified-binary-multiplication-for-ghash/
|
||||
function bmul32(x, y) {
|
||||
const x0 = x & 286331153;
|
||||
const x1 = x & 572662306;
|
||||
const x2 = x & 1145324612;
|
||||
const x3 = x & 2290649224;
|
||||
const y0 = y & 286331153;
|
||||
const y1 = y & 572662306;
|
||||
const y2 = y & 1145324612;
|
||||
const y3 = y & 2290649224;
|
||||
let res = (wrapMul(x0, y0) ^ wrapMul(x1, y3) ^ wrapMul(x2, y2) ^ wrapMul(x3, y1)) & 286331153;
|
||||
res |= (wrapMul(x0, y1) ^ wrapMul(x1, y0) ^ wrapMul(x2, y3) ^ wrapMul(x3, y2)) & 572662306;
|
||||
res |= (wrapMul(x0, y2) ^ wrapMul(x1, y1) ^ wrapMul(x2, y0) ^ wrapMul(x3, y3)) & 1145324612;
|
||||
res |= (wrapMul(x0, y3) ^ wrapMul(x1, y2) ^ wrapMul(x2, y1) ^ wrapMul(x3, y0)) & 2290649224;
|
||||
return res >>> 0;
|
||||
}
|
||||
function mulPart(arr) {
|
||||
const a = new Uint32Array(18);
|
||||
a[0] = arr[0];
|
||||
a[1] = arr[1];
|
||||
a[2] = arr[2];
|
||||
a[3] = arr[3];
|
||||
a[4] = a[0] ^ a[1];
|
||||
a[5] = a[2] ^ a[3];
|
||||
a[6] = a[0] ^ a[2];
|
||||
a[7] = a[1] ^ a[3];
|
||||
a[8] = a[6] ^ a[7];
|
||||
a[9] = rev32(arr[0]);
|
||||
a[10] = rev32(arr[1]);
|
||||
a[11] = rev32(arr[2]);
|
||||
a[12] = rev32(arr[3]);
|
||||
a[13] = a[9] ^ a[10];
|
||||
a[14] = a[11] ^ a[12];
|
||||
a[15] = a[9] ^ a[11];
|
||||
a[16] = a[10] ^ a[12];
|
||||
a[17] = a[15] ^ a[16];
|
||||
return a;
|
||||
}
|
||||
export function polyval(h, data) {
|
||||
ensureBytes(h);
|
||||
ensureBytes(data);
|
||||
const s = new Uint32Array(4);
|
||||
// Precompute for multiplication
|
||||
const a = mulPart(u32(h));
|
||||
if (data.length % 16)
|
||||
throw new Error('polyval: data must be padded to 16 bytes');
|
||||
const data32 = u32(data);
|
||||
for (let i = 0; i < data32.length; i += 4) {
|
||||
// Xor
|
||||
s[0] ^= data32[i + 0];
|
||||
s[1] ^= data32[i + 1];
|
||||
s[2] ^= data32[i + 2];
|
||||
s[3] ^= data32[i + 3];
|
||||
// Dot via Karatsuba multiplication, based on MIT-licensed
|
||||
// https://bearssl.org/gitweb/?p=BearSSL;a=blob;f=src/hash/ghash_ctmul32.c;hb=4b6046412
|
||||
const b = mulPart(s);
|
||||
const c = new Uint32Array(18);
|
||||
for (let i = 0; i < 18; i++)
|
||||
c[i] = bmul32(a[i], b[i]);
|
||||
c[4] ^= c[0] ^ c[1];
|
||||
c[5] ^= c[2] ^ c[3];
|
||||
c[8] ^= c[6] ^ c[7];
|
||||
c[13] ^= c[9] ^ c[10];
|
||||
c[14] ^= c[11] ^ c[12];
|
||||
c[17] ^= c[15] ^ c[16];
|
||||
const zw = new Uint32Array(8);
|
||||
zw[0] = c[0];
|
||||
zw[1] = c[4] ^ (rev32(c[9]) >>> 1);
|
||||
zw[2] = c[1] ^ c[0] ^ c[2] ^ c[6] ^ (rev32(c[13]) >>> 1);
|
||||
zw[3] = c[4] ^ c[5] ^ c[8] ^ (rev32(c[10] ^ c[9] ^ c[11] ^ c[15]) >>> 1);
|
||||
zw[4] = c[2] ^ c[1] ^ c[3] ^ c[7] ^ (rev32(c[13] ^ c[14] ^ c[17]) >>> 1);
|
||||
zw[5] = c[5] ^ (rev32(c[11] ^ c[10] ^ c[12] ^ c[16]) >>> 1);
|
||||
zw[6] = c[3] ^ (rev32(c[14]) >>> 1);
|
||||
zw[7] = rev32(c[12]) >>> 1;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const lw = zw[i];
|
||||
zw[i + 4] ^= lw ^ (lw >>> 1) ^ (lw >>> 2) ^ (lw >>> 7);
|
||||
zw[i + 3] ^= (lw << 31) ^ (lw << 30) ^ (lw << 25);
|
||||
}
|
||||
s[0] = zw[4];
|
||||
s[1] = zw[5];
|
||||
s[2] = zw[6];
|
||||
s[3] = zw[7];
|
||||
}
|
||||
return u8(s);
|
||||
}
|
||||
//# sourceMappingURL=_polyval.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-154
@@ -1,154 +0,0 @@
|
||||
// Basic utils for salsa-like ciphers
|
||||
// Check out _micro.ts for descriptive documentation.
|
||||
import assert from './_assert.js';
|
||||
import { u32, utf8ToBytes, checkOpts } from './utils.js';
|
||||
/*
|
||||
RFC8439 requires multi-step cipher stream, where
|
||||
authKey starts with counter: 0, actual msg with counter: 1.
|
||||
|
||||
For this, we need a way to re-use nonce / counter:
|
||||
|
||||
const counter = new Uint8Array(4);
|
||||
chacha(..., counter, ...); // counter is now 1
|
||||
chacha(..., counter, ...); // counter is now 2
|
||||
|
||||
This is complicated:
|
||||
|
||||
- Original papers don't allow mutating counters
|
||||
- Counter overflow is undefined: https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/
|
||||
- 3rd-party library stablelib implementation uses an approach where you can provide
|
||||
nonce and counter instead of just nonce - and it will re-use it
|
||||
- We could have did something similar, but ChaCha has different counter position
|
||||
(counter | nonce), which is not composable with XChaCha, because full counter
|
||||
is (nonce16 | counter | nonce16). Stablelib doesn't support in-place counter for XChaCha.
|
||||
- We could separate nonce & counter and provide separate API for counter re-use, but
|
||||
there are different counter sizes depending on an algorithm.
|
||||
- Salsa & ChaCha also differ in structures of key / sigma:
|
||||
|
||||
salsa: c0 | k(4) | c1 | nonce(2) | ctr(2) | c2 | k(4) | c4
|
||||
chacha: c(4) | k(8) | ctr(1) | nonce(3)
|
||||
chachaDJB: c(4) | k(8) | ctr(2) | nonce(2)
|
||||
- Creating function such as `setSalsaState(key, nonce, sigma, data)` won't work,
|
||||
because we can't re-use counter array
|
||||
- 32-bit nonce is `2 ** 32 * 64` = 256GB with 32-bit counter
|
||||
- JS does not allow UintArrays bigger than 4GB, so supporting 64-bit counters doesn't matter
|
||||
|
||||
Structure is as following:
|
||||
|
||||
key=16 -> sigma16, k=key|key
|
||||
key=32 -> sigma32, k=key
|
||||
|
||||
nonces:
|
||||
salsa20: 8 (8-byte counter)
|
||||
chacha20djb: 8 (8-byte counter)
|
||||
chacha20tls: 12 (4-byte counter)
|
||||
xsalsa: 24 (16 -> hsalsa, 8 -> old nonce)
|
||||
xchacha: 24 (16 -> hchacha, 8 -> old nonce)
|
||||
|
||||
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2
|
||||
Use the subkey and remaining 8 byte nonce with ChaCha20 as normal
|
||||
(prefixed by 4 NUL bytes, since [RFC8439] specifies a 12-byte nonce).
|
||||
*/
|
||||
const sigma16 = utf8ToBytes('expand 16-byte k');
|
||||
const sigma32 = utf8ToBytes('expand 32-byte k');
|
||||
const sigma16_32 = u32(sigma16);
|
||||
const sigma32_32 = u32(sigma32);
|
||||
// Is byte array aligned to 4 byte offset (u32)?
|
||||
const isAligned32 = (b) => !(b.byteOffset % 4);
|
||||
export const salsaBasic = (opts) => {
|
||||
const { core, rounds, counterRight, counterLen, allow128bitKeys, extendNonceFn, blockLen } = checkOpts({ rounds: 20, counterRight: false, counterLen: 8, allow128bitKeys: true, blockLen: 64 }, opts);
|
||||
assert.number(counterLen);
|
||||
assert.number(rounds);
|
||||
assert.number(blockLen);
|
||||
assert.bool(counterRight);
|
||||
assert.bool(allow128bitKeys);
|
||||
const blockLen32 = blockLen / 4;
|
||||
if (blockLen % 4 !== 0)
|
||||
throw new Error('Salsa/ChaCha: blockLen must be aligned to 4 bytes');
|
||||
return (key, nonce, data, output, counter = 0) => {
|
||||
assert.bytes(key);
|
||||
assert.bytes(nonce);
|
||||
assert.bytes(data);
|
||||
if (!output)
|
||||
output = new Uint8Array(data.length);
|
||||
assert.bytes(output);
|
||||
assert.number(counter);
|
||||
// > new Uint32Array([2**32])
|
||||
// Uint32Array(1) [ 0 ]
|
||||
// > new Uint32Array([2**32-1])
|
||||
// Uint32Array(1) [ 4294967295 ]
|
||||
if (counter < 0 || counter >= 2 ** 32 - 1)
|
||||
throw new Error('Salsa/ChaCha: counter overflow');
|
||||
if (output.length < data.length) {
|
||||
throw new Error(`Salsa/ChaCha: output (${output.length}) is shorter than data (${data.length})`);
|
||||
}
|
||||
const toClean = [];
|
||||
let k, sigma;
|
||||
// Handle 128 byte keys
|
||||
if (key.length === 32) {
|
||||
k = key;
|
||||
sigma = sigma32_32;
|
||||
}
|
||||
else if (key.length === 16 && allow128bitKeys) {
|
||||
k = new Uint8Array(32);
|
||||
k.set(key);
|
||||
k.set(key, 16);
|
||||
sigma = sigma16_32;
|
||||
toClean.push(k);
|
||||
}
|
||||
else
|
||||
throw new Error(`Salsa/ChaCha: invalid 32-byte key, got length=${key.length}`);
|
||||
// Handle extended nonce (HChaCha/HSalsa)
|
||||
if (extendNonceFn) {
|
||||
if (nonce.length <= 16)
|
||||
throw new Error(`Salsa/ChaCha: extended nonce must be bigger than 16 bytes`);
|
||||
k = extendNonceFn(sigma, k, nonce.subarray(0, 16), new Uint8Array(32));
|
||||
toClean.push(k);
|
||||
nonce = nonce.subarray(16);
|
||||
}
|
||||
// Handle nonce counter
|
||||
const nonceLen = 16 - counterLen;
|
||||
if (nonce.length !== nonceLen)
|
||||
throw new Error(`Salsa/ChaCha: nonce must be ${nonceLen} or 16 bytes`);
|
||||
// Pad counter when nonce is 64 bit
|
||||
if (nonceLen !== 12) {
|
||||
const nc = new Uint8Array(12);
|
||||
nc.set(nonce, counterRight ? 0 : 12 - nonce.length);
|
||||
toClean.push((nonce = nc));
|
||||
}
|
||||
// Counter positions
|
||||
const block = new Uint8Array(blockLen);
|
||||
// Cast to Uint32Array for speed
|
||||
const b32 = u32(block);
|
||||
const k32 = u32(k);
|
||||
const n32 = u32(nonce);
|
||||
// Make sure that buffers aligned to 4 bytes
|
||||
const d32 = isAligned32(data) && u32(data);
|
||||
const o32 = isAligned32(output) && u32(output);
|
||||
toClean.push(b32);
|
||||
const len = data.length;
|
||||
for (let pos = 0, ctr = counter; pos < len; ctr++) {
|
||||
core(sigma, k32, n32, b32, ctr, rounds);
|
||||
if (ctr >= 2 ** 32 - 1)
|
||||
throw new Error('Salsa/ChaCha: counter overflow');
|
||||
const take = Math.min(blockLen, len - pos);
|
||||
// full block && aligned to 4 bytes
|
||||
if (take === blockLen && o32 && d32) {
|
||||
const pos32 = pos / 4;
|
||||
if (pos % 4 !== 0)
|
||||
throw new Error('Salsa/ChaCha: invalid block position');
|
||||
for (let j = 0; j < blockLen32; j++)
|
||||
o32[pos32 + j] = d32[pos32 + j] ^ b32[j];
|
||||
pos += blockLen;
|
||||
continue;
|
||||
}
|
||||
for (let j = 0; j < take; j++)
|
||||
output[pos + j] = data[pos + j] ^ block[j];
|
||||
pos += take;
|
||||
}
|
||||
for (let i = 0; i < toClean.length; i++)
|
||||
toClean[i].fill(0);
|
||||
return output;
|
||||
};
|
||||
};
|
||||
//# sourceMappingURL=_salsa.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"_salsa.js","sourceRoot":"","sources":["../src/_salsa.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,qDAAqD;AACrD,OAAO,MAAM,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CE;AAEF,MAAM,OAAO,GAAG,WAAW,CAAC,kBAAkB,CAAC,CAAC;AAChD,MAAM,OAAO,GAAG,WAAW,CAAC,kBAAkB,CAAC,CAAC;AAChD,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;AAChC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;AAmBhC,gDAAgD;AAChD,MAAM,WAAW,GAAG,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;AAE3D,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAe,EAAE,EAAE;IAC5C,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,GACxF,SAAS,CACP,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EACvF,IAAI,CACL,CAAC;IACJ,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC1B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACtB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxB,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1B,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC7B,MAAM,UAAU,GAAG,QAAQ,GAAG,CAAC,CAAC;IAChC,IAAI,QAAQ,GAAG,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IAC7F,OAAO,CACL,GAAe,EACf,KAAiB,EACjB,IAAgB,EAChB,MAAmB,EACnB,OAAO,GAAG,CAAC,EACC,EAAE;QACd,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClB,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnB,IAAI,CAAC,MAAM;YAAE,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACvB,6BAA6B;QAC7B,uBAAuB;QACvB,+BAA+B;QAC/B,gCAAgC;QAChC,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC7F,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE;YAC/B,MAAM,IAAI,KAAK,CACb,yBAAyB,MAAM,CAAC,MAAM,2BAA2B,IAAI,CAAC,MAAM,GAAG,CAChF,CAAC;SACH;QACD,MAAM,OAAO,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,EAAE,KAAK,CAAC;QACb,uBAAuB;QACvB,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE;YACrB,CAAC,GAAG,GAAG,CAAC;YACR,KAAK,GAAG,UAAU,CAAC;SACpB;aAAM,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,IAAI,eAAe,EAAE;YAC/C,CAAC,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;YACvB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACX,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACf,KAAK,GAAG,UAAU,CAAC;YACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACjB;;YAAM,MAAM,IAAI,KAAK,CAAC,iDAAiD,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACtF,yCAAyC;QACzC,IAAI,aAAa,EAAE;YACjB,IAAI,KAAK,CAAC,MAAM,IAAI,EAAE;gBACpB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;YAC/E,CAAC,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;YACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChB,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;SAC5B;QACD,uBAAuB;QACvB,MAAM,QAAQ,GAAG,EAAE,GAAG,UAAU,CAAC;QACjC,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ;YAC3B,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,cAAc,CAAC,CAAC;QACzE,mCAAmC;QACnC,IAAI,QAAQ,KAAK,EAAE,EAAE;YACnB,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;YAC9B,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;YACpD,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;SAC5B;QACD,oBAAoB;QACpB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;QACvC,gCAAgC;QAChC,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QACvB,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACnB,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QACvB,4CAA4C;QAC5C,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC;QACxB,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,EAAE;YACjD,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;YACxC,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;YAC3C,mCAAmC;YACnC,IAAI,IAAI,KAAK,QAAQ,IAAI,GAAG,IAAI,GAAG,EAAE;gBACnC,MAAM,KAAK,GAAG,GAAG,GAAG,CAAC,CAAC;gBACtB,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;gBAC3E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE;oBAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC9E,GAAG,IAAI,QAAQ,CAAC;gBAChB,SAAS;aACV;YACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE;gBAAE,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1E,GAAG,IAAI,IAAI,CAAC;SACb;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5D,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC,CAAC"}
|
||||
-329
@@ -1,329 +0,0 @@
|
||||
import { createView, ensureBytes, equalBytes, setBigUint64, u32 } from './utils.js';
|
||||
import { poly1305 } from './_poly1305.js';
|
||||
import { salsaBasic } from './_salsa.js';
|
||||
// ChaCha20 stream cipher was released in 2008. ChaCha aims to increase
|
||||
// the diffusion per round, but had slightly less cryptanalysis.
|
||||
// https://cr.yp.to/chacha.html, http://cr.yp.to/chacha/chacha-20080128.pdf
|
||||
// Left rotate for uint32
|
||||
const rotl = (a, b) => (a << b) | (a >>> (32 - b));
|
||||
/**
|
||||
* ChaCha core function.
|
||||
*/
|
||||
// prettier-ignore
|
||||
function chachaCore(c, k, n, out, cnt, rounds = 20) {
|
||||
let y00 = c[0], y01 = c[1], y02 = c[2], y03 = c[3]; // "expa" "nd 3" "2-by" "te k"
|
||||
let y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3]; // Key Key Key Key
|
||||
let y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7]; // Key Key Key Key
|
||||
let y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2]; // Counter Counter Nonce Nonce
|
||||
// Save state to temporary variables
|
||||
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
||||
// Main loop
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
x00 = (x00 + x04) | 0;
|
||||
x12 = rotl(x12 ^ x00, 16);
|
||||
x08 = (x08 + x12) | 0;
|
||||
x04 = rotl(x04 ^ x08, 12);
|
||||
x00 = (x00 + x04) | 0;
|
||||
x12 = rotl(x12 ^ x00, 8);
|
||||
x08 = (x08 + x12) | 0;
|
||||
x04 = rotl(x04 ^ x08, 7);
|
||||
x01 = (x01 + x05) | 0;
|
||||
x13 = rotl(x13 ^ x01, 16);
|
||||
x09 = (x09 + x13) | 0;
|
||||
x05 = rotl(x05 ^ x09, 12);
|
||||
x01 = (x01 + x05) | 0;
|
||||
x13 = rotl(x13 ^ x01, 8);
|
||||
x09 = (x09 + x13) | 0;
|
||||
x05 = rotl(x05 ^ x09, 7);
|
||||
x02 = (x02 + x06) | 0;
|
||||
x14 = rotl(x14 ^ x02, 16);
|
||||
x10 = (x10 + x14) | 0;
|
||||
x06 = rotl(x06 ^ x10, 12);
|
||||
x02 = (x02 + x06) | 0;
|
||||
x14 = rotl(x14 ^ x02, 8);
|
||||
x10 = (x10 + x14) | 0;
|
||||
x06 = rotl(x06 ^ x10, 7);
|
||||
x03 = (x03 + x07) | 0;
|
||||
x15 = rotl(x15 ^ x03, 16);
|
||||
x11 = (x11 + x15) | 0;
|
||||
x07 = rotl(x07 ^ x11, 12);
|
||||
x03 = (x03 + x07) | 0;
|
||||
x15 = rotl(x15 ^ x03, 8);
|
||||
x11 = (x11 + x15) | 0;
|
||||
x07 = rotl(x07 ^ x11, 7);
|
||||
x00 = (x00 + x05) | 0;
|
||||
x15 = rotl(x15 ^ x00, 16);
|
||||
x10 = (x10 + x15) | 0;
|
||||
x05 = rotl(x05 ^ x10, 12);
|
||||
x00 = (x00 + x05) | 0;
|
||||
x15 = rotl(x15 ^ x00, 8);
|
||||
x10 = (x10 + x15) | 0;
|
||||
x05 = rotl(x05 ^ x10, 7);
|
||||
x01 = (x01 + x06) | 0;
|
||||
x12 = rotl(x12 ^ x01, 16);
|
||||
x11 = (x11 + x12) | 0;
|
||||
x06 = rotl(x06 ^ x11, 12);
|
||||
x01 = (x01 + x06) | 0;
|
||||
x12 = rotl(x12 ^ x01, 8);
|
||||
x11 = (x11 + x12) | 0;
|
||||
x06 = rotl(x06 ^ x11, 7);
|
||||
x02 = (x02 + x07) | 0;
|
||||
x13 = rotl(x13 ^ x02, 16);
|
||||
x08 = (x08 + x13) | 0;
|
||||
x07 = rotl(x07 ^ x08, 12);
|
||||
x02 = (x02 + x07) | 0;
|
||||
x13 = rotl(x13 ^ x02, 8);
|
||||
x08 = (x08 + x13) | 0;
|
||||
x07 = rotl(x07 ^ x08, 7);
|
||||
x03 = (x03 + x04) | 0;
|
||||
x14 = rotl(x14 ^ x03, 16);
|
||||
x09 = (x09 + x14) | 0;
|
||||
x04 = rotl(x04 ^ x09, 12);
|
||||
x03 = (x03 + x04) | 0;
|
||||
x14 = rotl(x14 ^ x03, 8);
|
||||
x09 = (x09 + x14) | 0;
|
||||
x04 = rotl(x04 ^ x09, 7);
|
||||
}
|
||||
// Write output
|
||||
let oi = 0;
|
||||
out[oi++] = (y00 + x00) | 0;
|
||||
out[oi++] = (y01 + x01) | 0;
|
||||
out[oi++] = (y02 + x02) | 0;
|
||||
out[oi++] = (y03 + x03) | 0;
|
||||
out[oi++] = (y04 + x04) | 0;
|
||||
out[oi++] = (y05 + x05) | 0;
|
||||
out[oi++] = (y06 + x06) | 0;
|
||||
out[oi++] = (y07 + x07) | 0;
|
||||
out[oi++] = (y08 + x08) | 0;
|
||||
out[oi++] = (y09 + x09) | 0;
|
||||
out[oi++] = (y10 + x10) | 0;
|
||||
out[oi++] = (y11 + x11) | 0;
|
||||
out[oi++] = (y12 + x12) | 0;
|
||||
out[oi++] = (y13 + x13) | 0;
|
||||
out[oi++] = (y14 + x14) | 0;
|
||||
out[oi++] = (y15 + x15) | 0;
|
||||
}
|
||||
/**
|
||||
* hchacha helper method, used primarily in xchacha, to hash
|
||||
* key and nonce into key' and nonce'.
|
||||
* Same as chachaCore, but there doesn't seem to be a way to move the block
|
||||
* out without 25% performance hit.
|
||||
*/
|
||||
// prettier-ignore
|
||||
export function hchacha(c, key, src, out) {
|
||||
const k32 = u32(key);
|
||||
const i32 = u32(src);
|
||||
const o32 = u32(out);
|
||||
let x00 = c[0], x01 = c[1], x02 = c[2], x03 = c[3];
|
||||
let x04 = k32[0], x05 = k32[1], x06 = k32[2], x07 = k32[3];
|
||||
let x08 = k32[4], x09 = k32[5], x10 = k32[6], x11 = k32[7];
|
||||
let x12 = i32[0], x13 = i32[1], x14 = i32[2], x15 = i32[3];
|
||||
for (let i = 0; i < 20; i += 2) {
|
||||
x00 = (x00 + x04) | 0;
|
||||
x12 = rotl(x12 ^ x00, 16);
|
||||
x08 = (x08 + x12) | 0;
|
||||
x04 = rotl(x04 ^ x08, 12);
|
||||
x00 = (x00 + x04) | 0;
|
||||
x12 = rotl(x12 ^ x00, 8);
|
||||
x08 = (x08 + x12) | 0;
|
||||
x04 = rotl(x04 ^ x08, 7);
|
||||
x01 = (x01 + x05) | 0;
|
||||
x13 = rotl(x13 ^ x01, 16);
|
||||
x09 = (x09 + x13) | 0;
|
||||
x05 = rotl(x05 ^ x09, 12);
|
||||
x01 = (x01 + x05) | 0;
|
||||
x13 = rotl(x13 ^ x01, 8);
|
||||
x09 = (x09 + x13) | 0;
|
||||
x05 = rotl(x05 ^ x09, 7);
|
||||
x02 = (x02 + x06) | 0;
|
||||
x14 = rotl(x14 ^ x02, 16);
|
||||
x10 = (x10 + x14) | 0;
|
||||
x06 = rotl(x06 ^ x10, 12);
|
||||
x02 = (x02 + x06) | 0;
|
||||
x14 = rotl(x14 ^ x02, 8);
|
||||
x10 = (x10 + x14) | 0;
|
||||
x06 = rotl(x06 ^ x10, 7);
|
||||
x03 = (x03 + x07) | 0;
|
||||
x15 = rotl(x15 ^ x03, 16);
|
||||
x11 = (x11 + x15) | 0;
|
||||
x07 = rotl(x07 ^ x11, 12);
|
||||
x03 = (x03 + x07) | 0;
|
||||
x15 = rotl(x15 ^ x03, 8);
|
||||
x11 = (x11 + x15) | 0;
|
||||
x07 = rotl(x07 ^ x11, 7);
|
||||
x00 = (x00 + x05) | 0;
|
||||
x15 = rotl(x15 ^ x00, 16);
|
||||
x10 = (x10 + x15) | 0;
|
||||
x05 = rotl(x05 ^ x10, 12);
|
||||
x00 = (x00 + x05) | 0;
|
||||
x15 = rotl(x15 ^ x00, 8);
|
||||
x10 = (x10 + x15) | 0;
|
||||
x05 = rotl(x05 ^ x10, 7);
|
||||
x01 = (x01 + x06) | 0;
|
||||
x12 = rotl(x12 ^ x01, 16);
|
||||
x11 = (x11 + x12) | 0;
|
||||
x06 = rotl(x06 ^ x11, 12);
|
||||
x01 = (x01 + x06) | 0;
|
||||
x12 = rotl(x12 ^ x01, 8);
|
||||
x11 = (x11 + x12) | 0;
|
||||
x06 = rotl(x06 ^ x11, 7);
|
||||
x02 = (x02 + x07) | 0;
|
||||
x13 = rotl(x13 ^ x02, 16);
|
||||
x08 = (x08 + x13) | 0;
|
||||
x07 = rotl(x07 ^ x08, 12);
|
||||
x02 = (x02 + x07) | 0;
|
||||
x13 = rotl(x13 ^ x02, 8);
|
||||
x08 = (x08 + x13) | 0;
|
||||
x07 = rotl(x07 ^ x08, 7);
|
||||
x03 = (x03 + x04) | 0;
|
||||
x14 = rotl(x14 ^ x03, 16);
|
||||
x09 = (x09 + x14) | 0;
|
||||
x04 = rotl(x04 ^ x09, 12);
|
||||
x03 = (x03 + x04) | 0;
|
||||
x14 = rotl(x14 ^ x03, 8);
|
||||
x09 = (x09 + x14) | 0;
|
||||
x04 = rotl(x04 ^ x09, 7);
|
||||
}
|
||||
o32[0] = x00;
|
||||
o32[1] = x01;
|
||||
o32[2] = x02;
|
||||
o32[3] = x03;
|
||||
o32[4] = x12;
|
||||
o32[5] = x13;
|
||||
o32[6] = x14;
|
||||
o32[7] = x15;
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.
|
||||
*/
|
||||
export const chacha20orig = /* @__PURE__ */ salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 8,
|
||||
});
|
||||
/**
|
||||
* ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
export const chacha20 = /* @__PURE__ */ salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* XChaCha eXtended-nonce ChaCha. 24-byte nonce.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
*/
|
||||
export const xchacha20 = /* @__PURE__ */ salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 8,
|
||||
extendNonceFn: hchacha,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* Reduced 8-round chacha, described in original paper.
|
||||
*/
|
||||
export const chacha8 = /* @__PURE__ */ salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 8,
|
||||
});
|
||||
/**
|
||||
* Reduced 12-round chacha, described in original paper.
|
||||
*/
|
||||
export const chacha12 = /* @__PURE__ */ salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 12,
|
||||
});
|
||||
const ZERO = /* @__PURE__ */ new Uint8Array(16);
|
||||
// Pad to digest size with zeros
|
||||
const updatePadded = (h, msg) => {
|
||||
h.update(msg);
|
||||
const left = msg.length % 16;
|
||||
if (left)
|
||||
h.update(ZERO.subarray(left));
|
||||
};
|
||||
const computeTag = (fn, key, nonce, data, AAD) => {
|
||||
const authKey = fn(key, nonce, new Uint8Array(32));
|
||||
const h = poly1305.create(authKey);
|
||||
if (AAD)
|
||||
updatePadded(h, AAD);
|
||||
updatePadded(h, data);
|
||||
const num = new Uint8Array(16);
|
||||
const view = createView(num);
|
||||
setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
|
||||
setBigUint64(view, 8, BigInt(data.length), true);
|
||||
h.update(num);
|
||||
const res = h.digest();
|
||||
authKey.fill(0);
|
||||
return res;
|
||||
};
|
||||
/**
|
||||
* AEAD algorithm from RFC 8439.
|
||||
* Salsa20 and chacha (RFC 8439) use poly1305 differently.
|
||||
* We could have composed them similar to:
|
||||
* https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250
|
||||
* But it's hard because of authKey:
|
||||
* In salsa20, authKey changes position in salsa stream.
|
||||
* In chacha, authKey can't be computed inside computeTag, it modifies the counter.
|
||||
*/
|
||||
export const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
||||
const tagLength = 16;
|
||||
ensureBytes(key, 32);
|
||||
ensureBytes(nonce);
|
||||
return {
|
||||
tagLength,
|
||||
encrypt: (plaintext, output) => {
|
||||
const plength = plaintext.length;
|
||||
const clength = plength + tagLength;
|
||||
if (output) {
|
||||
ensureBytes(output, clength);
|
||||
}
|
||||
else {
|
||||
output = new Uint8Array(clength);
|
||||
}
|
||||
xorStream(key, nonce, plaintext, output, 1);
|
||||
const tag = computeTag(xorStream, key, nonce, output.subarray(0, -tagLength), AAD);
|
||||
output.set(tag, plength); // append tag
|
||||
return output;
|
||||
},
|
||||
decrypt: (ciphertext, output) => {
|
||||
const clength = ciphertext.length;
|
||||
const plength = clength - tagLength;
|
||||
if (clength < tagLength)
|
||||
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
|
||||
if (output) {
|
||||
ensureBytes(output, plength);
|
||||
}
|
||||
else {
|
||||
output = new Uint8Array(plength);
|
||||
}
|
||||
const data = ciphertext.subarray(0, -tagLength);
|
||||
const passedTag = ciphertext.subarray(-tagLength);
|
||||
const tag = computeTag(xorStream, key, nonce, data, AAD);
|
||||
if (!equalBytes(passedTag, tag))
|
||||
throw new Error('invalid tag');
|
||||
xorStream(key, nonce, data, output, 1);
|
||||
return output;
|
||||
},
|
||||
};
|
||||
};
|
||||
/**
|
||||
* ChaCha20-Poly1305 from RFC 8439.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
export const chacha20poly1305 = /* @__PURE__ */ _poly1305_aead(chacha20);
|
||||
/**
|
||||
* XChaCha20-Poly1305 extended-nonce chacha.
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
export const xchacha20poly1305 = /* @__PURE__ */ _poly1305_aead(xchacha20);
|
||||
//# sourceMappingURL=chacha.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-3
@@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
throw new Error('noble-ciphers have no entry-point: consult README for usage');
|
||||
//# sourceMappingURL=index.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC"}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"type": "module",
|
||||
"browser": {
|
||||
"node:crypto": false
|
||||
},
|
||||
"node": {
|
||||
"./crypto": "./esm/cryptoNode.js"
|
||||
}
|
||||
}
|
||||
-207
@@ -1,207 +0,0 @@
|
||||
import { ensureBytes, u32, equalBytes } from './utils.js';
|
||||
import { salsaBasic } from './_salsa.js';
|
||||
import { poly1305 } from './_poly1305.js';
|
||||
// Salsa20 stream cipher was released in 2005.
|
||||
// Salsa's goal was to implement AES replacement that does not rely on S-Boxes,
|
||||
// which are hard to implement in a constant-time manner.
|
||||
// https://cr.yp.to/snuffle.html, https://cr.yp.to/snuffle/salsafamily-20071225.pdf
|
||||
// Left rotate for uint32
|
||||
const rotl = (a, b) => (a << b) | (a >>> (32 - b));
|
||||
/**
|
||||
* Salsa20 core function.
|
||||
*/
|
||||
// prettier-ignore
|
||||
function salsaCore(c, k, i, out, cnt, rounds = 20) {
|
||||
// Based on https://cr.yp.to/salsa20.html
|
||||
let y00 = c[0], y01 = k[0], y02 = k[1], y03 = k[2]; // "expa" Key Key Key
|
||||
let y04 = k[3], y05 = c[1], y06 = i[0], y07 = i[1]; // Key "nd 3" Nonce Nonce
|
||||
let y08 = cnt, y09 = 0, y10 = c[2], y11 = k[4]; // Pos. Pos. "2-by" Key
|
||||
let y12 = k[5], y13 = k[6], y14 = k[7], y15 = c[3]; // Key Key Key "te k"
|
||||
// Save state to temporary variables
|
||||
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
||||
// Main loop
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
x04 ^= rotl(x00 + x12 | 0, 7);
|
||||
x08 ^= rotl(x04 + x00 | 0, 9);
|
||||
x12 ^= rotl(x08 + x04 | 0, 13);
|
||||
x00 ^= rotl(x12 + x08 | 0, 18);
|
||||
x09 ^= rotl(x05 + x01 | 0, 7);
|
||||
x13 ^= rotl(x09 + x05 | 0, 9);
|
||||
x01 ^= rotl(x13 + x09 | 0, 13);
|
||||
x05 ^= rotl(x01 + x13 | 0, 18);
|
||||
x14 ^= rotl(x10 + x06 | 0, 7);
|
||||
x02 ^= rotl(x14 + x10 | 0, 9);
|
||||
x06 ^= rotl(x02 + x14 | 0, 13);
|
||||
x10 ^= rotl(x06 + x02 | 0, 18);
|
||||
x03 ^= rotl(x15 + x11 | 0, 7);
|
||||
x07 ^= rotl(x03 + x15 | 0, 9);
|
||||
x11 ^= rotl(x07 + x03 | 0, 13);
|
||||
x15 ^= rotl(x11 + x07 | 0, 18);
|
||||
x01 ^= rotl(x00 + x03 | 0, 7);
|
||||
x02 ^= rotl(x01 + x00 | 0, 9);
|
||||
x03 ^= rotl(x02 + x01 | 0, 13);
|
||||
x00 ^= rotl(x03 + x02 | 0, 18);
|
||||
x06 ^= rotl(x05 + x04 | 0, 7);
|
||||
x07 ^= rotl(x06 + x05 | 0, 9);
|
||||
x04 ^= rotl(x07 + x06 | 0, 13);
|
||||
x05 ^= rotl(x04 + x07 | 0, 18);
|
||||
x11 ^= rotl(x10 + x09 | 0, 7);
|
||||
x08 ^= rotl(x11 + x10 | 0, 9);
|
||||
x09 ^= rotl(x08 + x11 | 0, 13);
|
||||
x10 ^= rotl(x09 + x08 | 0, 18);
|
||||
x12 ^= rotl(x15 + x14 | 0, 7);
|
||||
x13 ^= rotl(x12 + x15 | 0, 9);
|
||||
x14 ^= rotl(x13 + x12 | 0, 13);
|
||||
x15 ^= rotl(x14 + x13 | 0, 18);
|
||||
}
|
||||
// Write output
|
||||
let oi = 0;
|
||||
out[oi++] = (y00 + x00) | 0;
|
||||
out[oi++] = (y01 + x01) | 0;
|
||||
out[oi++] = (y02 + x02) | 0;
|
||||
out[oi++] = (y03 + x03) | 0;
|
||||
out[oi++] = (y04 + x04) | 0;
|
||||
out[oi++] = (y05 + x05) | 0;
|
||||
out[oi++] = (y06 + x06) | 0;
|
||||
out[oi++] = (y07 + x07) | 0;
|
||||
out[oi++] = (y08 + x08) | 0;
|
||||
out[oi++] = (y09 + x09) | 0;
|
||||
out[oi++] = (y10 + x10) | 0;
|
||||
out[oi++] = (y11 + x11) | 0;
|
||||
out[oi++] = (y12 + x12) | 0;
|
||||
out[oi++] = (y13 + x13) | 0;
|
||||
out[oi++] = (y14 + x14) | 0;
|
||||
out[oi++] = (y15 + x15) | 0;
|
||||
}
|
||||
/**
|
||||
* hsalsa hashing function, used primarily in xsalsa, to hash
|
||||
* key and nonce into key' and nonce'.
|
||||
* Same as salsaCore, but there doesn't seem to be a way to move the block
|
||||
* out without 25% performance hit.
|
||||
*/
|
||||
// prettier-ignore
|
||||
export function hsalsa(c, key, nonce, out) {
|
||||
const k32 = u32(key);
|
||||
const i32 = u32(nonce);
|
||||
const o32 = u32(out);
|
||||
let x00 = c[0], x01 = k32[0], x02 = k32[1], x03 = k32[2], x04 = k32[3];
|
||||
let x05 = c[1], x06 = i32[0], x07 = i32[1], x08 = i32[2], x09 = i32[3];
|
||||
let x10 = c[2], x11 = k32[4], x12 = k32[5], x13 = k32[6], x14 = k32[7];
|
||||
let x15 = c[3];
|
||||
// Main loop
|
||||
for (let i = 0; i < 20; i += 2) {
|
||||
x04 ^= rotl(x00 + x12 | 0, 7);
|
||||
x08 ^= rotl(x04 + x00 | 0, 9);
|
||||
x12 ^= rotl(x08 + x04 | 0, 13);
|
||||
x00 ^= rotl(x12 + x08 | 0, 18);
|
||||
x09 ^= rotl(x05 + x01 | 0, 7);
|
||||
x13 ^= rotl(x09 + x05 | 0, 9);
|
||||
x01 ^= rotl(x13 + x09 | 0, 13);
|
||||
x05 ^= rotl(x01 + x13 | 0, 18);
|
||||
x14 ^= rotl(x10 + x06 | 0, 7);
|
||||
x02 ^= rotl(x14 + x10 | 0, 9);
|
||||
x06 ^= rotl(x02 + x14 | 0, 13);
|
||||
x10 ^= rotl(x06 + x02 | 0, 18);
|
||||
x03 ^= rotl(x15 + x11 | 0, 7);
|
||||
x07 ^= rotl(x03 + x15 | 0, 9);
|
||||
x11 ^= rotl(x07 + x03 | 0, 13);
|
||||
x15 ^= rotl(x11 + x07 | 0, 18);
|
||||
x01 ^= rotl(x00 + x03 | 0, 7);
|
||||
x02 ^= rotl(x01 + x00 | 0, 9);
|
||||
x03 ^= rotl(x02 + x01 | 0, 13);
|
||||
x00 ^= rotl(x03 + x02 | 0, 18);
|
||||
x06 ^= rotl(x05 + x04 | 0, 7);
|
||||
x07 ^= rotl(x06 + x05 | 0, 9);
|
||||
x04 ^= rotl(x07 + x06 | 0, 13);
|
||||
x05 ^= rotl(x04 + x07 | 0, 18);
|
||||
x11 ^= rotl(x10 + x09 | 0, 7);
|
||||
x08 ^= rotl(x11 + x10 | 0, 9);
|
||||
x09 ^= rotl(x08 + x11 | 0, 13);
|
||||
x10 ^= rotl(x09 + x08 | 0, 18);
|
||||
x12 ^= rotl(x15 + x14 | 0, 7);
|
||||
x13 ^= rotl(x12 + x15 | 0, 9);
|
||||
x14 ^= rotl(x13 + x12 | 0, 13);
|
||||
x15 ^= rotl(x14 + x13 | 0, 18);
|
||||
}
|
||||
o32[0] = x00;
|
||||
o32[1] = x05;
|
||||
o32[2] = x10;
|
||||
o32[3] = x15;
|
||||
o32[4] = x06;
|
||||
o32[5] = x07;
|
||||
o32[6] = x08;
|
||||
o32[7] = x09;
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Salsa20 from original paper.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
export const salsa20 = /* @__PURE__ */ salsaBasic({ core: salsaCore, counterRight: true });
|
||||
/**
|
||||
* xsalsa20 eXtended-nonce salsa.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
export const xsalsa20 = /* @__PURE__ */ salsaBasic({
|
||||
core: salsaCore,
|
||||
counterRight: true,
|
||||
extendNonceFn: hsalsa,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* xsalsa20-poly1305 eXtended-nonce salsa.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
* Also known as secretbox from libsodium / nacl.
|
||||
*/
|
||||
export const xsalsa20poly1305 = (key, nonce) => {
|
||||
const tagLength = 16;
|
||||
ensureBytes(key, 32);
|
||||
ensureBytes(nonce, 24);
|
||||
return {
|
||||
tagLength,
|
||||
encrypt: (plaintext, output) => {
|
||||
ensureBytes(plaintext);
|
||||
// This is small optimization (calculate auth key with same call as encryption itself) makes it hard
|
||||
// to separate tag calculation and encryption itself, since 32 byte is half-block of salsa (64 byte)
|
||||
const clength = plaintext.length + 32;
|
||||
if (output) {
|
||||
ensureBytes(output, clength);
|
||||
}
|
||||
else {
|
||||
output = new Uint8Array(clength);
|
||||
}
|
||||
output.set(plaintext, 32);
|
||||
xsalsa20(key, nonce, output, output);
|
||||
const authKey = output.subarray(0, 32);
|
||||
const tag = poly1305(output.subarray(32), authKey);
|
||||
// Clean auth key, even though JS provides no guarantees about memory cleaning
|
||||
output.set(tag, tagLength);
|
||||
output.subarray(0, tagLength).fill(0);
|
||||
return output.subarray(tagLength);
|
||||
},
|
||||
decrypt: (ciphertext) => {
|
||||
ensureBytes(ciphertext);
|
||||
const clength = ciphertext.length;
|
||||
if (clength < tagLength)
|
||||
throw new Error('encrypted data should be at least 16 bytes');
|
||||
// Create new ciphertext array:
|
||||
// auth tag auth tag from ciphertext ciphertext
|
||||
// [bytes 0..16] [bytes 16..32] [bytes 32..]
|
||||
// 16 instead of 32, because we already have 16 byte tag
|
||||
const ciphertext_ = new Uint8Array(clength + tagLength); // alloc
|
||||
ciphertext_.set(ciphertext, tagLength);
|
||||
// Each xsalsa20 calls to hsalsa to calculate key, but seems not much perf difference
|
||||
// Separate call to calculate authkey, since first bytes contains tag
|
||||
const authKey = xsalsa20(key, nonce, new Uint8Array(32)); // alloc(32)
|
||||
const tag = poly1305(ciphertext_.subarray(32), authKey);
|
||||
if (!equalBytes(ciphertext_.subarray(16, 32), tag))
|
||||
throw new Error('invalid tag');
|
||||
const plaintext = xsalsa20(key, nonce, ciphertext_); // alloc
|
||||
// Clean auth key, even though JS provides no guarantees about memory cleaning
|
||||
plaintext.subarray(0, 32).fill(0);
|
||||
authKey.fill(0);
|
||||
return plaintext.subarray(32);
|
||||
},
|
||||
};
|
||||
};
|
||||
//# sourceMappingURL=salsa.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-58
@@ -1,58 +0,0 @@
|
||||
import { xchacha20poly1305 } from './chacha.js';
|
||||
import { xsalsa20poly1305 } from './salsa.js';
|
||||
import { concatBytes, ensureBytes, utf8ToBytes } from './utils.js';
|
||||
import { aes_256_gcm } from './webcrypto/aes.js';
|
||||
import { randomBytes } from './webcrypto/utils.js';
|
||||
export { utf8ToBytes };
|
||||
/**
|
||||
* Alias to xsalsa20poly1305, for compatibility with libsodium / nacl
|
||||
*/
|
||||
export function secretbox(key, nonce) {
|
||||
ensureBytes(key);
|
||||
ensureBytes(nonce);
|
||||
const xs = xsalsa20poly1305(key, nonce);
|
||||
return { seal: xs.encrypt, open: xs.decrypt };
|
||||
}
|
||||
export function randomKey() {
|
||||
return randomBytes(32);
|
||||
}
|
||||
/**
|
||||
* Encrypt plaintext under key with random nonce, using xchacha20poly1305.
|
||||
* User never touches nonce: it is prepended to ciphertext.
|
||||
*/
|
||||
export function encrypt(key, plaintext) {
|
||||
ensureBytes(key);
|
||||
const nonce = randomBytes(24);
|
||||
const ciphertext = xchacha20poly1305(key, nonce).encrypt(plaintext);
|
||||
return concatBytes(nonce, ciphertext);
|
||||
}
|
||||
/**
|
||||
* Decrypt plaintext under key with random nonce, using xchacha20poly1305.
|
||||
* User never touches nonce: it is prepended to ciphertext.
|
||||
*/
|
||||
export function decrypt(key, ciphertext) {
|
||||
const nonceLength = 24;
|
||||
ensureBytes(ciphertext);
|
||||
if (ciphertext.length <= nonceLength)
|
||||
throw new Error('invalid ciphertext length');
|
||||
const nonce = ciphertext.subarray(0, nonceLength);
|
||||
const ciphertextWithoutNonce = ciphertext.subarray(nonceLength);
|
||||
return xchacha20poly1305(key, nonce).decrypt(ciphertextWithoutNonce);
|
||||
}
|
||||
export async function aes_encrypt(key, plaintext) {
|
||||
const nonceLength = 12;
|
||||
ensureBytes(key);
|
||||
const nonce = randomBytes(nonceLength);
|
||||
const ciphertext = await aes_256_gcm(key, nonce).encrypt(plaintext);
|
||||
return concatBytes(nonce, ciphertext);
|
||||
}
|
||||
export async function aes_decrypt(key, ciphertext) {
|
||||
const nonceLength = 12;
|
||||
ensureBytes(ciphertext);
|
||||
if (ciphertext.length <= nonceLength)
|
||||
throw new Error('invalid ciphertext length');
|
||||
const nonce = ciphertext.subarray(0, nonceLength);
|
||||
const ciphertextWithoutNonce = ciphertext.subarray(nonceLength);
|
||||
return aes_256_gcm(key, nonce).decrypt(ciphertextWithoutNonce);
|
||||
}
|
||||
//# sourceMappingURL=simple.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"simple.js","sourceRoot":"","sources":["../src/simple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,CAAC;AAEvB;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAe,EAAE,KAAiB;IAC1D,WAAW,CAAC,GAAG,CAAC,CAAC;IACjB,WAAW,CAAC,KAAK,CAAC,CAAC;IACnB,MAAM,EAAE,GAAG,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC;AACzB,CAAC;AACD;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAe,EAAE,SAAqB;IAC5D,WAAW,CAAC,GAAG,CAAC,CAAC;IACjB,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,UAAU,GAAG,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpE,OAAO,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAe,EAAE,UAAsB;IAC7D,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,WAAW,CAAC,UAAU,CAAC,CAAC;IACxB,IAAI,UAAU,CAAC,MAAM,IAAI,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACnF,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,sBAAsB,GAAG,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChE,OAAO,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAe,EAAE,SAAqB;IACtE,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,WAAW,CAAC,GAAG,CAAC,CAAC;IACjB,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpE,OAAO,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAe,EAAE,UAAsB;IACvE,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,WAAW,CAAC,UAAU,CAAC,CAAC;IACxB,IAAI,UAAU,CAAC,MAAM,IAAI,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACnF,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,sBAAsB,GAAG,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChE,OAAO,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;AACjE,CAAC"}
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
||||
const u8a = (a) => a instanceof Uint8Array;
|
||||
// Cast array to different type
|
||||
export const u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
||||
export const u16 = (arr) => new Uint16Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 2));
|
||||
export const u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
||||
// Cast array to view
|
||||
export const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
||||
// big-endian hardware is rare. Just in case someone still decides to run ciphers:
|
||||
// early-throw an error because we don't support BE yet.
|
||||
export const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;
|
||||
if (!isLE)
|
||||
throw new Error('Non little-endian hardware is not supported');
|
||||
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
|
||||
/**
|
||||
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
|
||||
*/
|
||||
export function bytesToHex(bytes) {
|
||||
if (!u8a(bytes))
|
||||
throw new Error('Uint8Array expected');
|
||||
// pre-caching improves the speed 6x
|
||||
let hex = '';
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
hex += hexes[bytes[i]];
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
/**
|
||||
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
|
||||
*/
|
||||
export function hexToBytes(hex) {
|
||||
if (typeof hex !== 'string')
|
||||
throw new Error('hex string expected, got ' + typeof hex);
|
||||
const len = hex.length;
|
||||
if (len % 2)
|
||||
throw new Error('padded hex string expected, got unpadded hex of length ' + len);
|
||||
const array = new Uint8Array(len / 2);
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const j = i * 2;
|
||||
const hexByte = hex.slice(j, j + 2);
|
||||
const byte = Number.parseInt(hexByte, 16);
|
||||
if (Number.isNaN(byte) || byte < 0)
|
||||
throw new Error('Invalid byte sequence');
|
||||
array[i] = byte;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
// There is no setImmediate in browser and setTimeout is slow.
|
||||
// call of async fn will return Promise, which will be fullfiled only on
|
||||
// next scheduler queue processing step and this is exactly what we need.
|
||||
export const nextTick = async () => { };
|
||||
// Returns control to thread each 'tick' ms to avoid blocking
|
||||
export async function asyncLoop(iters, tick, cb) {
|
||||
let ts = Date.now();
|
||||
for (let i = 0; i < iters; i++) {
|
||||
cb(i);
|
||||
// Date.now() is not monotonic, so in case if clock goes backwards we return return control too
|
||||
const diff = Date.now() - ts;
|
||||
if (diff >= 0 && diff < tick)
|
||||
continue;
|
||||
await nextTick();
|
||||
ts += diff;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
|
||||
*/
|
||||
export function utf8ToBytes(str) {
|
||||
if (typeof str !== 'string')
|
||||
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
|
||||
}
|
||||
export function bytesToUtf8(bytes) {
|
||||
return new TextDecoder().decode(bytes);
|
||||
}
|
||||
/**
|
||||
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
|
||||
* Warning: when Uint8Array is passed, it would NOT get copied.
|
||||
* Keep in mind for future mutable operations.
|
||||
*/
|
||||
export function toBytes(data) {
|
||||
if (typeof data === 'string')
|
||||
data = utf8ToBytes(data);
|
||||
if (!u8a(data))
|
||||
throw new Error(`expected Uint8Array, got ${typeof data}`);
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Copies several Uint8Arrays into one.
|
||||
*/
|
||||
export function concatBytes(...arrays) {
|
||||
const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
|
||||
let pad = 0; // walk through each item, ensure they have proper type
|
||||
arrays.forEach((a) => {
|
||||
if (!u8a(a))
|
||||
throw new Error('Uint8Array expected');
|
||||
r.set(a, pad);
|
||||
pad += a.length;
|
||||
});
|
||||
return r;
|
||||
}
|
||||
// Check if object doens't have custom constructor (like Uint8Array/Array)
|
||||
const isPlainObject = (obj) => Object.prototype.toString.call(obj) === '[object Object]' && obj.constructor === Object;
|
||||
export function checkOpts(defaults, opts) {
|
||||
if (opts !== undefined && (typeof opts !== 'object' || !isPlainObject(opts)))
|
||||
throw new Error('options must be object or undefined');
|
||||
const merged = Object.assign(defaults, opts);
|
||||
return merged;
|
||||
}
|
||||
export function ensureBytes(b, len) {
|
||||
if (!(b instanceof Uint8Array))
|
||||
throw new Error('Uint8Array expected');
|
||||
if (typeof len === 'number')
|
||||
if (b.length !== len)
|
||||
throw new Error(`Uint8Array length ${len} expected`);
|
||||
}
|
||||
// Constant-time equality
|
||||
export function equalBytes(a, b) {
|
||||
// Should not happen
|
||||
if (a.length !== b.length)
|
||||
throw new Error('equalBytes: Different size of Uint8Arrays');
|
||||
let isSame = true;
|
||||
for (let i = 0; i < a.length; i++)
|
||||
isSame && (isSame = a[i] === b[i]); // Lets hope JIT won't optimize away.
|
||||
return isSame;
|
||||
}
|
||||
// For runtime check if class implements interface
|
||||
export class Hash {
|
||||
}
|
||||
// Polyfill for Safari 14
|
||||
export function setBigUint64(view, byteOffset, value, isLE) {
|
||||
if (typeof view.setBigUint64 === 'function')
|
||||
return view.setBigUint64(byteOffset, value, isLE);
|
||||
const _32n = BigInt(32);
|
||||
const _u32_max = BigInt(0xffffffff);
|
||||
const wh = Number((value >> _32n) & _u32_max);
|
||||
const wl = Number(value & _u32_max);
|
||||
const h = isLE ? 4 : 0;
|
||||
const l = isLE ? 0 : 4;
|
||||
view.setUint32(byteOffset + h, wh, isLE);
|
||||
view.setUint32(byteOffset + l, wl, isLE);
|
||||
}
|
||||
//# sourceMappingURL=utils.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-42
@@ -1,42 +0,0 @@
|
||||
import { ensureBytes } from '../utils.js';
|
||||
import { getWebcryptoSubtle } from './utils.js';
|
||||
function generate(algo, length) {
|
||||
const keyLength = length / 8;
|
||||
const keyParams = { name: algo, length };
|
||||
const cryptParams = { name: algo };
|
||||
// const params: Record<string, any> = ({ e: algo, i: { name: algo, length } });
|
||||
return (key, nonce) => {
|
||||
ensureBytes(key, keyLength);
|
||||
if (algo === 'AES-CTR') {
|
||||
cryptParams.counter = nonce;
|
||||
cryptParams.length = 64;
|
||||
}
|
||||
else {
|
||||
cryptParams.iv = nonce;
|
||||
}
|
||||
return {
|
||||
keyLength,
|
||||
async encrypt(plaintext) {
|
||||
ensureBytes(plaintext);
|
||||
const cr = getWebcryptoSubtle();
|
||||
const iKey = await cr.importKey('raw', key, keyParams, true, ['encrypt']);
|
||||
const cipher = await cr.encrypt(cryptParams, iKey, plaintext);
|
||||
return new Uint8Array(cipher);
|
||||
},
|
||||
async decrypt(ciphertext) {
|
||||
ensureBytes(ciphertext);
|
||||
const cr = getWebcryptoSubtle();
|
||||
const iKey = await cr.importKey('raw', key, keyParams, true, ['decrypt']);
|
||||
const plaintext = await cr.decrypt(cryptParams, iKey, ciphertext);
|
||||
return new Uint8Array(plaintext);
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
export const aes_128_ctr = generate('AES-CTR', 128);
|
||||
export const aes_256_ctr = generate('AES-CTR', 256);
|
||||
export const aes_128_cbc = generate('AES-CBC', 128);
|
||||
export const aes_256_cbc = generate('AES-CBC', 256);
|
||||
export const aes_128_gcm = generate('AES-GCM', 128);
|
||||
export const aes_256_gcm = generate('AES-GCM', 256);
|
||||
//# sourceMappingURL=aes.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"aes.js","sourceRoot":"","sources":["../../src/webcrypto/aes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,SAAS,QAAQ,CAAC,IAAY,EAAE,MAAc;IAC5C,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC;IAC7B,MAAM,SAAS,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACzC,MAAM,WAAW,GAAwB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxD,gFAAgF;IAEhF,OAAO,CAAC,GAAe,EAAE,KAAiB,EAAE,EAAE;QAC5C,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC5B,IAAI,IAAI,KAAK,SAAS,EAAE;YACtB,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;YAC5B,WAAW,CAAC,MAAM,GAAG,EAAE,CAAC;SACzB;aAAM;YACL,WAAW,CAAC,EAAE,GAAG,KAAK,CAAC;SACxB;QAED,OAAO;YACL,SAAS;YAET,KAAK,CAAC,OAAO,CAAC,SAAqB;gBACjC,WAAW,CAAC,SAAS,CAAC,CAAC;gBACvB,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;gBAC1E,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;gBAC9D,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;YAED,KAAK,CAAC,OAAO,CAAC,UAAsB;gBAClC,WAAW,CAAC,UAAU,CAAC,CAAC;gBACxB,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;gBAC1E,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;gBAClE,OAAO,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;YACnC,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACpD,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAEpD,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACpD,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAEpD,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACpD,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC"}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
export const crypto = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
|
||||
//# sourceMappingURL=crypto.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/webcrypto/crypto.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,MAAM,GACjB,OAAO,UAAU,KAAK,QAAQ,IAAI,QAAQ,IAAI,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC"}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
|
||||
// See utils.ts for details.
|
||||
// The file will throw on node.js 14 and earlier.
|
||||
// @ts-ignore
|
||||
import * as nc from 'node:crypto';
|
||||
export const crypto = nc && typeof nc === 'object' && 'webcrypto' in nc ? nc.webcrypto : undefined;
|
||||
//# sourceMappingURL=cryptoNode.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"cryptoNode.js","sourceRoot":"","sources":["../../src/webcrypto/cryptoNode.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,4BAA4B;AAC5B,iDAAiD;AACjD,aAAa;AACb,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,MAAM,CAAC,MAAM,MAAM,GACjB,EAAE,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,WAAW,IAAI,EAAE,CAAC,CAAC,CAAE,EAAE,CAAC,SAAiB,CAAC,CAAC,CAAC,SAAS,CAAC"}
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
import { getWebcryptoSubtle } from './utils.js';
|
||||
// Format-preserving encryption algorithm (FPE-FF1) specified in NIST Special Publication 800-38G.
|
||||
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf
|
||||
// Utils
|
||||
function toBytesBE(num, length) {
|
||||
let hex = num.toString(16);
|
||||
hex = hex.length & 1 ? `0${hex}` : hex;
|
||||
if (length)
|
||||
hex = hex.padStart(length * 2, '00');
|
||||
const len = hex.length / 2;
|
||||
const u8 = new Uint8Array(len);
|
||||
for (let j = 0, i = 0; i < hex.length && i < len * 2; i += 2, j++)
|
||||
u8[j] = parseInt(hex[i] + hex[i + 1], 16);
|
||||
return u8;
|
||||
}
|
||||
function fromBytesBE(bytes) {
|
||||
let value = 0n;
|
||||
for (let i = bytes.length - 1, j = 0; i >= 0; i--, j++)
|
||||
value += (BigInt(bytes[i]) & 255n) << (8n * BigInt(j));
|
||||
return value;
|
||||
}
|
||||
function mod(a, b) {
|
||||
const result = a % b;
|
||||
return result >= 0 ? result : b + result;
|
||||
}
|
||||
// AES stuff
|
||||
const BLOCK_LEN = 16;
|
||||
const IV = new Uint8Array(BLOCK_LEN);
|
||||
export async function encryptBlock(msg, key) {
|
||||
if (key.length !== 16 && key.length !== 32)
|
||||
throw new Error('Invalid key length');
|
||||
const cr = getWebcryptoSubtle();
|
||||
const mode = { name: `AES-CBC`, length: key.length * 8 };
|
||||
const wKey = await cr.importKey('raw', key, mode, true, ['encrypt']);
|
||||
const cipher = await cr.encrypt({ name: `aes-cbc`, iv: IV, counter: IV, length: 64 }, wKey, msg);
|
||||
return new Uint8Array(cipher).subarray(0, 16);
|
||||
}
|
||||
function NUMradix(radix, data) {
|
||||
let res = 0n;
|
||||
for (let i of data)
|
||||
res = res * BigInt(radix) + BigInt(i);
|
||||
return res;
|
||||
}
|
||||
async function getRound(radix, key, tweak, x) {
|
||||
if (radix > 2 ** 16 - 1)
|
||||
throw new Error(`Invalid radix: ${radix}`);
|
||||
// radix**minlen ≥ 100
|
||||
const minLen = Math.ceil(Math.log(100) / Math.log(radix));
|
||||
const maxLen = 2 ** 32 - 1;
|
||||
// 2 ≤ minlen ≤ maxlen < 2**32
|
||||
if (2 > minLen || minLen > maxLen || maxLen >= 2 ** 32)
|
||||
throw new Error('Invalid radix: 2 ≤ minlen ≤ maxlen < 2**32');
|
||||
if (x.length < minLen || x.length > maxLen)
|
||||
throw new Error('X is outside minLen..maxLen bounds');
|
||||
const u = Math.floor(x.length / 2);
|
||||
const v = x.length - u;
|
||||
const b = Math.ceil(Math.ceil(v * Math.log2(radix)) / 8);
|
||||
const d = 4 * Math.ceil(b / 4) + 4;
|
||||
const padding = mod(-tweak.length - b - 1, 16);
|
||||
// P = [1]1 || [2]1 || [1]1 || [radix]3 || [10]1 || [u mod 256]1 || [n]4 || [t]4.
|
||||
const P = new Uint8Array([1, 2, 1, 0, 0, 0, 10, u, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
const view = new DataView(P.buffer);
|
||||
view.setUint16(4, radix, false);
|
||||
view.setUint32(8, x.length, false);
|
||||
view.setUint32(12, tweak.length, false);
|
||||
// Q = T || [0](−t−b−1) mod 16 || [i]1 || [NUMradix(B)]b.
|
||||
const PQ = new Uint8Array(P.length + tweak.length + padding + 1 + b);
|
||||
PQ.set(P);
|
||||
P.fill(0);
|
||||
PQ.set(tweak, P.length);
|
||||
const round = async (A, B, i, decrypt = false) => {
|
||||
// Q = ... || [i]1 || [NUMradix(B)]b.
|
||||
PQ[PQ.length - b - 1] = i;
|
||||
if (b)
|
||||
PQ.set(toBytesBE(NUMradix(radix, B), b), PQ.length - b);
|
||||
// PRF
|
||||
let r = new Uint8Array(16);
|
||||
for (let j = 0; j < PQ.length / BLOCK_LEN; j++) {
|
||||
for (let i = 0; i < BLOCK_LEN; i++)
|
||||
r[i] ^= PQ[j * BLOCK_LEN + i];
|
||||
r.set(await encryptBlock(r, key));
|
||||
}
|
||||
// Let S be the first d bytes of the following string of ⎡d/16⎤ blocks:
|
||||
// R || CIPHK(R ⊕[1]16) || CIPHK(R ⊕[2]16) ...CIPHK(R ⊕[⎡d / 16⎤ – 1]16).
|
||||
let s = Array.from(r);
|
||||
for (let j = 1; s.length < d; j++) {
|
||||
const block = toBytesBE(BigInt(j), 16);
|
||||
for (let k = 0; k < BLOCK_LEN; k++)
|
||||
block[k] ^= r[k];
|
||||
s.push(...Array.from(await encryptBlock(block, key)));
|
||||
}
|
||||
let y = fromBytesBE(Uint8Array.from(s.slice(0, d)));
|
||||
s.fill(0);
|
||||
if (decrypt)
|
||||
y = -y;
|
||||
const m = i % 2 === 0 ? u : v;
|
||||
let c = mod(NUMradix(radix, A) + y, BigInt(radix) ** BigInt(m));
|
||||
// STR(radix, m, c)
|
||||
const C = Array(m).fill(0);
|
||||
for (let i = 0; i < m; i++, c /= BigInt(radix))
|
||||
C[m - 1 - i] = Number(c % BigInt(radix));
|
||||
A.fill(0);
|
||||
A = B;
|
||||
B = C;
|
||||
return [A, B];
|
||||
};
|
||||
const destroy = () => PQ.fill(0);
|
||||
return { u, round, destroy };
|
||||
}
|
||||
const EMPTY_BUF = new Uint8Array([]);
|
||||
export function FF1(radix, key, tweak = EMPTY_BUF) {
|
||||
const PQ = getRound.bind(null, radix, key, tweak);
|
||||
return {
|
||||
async encrypt(x) {
|
||||
const { u, round, destroy } = await PQ(x);
|
||||
let [A, B] = [x.slice(0, u), x.slice(u)];
|
||||
for (let i = 0; i < 10; i++)
|
||||
[A, B] = await round(A, B, i);
|
||||
destroy();
|
||||
const res = A.concat(B);
|
||||
A.fill(0);
|
||||
B.fill(0);
|
||||
return res;
|
||||
},
|
||||
async decrypt(x) {
|
||||
const { u, round, destroy } = await PQ(x);
|
||||
// The FF1.Decrypt algorithm is similar to the FF1.Encrypt algorithm;
|
||||
// the differences are in Step 6, where:
|
||||
// 1) the order of the indices is reversed,
|
||||
// 2) the roles of A and B are swapped
|
||||
// 3) modular addition is replaced by modular subtraction, in Step 6vi.
|
||||
let [B, A] = [x.slice(0, u), x.slice(u)];
|
||||
for (let i = 9; i >= 0; i--)
|
||||
[A, B] = await round(A, B, i, true);
|
||||
destroy();
|
||||
const res = B.concat(A);
|
||||
A.fill(0);
|
||||
B.fill(0);
|
||||
return res;
|
||||
},
|
||||
};
|
||||
}
|
||||
// Binary string which encodes each byte in little-endian byte order
|
||||
const binLE = {
|
||||
encode(bytes) {
|
||||
const x = [];
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
for (let j = 0, tmp = bytes[i]; j < 8; j++, tmp >>= 1)
|
||||
x.push(tmp & 1);
|
||||
}
|
||||
return x;
|
||||
},
|
||||
decode(b) {
|
||||
if (b.length % 8)
|
||||
throw new Error('Invalid binary string');
|
||||
const res = new Uint8Array(b.length / 8);
|
||||
for (let i = 0, j = 0; i < res.length; i++) {
|
||||
res[i] = b[j++] | (b[j++] << 1) | (b[j++] << 2) | (b[j++] << 3);
|
||||
res[i] |= (b[j++] << 4) | (b[j++] << 5) | (b[j++] << 6) | (b[j++] << 7);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
};
|
||||
export function BinaryFF1(key, tweak = EMPTY_BUF) {
|
||||
const ff1 = FF1(2, key, tweak);
|
||||
return {
|
||||
encrypt: async (x) => binLE.decode(await ff1.encrypt(binLE.encode(x))),
|
||||
decrypt: async (x) => binLE.decode(await ff1.decrypt(binLE.encode(x))),
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=ff1.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-122
@@ -1,122 +0,0 @@
|
||||
import { createView, setBigUint64 } from '../utils.js';
|
||||
import { polyval } from '../_polyval.js';
|
||||
import { getWebcryptoSubtle } from './utils.js';
|
||||
/**
|
||||
* AES-GCM-SIV: classic AES-GCM with nonce-misuse resistance.
|
||||
* RFC 8452, https://datatracker.ietf.org/doc/html/rfc8452
|
||||
*/
|
||||
// AES stuff (same as ff1)
|
||||
const BLOCK_LEN = 16;
|
||||
const IV = new Uint8Array(BLOCK_LEN);
|
||||
async function encryptBlock(msg, key) {
|
||||
if (key.length !== 16 && key.length !== 32)
|
||||
throw new Error('Invalid key length');
|
||||
const mode = { name: `AES-CBC`, length: key.length * 8 };
|
||||
const cr = getWebcryptoSubtle();
|
||||
const wKey = await cr.importKey('raw', key, mode, true, ['encrypt']);
|
||||
const cipher = await cr.encrypt({ name: `aes-cbc`, iv: IV, counter: IV, length: 64 }, wKey, msg);
|
||||
return new Uint8Array(cipher).subarray(0, 16);
|
||||
}
|
||||
// Kinda constant-time equality
|
||||
function equalBytes(a, b) {
|
||||
// Should not happen
|
||||
if (a.length !== b.length)
|
||||
throw new Error('equalBytes: Different size of Uint8Arrays');
|
||||
let flag = true;
|
||||
for (let i = 0; i < a.length; i++)
|
||||
if (a[i] !== b[i])
|
||||
flag && (flag = false);
|
||||
return flag;
|
||||
}
|
||||
// Wrap position so it will be in padded to blockSize
|
||||
const wrapPos = (pos, blockSize) => Math.ceil(pos / blockSize) * blockSize;
|
||||
const limit = (name, min, max) => (value) => {
|
||||
if (!Number.isSafeInteger(value) || min > value || value > max)
|
||||
throw new Error(`${name}: invalid value=${value}, must be [${min}..${max}]`);
|
||||
};
|
||||
// From RFC 8452: Section 6
|
||||
const AAD_LIMIT = limit('AAD', 0, 2 ** 36);
|
||||
const PLAIN_LIMIT = limit('Plaintext', 0, 2 ** 36);
|
||||
const NONCE_LIMIT = limit('Nonce', 12, 12);
|
||||
const CIPHER_LIMIT = limit('Ciphertext', 16, 2 ** 36 + 16);
|
||||
// nodejs api doesn't support 32bit counters, browser does
|
||||
async function ctr(key, tag, input) {
|
||||
// The initial counter block is the tag with the most significant bit of the last byte set to one.
|
||||
let block = tag.slice();
|
||||
block[15] |= 0x80;
|
||||
let view = createView(block);
|
||||
let output = new Uint8Array(input.length);
|
||||
for (let pos = 0; pos < input.length;) {
|
||||
const encryptedBlock = await encryptBlock(block, key);
|
||||
view.setUint32(0, view.getUint32(0, true) + 1, true);
|
||||
const take = Math.min(input.length, encryptedBlock.length);
|
||||
for (let j = 0; j < take; j++, pos++)
|
||||
output[pos] = encryptedBlock[j] ^ input[pos];
|
||||
}
|
||||
return new Uint8Array(output);
|
||||
}
|
||||
export async function deriveKeys(key, nonce) {
|
||||
NONCE_LIMIT(nonce.length);
|
||||
const len = key.length;
|
||||
if (len !== 16 && len !== 32)
|
||||
throw new Error(`key length must be 16 or 32 bytes, got: ${len} bytes`);
|
||||
const encKey = new Uint8Array(len);
|
||||
const authKey = new Uint8Array(16);
|
||||
let counter = 0;
|
||||
const deriveBlock = new Uint8Array(nonce.length + 4);
|
||||
deriveBlock.set(nonce, 4);
|
||||
const view = createView(deriveBlock);
|
||||
for (const derivedKey of [authKey, encKey]) {
|
||||
for (let i = 0; i < derivedKey.length; i += 8) {
|
||||
view.setUint32(0, counter++, true);
|
||||
const block = await encryptBlock(deriveBlock, key);
|
||||
derivedKey.set(block.subarray(0, 8), i);
|
||||
}
|
||||
}
|
||||
return { authKey, encKey };
|
||||
}
|
||||
export async function aes_256_gcm_siv(key, nonce, AAD) {
|
||||
const { encKey, authKey } = await deriveKeys(key, nonce);
|
||||
const computeTag = async (data, AAD) => {
|
||||
const dataPos = wrapPos(AAD.length, 16);
|
||||
const lenPos = wrapPos(dataPos + data.length, 16);
|
||||
const block = new Uint8Array(lenPos + 16);
|
||||
const view = createView(block);
|
||||
block.set(AAD);
|
||||
block.set(data, dataPos);
|
||||
setBigUint64(view, lenPos, BigInt(AAD.length * 8), true);
|
||||
setBigUint64(view, lenPos + 8, BigInt(data.length * 8), true);
|
||||
// Compute the expected tag by XORing S_s and the nonce, clearing the
|
||||
// most significant bit of the last byte and encrypting with the
|
||||
// message-encryption key.
|
||||
const tag = polyval(authKey, block);
|
||||
for (let i = 0; i < 12; i++)
|
||||
tag[i] ^= nonce[i];
|
||||
// Clear the highest bit
|
||||
tag[15] &= 0x7f;
|
||||
return await encryptBlock(tag, encKey);
|
||||
};
|
||||
return {
|
||||
// computeTag,
|
||||
encrypt: async (plaintext) => {
|
||||
AAD_LIMIT(AAD.length);
|
||||
PLAIN_LIMIT(plaintext.length);
|
||||
const tag = await computeTag(plaintext, AAD);
|
||||
const out = new Uint8Array(plaintext.length + 16);
|
||||
out.set(tag, plaintext.length);
|
||||
out.set(await ctr(encKey, tag, plaintext));
|
||||
return out;
|
||||
},
|
||||
decrypt: async (ciphertext) => {
|
||||
CIPHER_LIMIT(ciphertext.length);
|
||||
AAD_LIMIT(AAD.length);
|
||||
const tag = ciphertext.subarray(-16);
|
||||
const plaintext = await ctr(encKey, tag, ciphertext.subarray(0, -16));
|
||||
const expectedTag = await computeTag(plaintext, AAD);
|
||||
if (!equalBytes(tag, expectedTag))
|
||||
throw new Error('invalid poly1305 tag');
|
||||
return plaintext;
|
||||
},
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=siv.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-23
@@ -1,23 +0,0 @@
|
||||
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
|
||||
// node.js versions earlier than v19 don't declare it in global scope.
|
||||
// For node.js, package.js on#exports field mapping rewrites import
|
||||
// from `crypto` to `cryptoNode`, which imports native module.
|
||||
// Makes the utils un-importable in browsers without a bundler.
|
||||
// Once node.js 18 is deprecated, we can just drop the import.
|
||||
import { crypto } from '@noble/ciphers/webcrypto/crypto';
|
||||
/**
|
||||
* Secure PRNG. Uses `crypto.getRandomValues`, which defers to OS.
|
||||
*/
|
||||
export function randomBytes(bytesLength = 32) {
|
||||
if (crypto && typeof crypto.getRandomValues === 'function') {
|
||||
return crypto.getRandomValues(new Uint8Array(bytesLength));
|
||||
}
|
||||
throw new Error('crypto.getRandomValues must be defined');
|
||||
}
|
||||
export function getWebcryptoSubtle() {
|
||||
if (crypto && typeof crypto.subtle === 'object' && crypto.subtle != null) {
|
||||
return crypto.subtle;
|
||||
}
|
||||
throw new Error('crypto.subtle must be defined');
|
||||
}
|
||||
//# sourceMappingURL=utils.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/webcrypto/utils.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,sEAAsE;AACtE,mEAAmE;AACnE,8DAA8D;AAC9D,+DAA+D;AAC/D,8DAA8D;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAEzD;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,WAAW,GAAG,EAAE;IAC1C,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,eAAe,KAAK,UAAU,EAAE;QAC1D,OAAO,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;KAC5D;IACD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI,EAAE;QACxE,OAAO,MAAM,CAAC,MAAM,CAAC;KACtB;IACD,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;AACnD,CAAC"}
|
||||
-1
@@ -1 +0,0 @@
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":""}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
throw new Error('noble-ciphers have no entry-point: consult README for usage');
|
||||
//# sourceMappingURL=index.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":";AAAA,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC"}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
{
|
||||
"name": "@noble/ciphers",
|
||||
"version": "0.2.0",
|
||||
"description": "Auditable & minimal JS implementation of Salsa20, ChaCha, Poly1305 & AES-SIV",
|
||||
"files": [
|
||||
"esm",
|
||||
"src",
|
||||
"webcrypto",
|
||||
"*.js",
|
||||
"*.js.map",
|
||||
"*.d.ts",
|
||||
"*.d.ts.map"
|
||||
],
|
||||
"scripts": {
|
||||
"bench": "node benchmark/aead.js noble && node benchmark/ciphers.js noble",
|
||||
"bench:all": "node benchmark/{aead,ciphers,poly}.js",
|
||||
"bench:install": "cd benchmark && npm install && cd ../../",
|
||||
"build": "npm run build:clean; tsc && tsc -p tsconfig.esm.json",
|
||||
"build:release": "cd build; npm i; npm run build",
|
||||
"build:clean": "rm *.{js,d.ts,js.map,d.ts.map} esm/*.{js,d.ts,js.map,d.ts.map} 2> /dev/null; rm -r esm/webcrypto 2> /dev/null",
|
||||
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/**/*.{js,ts,mjs}'",
|
||||
"format": "prettier --write 'src/**/*.{js,ts}' 'test/**/*.{js,ts,mjs}'",
|
||||
"test": "node test/index.js"
|
||||
},
|
||||
"author": "Paul Miller (https://paulmillr.com)",
|
||||
"homepage": "https://paulmillr.com/noble/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paulmillr/noble-ciphers.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@scure/base": "1.1.1",
|
||||
"fast-check": "3.0.0",
|
||||
"micro-bmark": "0.3.1",
|
||||
"micro-should": "0.4.0",
|
||||
"prettier": "2.8.4",
|
||||
"typescript": "5.0.2"
|
||||
},
|
||||
"main": "index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./index.d.ts",
|
||||
"import": "./esm/index.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./webcrypto/crypto": {
|
||||
"types": "./webcrypto/crypto.d.ts",
|
||||
"node": {
|
||||
"import": "./esm/webcrypto/cryptoNode.js",
|
||||
"default": "./webcrypto/cryptoNode.js"
|
||||
},
|
||||
"import": "./esm/webcrypto/crypto.js",
|
||||
"default": "./webcrypto/crypto.js"
|
||||
},
|
||||
"./_micro": {
|
||||
"types": "./_micro.d.ts",
|
||||
"import": "./esm/_micro.js",
|
||||
"default": "./_micro.js"
|
||||
},
|
||||
"./_poly1305": {
|
||||
"types": "./_poly1305.d.ts",
|
||||
"import": "./esm/_poly1305.js",
|
||||
"default": "./_poly1305.js"
|
||||
},
|
||||
"./chacha": {
|
||||
"types": "./chacha.d.ts",
|
||||
"import": "./esm/chacha.js",
|
||||
"default": "./chacha.js"
|
||||
},
|
||||
"./salsa": {
|
||||
"types": "./salsa.d.ts",
|
||||
"import": "./esm/salsa.js",
|
||||
"default": "./salsa.js"
|
||||
},
|
||||
"./simple": {
|
||||
"types": "./simple.d.ts",
|
||||
"import": "./esm/simple.js",
|
||||
"default": "./simple.js"
|
||||
},
|
||||
"./utils": {
|
||||
"types": "./utils.d.ts",
|
||||
"import": "./esm/utils.js",
|
||||
"default": "./utils.js"
|
||||
},
|
||||
"./index": {
|
||||
"types": "./index.d.ts",
|
||||
"import": "./esm/index.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./webcrypto/aes": {
|
||||
"types": "./webcrypto/aes.d.ts",
|
||||
"import": "./esm/webcrypto/aes.js",
|
||||
"default": "./webcrypto/aes.js"
|
||||
},
|
||||
"./webcrypto/siv": {
|
||||
"types": "./webcrypto/siv.d.ts",
|
||||
"import": "./esm/webcrypto/siv.js",
|
||||
"default": "./webcrypto/siv.js"
|
||||
},
|
||||
"./webcrypto/ff1": {
|
||||
"types": "./webcrypto/ff1.d.ts",
|
||||
"import": "./esm/webcrypto/ff1.js",
|
||||
"default": "./webcrypto/ff1.js"
|
||||
},
|
||||
"./webcrypto/utils": {
|
||||
"types": "./webcrypto/utils.d.ts",
|
||||
"import": "./esm/webcrypto/utils.js",
|
||||
"default": "./webcrypto/utils.js"
|
||||
}
|
||||
},
|
||||
"browser": {
|
||||
"node:crypto": false,
|
||||
"./webcrypto/crypto": "./webcrypto/crypto.js"
|
||||
},
|
||||
"keywords": [
|
||||
"salsa20",
|
||||
"chacha",
|
||||
"aes",
|
||||
"cryptography",
|
||||
"crypto",
|
||||
"noble",
|
||||
"cipher",
|
||||
"ciphers",
|
||||
"xsalsa20",
|
||||
"xchacha20",
|
||||
"poly1305",
|
||||
"xsalsa20poly1305",
|
||||
"chacha20poly1305",
|
||||
"xchacha20poly1305",
|
||||
"secretbox",
|
||||
"rijndael",
|
||||
"siv"
|
||||
],
|
||||
"funding": "https://paulmillr.com/funding/"
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
import { Cipher } from './utils.js';
|
||||
/**
|
||||
* hsalsa hashing function, used primarily in xsalsa, to hash
|
||||
* key and nonce into key' and nonce'.
|
||||
* Same as salsaCore, but there doesn't seem to be a way to move the block
|
||||
* out without 25% performance hit.
|
||||
*/
|
||||
export declare function hsalsa(c: Uint32Array, key: Uint8Array, nonce: Uint8Array, out: Uint8Array): Uint8Array;
|
||||
/**
|
||||
* Salsa20 from original paper.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
export declare const salsa20: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* xsalsa20 eXtended-nonce salsa.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
export declare const xsalsa20: (key: Uint8Array, nonce: Uint8Array, data: Uint8Array, output?: Uint8Array | undefined, counter?: number) => Uint8Array;
|
||||
/**
|
||||
* xsalsa20-poly1305 eXtended-nonce salsa.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
* Also known as secretbox from libsodium / nacl.
|
||||
*/
|
||||
export declare const xsalsa20poly1305: (key: Uint8Array, nonce: Uint8Array) => Cipher;
|
||||
//# sourceMappingURL=salsa.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"salsa.d.ts","sourceRoot":"","sources":["src/salsa.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,MAAM,EAAE,MAAM,YAAY,CAAC;AA4DlE;;;;;GAKG;AAEH,wBAAgB,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAAG,UAAU,CAoCtG;AAED;;;GAGG;AACH,eAAO,MAAM,OAAO,yHAAsE,CAAC;AAE3F;;;GAGG;AACH,eAAO,MAAM,QAAQ,yHAKnB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAS,UAAU,SAAS,UAAU,KAAG,MAgDrE,CAAC"}
|
||||
-212
@@ -1,212 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.xsalsa20poly1305 = exports.xsalsa20 = exports.salsa20 = exports.hsalsa = void 0;
|
||||
const utils_js_1 = require("./utils.js");
|
||||
const _salsa_js_1 = require("./_salsa.js");
|
||||
const _poly1305_js_1 = require("./_poly1305.js");
|
||||
// Salsa20 stream cipher was released in 2005.
|
||||
// Salsa's goal was to implement AES replacement that does not rely on S-Boxes,
|
||||
// which are hard to implement in a constant-time manner.
|
||||
// https://cr.yp.to/snuffle.html, https://cr.yp.to/snuffle/salsafamily-20071225.pdf
|
||||
// Left rotate for uint32
|
||||
const rotl = (a, b) => (a << b) | (a >>> (32 - b));
|
||||
/**
|
||||
* Salsa20 core function.
|
||||
*/
|
||||
// prettier-ignore
|
||||
function salsaCore(c, k, i, out, cnt, rounds = 20) {
|
||||
// Based on https://cr.yp.to/salsa20.html
|
||||
let y00 = c[0], y01 = k[0], y02 = k[1], y03 = k[2]; // "expa" Key Key Key
|
||||
let y04 = k[3], y05 = c[1], y06 = i[0], y07 = i[1]; // Key "nd 3" Nonce Nonce
|
||||
let y08 = cnt, y09 = 0, y10 = c[2], y11 = k[4]; // Pos. Pos. "2-by" Key
|
||||
let y12 = k[5], y13 = k[6], y14 = k[7], y15 = c[3]; // Key Key Key "te k"
|
||||
// Save state to temporary variables
|
||||
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
||||
// Main loop
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
x04 ^= rotl(x00 + x12 | 0, 7);
|
||||
x08 ^= rotl(x04 + x00 | 0, 9);
|
||||
x12 ^= rotl(x08 + x04 | 0, 13);
|
||||
x00 ^= rotl(x12 + x08 | 0, 18);
|
||||
x09 ^= rotl(x05 + x01 | 0, 7);
|
||||
x13 ^= rotl(x09 + x05 | 0, 9);
|
||||
x01 ^= rotl(x13 + x09 | 0, 13);
|
||||
x05 ^= rotl(x01 + x13 | 0, 18);
|
||||
x14 ^= rotl(x10 + x06 | 0, 7);
|
||||
x02 ^= rotl(x14 + x10 | 0, 9);
|
||||
x06 ^= rotl(x02 + x14 | 0, 13);
|
||||
x10 ^= rotl(x06 + x02 | 0, 18);
|
||||
x03 ^= rotl(x15 + x11 | 0, 7);
|
||||
x07 ^= rotl(x03 + x15 | 0, 9);
|
||||
x11 ^= rotl(x07 + x03 | 0, 13);
|
||||
x15 ^= rotl(x11 + x07 | 0, 18);
|
||||
x01 ^= rotl(x00 + x03 | 0, 7);
|
||||
x02 ^= rotl(x01 + x00 | 0, 9);
|
||||
x03 ^= rotl(x02 + x01 | 0, 13);
|
||||
x00 ^= rotl(x03 + x02 | 0, 18);
|
||||
x06 ^= rotl(x05 + x04 | 0, 7);
|
||||
x07 ^= rotl(x06 + x05 | 0, 9);
|
||||
x04 ^= rotl(x07 + x06 | 0, 13);
|
||||
x05 ^= rotl(x04 + x07 | 0, 18);
|
||||
x11 ^= rotl(x10 + x09 | 0, 7);
|
||||
x08 ^= rotl(x11 + x10 | 0, 9);
|
||||
x09 ^= rotl(x08 + x11 | 0, 13);
|
||||
x10 ^= rotl(x09 + x08 | 0, 18);
|
||||
x12 ^= rotl(x15 + x14 | 0, 7);
|
||||
x13 ^= rotl(x12 + x15 | 0, 9);
|
||||
x14 ^= rotl(x13 + x12 | 0, 13);
|
||||
x15 ^= rotl(x14 + x13 | 0, 18);
|
||||
}
|
||||
// Write output
|
||||
let oi = 0;
|
||||
out[oi++] = (y00 + x00) | 0;
|
||||
out[oi++] = (y01 + x01) | 0;
|
||||
out[oi++] = (y02 + x02) | 0;
|
||||
out[oi++] = (y03 + x03) | 0;
|
||||
out[oi++] = (y04 + x04) | 0;
|
||||
out[oi++] = (y05 + x05) | 0;
|
||||
out[oi++] = (y06 + x06) | 0;
|
||||
out[oi++] = (y07 + x07) | 0;
|
||||
out[oi++] = (y08 + x08) | 0;
|
||||
out[oi++] = (y09 + x09) | 0;
|
||||
out[oi++] = (y10 + x10) | 0;
|
||||
out[oi++] = (y11 + x11) | 0;
|
||||
out[oi++] = (y12 + x12) | 0;
|
||||
out[oi++] = (y13 + x13) | 0;
|
||||
out[oi++] = (y14 + x14) | 0;
|
||||
out[oi++] = (y15 + x15) | 0;
|
||||
}
|
||||
/**
|
||||
* hsalsa hashing function, used primarily in xsalsa, to hash
|
||||
* key and nonce into key' and nonce'.
|
||||
* Same as salsaCore, but there doesn't seem to be a way to move the block
|
||||
* out without 25% performance hit.
|
||||
*/
|
||||
// prettier-ignore
|
||||
function hsalsa(c, key, nonce, out) {
|
||||
const k32 = (0, utils_js_1.u32)(key);
|
||||
const i32 = (0, utils_js_1.u32)(nonce);
|
||||
const o32 = (0, utils_js_1.u32)(out);
|
||||
let x00 = c[0], x01 = k32[0], x02 = k32[1], x03 = k32[2], x04 = k32[3];
|
||||
let x05 = c[1], x06 = i32[0], x07 = i32[1], x08 = i32[2], x09 = i32[3];
|
||||
let x10 = c[2], x11 = k32[4], x12 = k32[5], x13 = k32[6], x14 = k32[7];
|
||||
let x15 = c[3];
|
||||
// Main loop
|
||||
for (let i = 0; i < 20; i += 2) {
|
||||
x04 ^= rotl(x00 + x12 | 0, 7);
|
||||
x08 ^= rotl(x04 + x00 | 0, 9);
|
||||
x12 ^= rotl(x08 + x04 | 0, 13);
|
||||
x00 ^= rotl(x12 + x08 | 0, 18);
|
||||
x09 ^= rotl(x05 + x01 | 0, 7);
|
||||
x13 ^= rotl(x09 + x05 | 0, 9);
|
||||
x01 ^= rotl(x13 + x09 | 0, 13);
|
||||
x05 ^= rotl(x01 + x13 | 0, 18);
|
||||
x14 ^= rotl(x10 + x06 | 0, 7);
|
||||
x02 ^= rotl(x14 + x10 | 0, 9);
|
||||
x06 ^= rotl(x02 + x14 | 0, 13);
|
||||
x10 ^= rotl(x06 + x02 | 0, 18);
|
||||
x03 ^= rotl(x15 + x11 | 0, 7);
|
||||
x07 ^= rotl(x03 + x15 | 0, 9);
|
||||
x11 ^= rotl(x07 + x03 | 0, 13);
|
||||
x15 ^= rotl(x11 + x07 | 0, 18);
|
||||
x01 ^= rotl(x00 + x03 | 0, 7);
|
||||
x02 ^= rotl(x01 + x00 | 0, 9);
|
||||
x03 ^= rotl(x02 + x01 | 0, 13);
|
||||
x00 ^= rotl(x03 + x02 | 0, 18);
|
||||
x06 ^= rotl(x05 + x04 | 0, 7);
|
||||
x07 ^= rotl(x06 + x05 | 0, 9);
|
||||
x04 ^= rotl(x07 + x06 | 0, 13);
|
||||
x05 ^= rotl(x04 + x07 | 0, 18);
|
||||
x11 ^= rotl(x10 + x09 | 0, 7);
|
||||
x08 ^= rotl(x11 + x10 | 0, 9);
|
||||
x09 ^= rotl(x08 + x11 | 0, 13);
|
||||
x10 ^= rotl(x09 + x08 | 0, 18);
|
||||
x12 ^= rotl(x15 + x14 | 0, 7);
|
||||
x13 ^= rotl(x12 + x15 | 0, 9);
|
||||
x14 ^= rotl(x13 + x12 | 0, 13);
|
||||
x15 ^= rotl(x14 + x13 | 0, 18);
|
||||
}
|
||||
o32[0] = x00;
|
||||
o32[1] = x05;
|
||||
o32[2] = x10;
|
||||
o32[3] = x15;
|
||||
o32[4] = x06;
|
||||
o32[5] = x07;
|
||||
o32[6] = x08;
|
||||
o32[7] = x09;
|
||||
return out;
|
||||
}
|
||||
exports.hsalsa = hsalsa;
|
||||
/**
|
||||
* Salsa20 from original paper.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
exports.salsa20 = (0, _salsa_js_1.salsaBasic)({ core: salsaCore, counterRight: true });
|
||||
/**
|
||||
* xsalsa20 eXtended-nonce salsa.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
exports.xsalsa20 = (0, _salsa_js_1.salsaBasic)({
|
||||
core: salsaCore,
|
||||
counterRight: true,
|
||||
extendNonceFn: hsalsa,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
/**
|
||||
* xsalsa20-poly1305 eXtended-nonce salsa.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
* Also known as secretbox from libsodium / nacl.
|
||||
*/
|
||||
const xsalsa20poly1305 = (key, nonce) => {
|
||||
const tagLength = 16;
|
||||
(0, utils_js_1.ensureBytes)(key, 32);
|
||||
(0, utils_js_1.ensureBytes)(nonce, 24);
|
||||
return {
|
||||
tagLength,
|
||||
encrypt: (plaintext, output) => {
|
||||
(0, utils_js_1.ensureBytes)(plaintext);
|
||||
// This is small optimization (calculate auth key with same call as encryption itself) makes it hard
|
||||
// to separate tag calculation and encryption itself, since 32 byte is half-block of salsa (64 byte)
|
||||
const clength = plaintext.length + 32;
|
||||
if (output) {
|
||||
(0, utils_js_1.ensureBytes)(output, clength);
|
||||
}
|
||||
else {
|
||||
output = new Uint8Array(clength);
|
||||
}
|
||||
output.set(plaintext, 32);
|
||||
(0, exports.xsalsa20)(key, nonce, output, output);
|
||||
const authKey = output.subarray(0, 32);
|
||||
const tag = (0, _poly1305_js_1.poly1305)(output.subarray(32), authKey);
|
||||
// Clean auth key, even though JS provides no guarantees about memory cleaning
|
||||
output.set(tag, tagLength);
|
||||
output.subarray(0, tagLength).fill(0);
|
||||
return output.subarray(tagLength);
|
||||
},
|
||||
decrypt: (ciphertext) => {
|
||||
(0, utils_js_1.ensureBytes)(ciphertext);
|
||||
const clength = ciphertext.length;
|
||||
if (clength < tagLength)
|
||||
throw new Error('encrypted data should be at least 16 bytes');
|
||||
// Create new ciphertext array:
|
||||
// auth tag auth tag from ciphertext ciphertext
|
||||
// [bytes 0..16] [bytes 16..32] [bytes 32..]
|
||||
// 16 instead of 32, because we already have 16 byte tag
|
||||
const ciphertext_ = new Uint8Array(clength + tagLength); // alloc
|
||||
ciphertext_.set(ciphertext, tagLength);
|
||||
// Each xsalsa20 calls to hsalsa to calculate key, but seems not much perf difference
|
||||
// Separate call to calculate authkey, since first bytes contains tag
|
||||
const authKey = (0, exports.xsalsa20)(key, nonce, new Uint8Array(32)); // alloc(32)
|
||||
const tag = (0, _poly1305_js_1.poly1305)(ciphertext_.subarray(32), authKey);
|
||||
if (!(0, utils_js_1.equalBytes)(ciphertext_.subarray(16, 32), tag))
|
||||
throw new Error('invalid tag');
|
||||
const plaintext = (0, exports.xsalsa20)(key, nonce, ciphertext_); // alloc
|
||||
// Clean auth key, even though JS provides no guarantees about memory cleaning
|
||||
plaintext.subarray(0, 32).fill(0);
|
||||
authKey.fill(0);
|
||||
return plaintext.subarray(32);
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.xsalsa20poly1305 = xsalsa20poly1305;
|
||||
//# sourceMappingURL=salsa.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-23
@@ -1,23 +0,0 @@
|
||||
import { utf8ToBytes } from './utils.js';
|
||||
export { utf8ToBytes };
|
||||
/**
|
||||
* Alias to xsalsa20poly1305, for compatibility with libsodium / nacl
|
||||
*/
|
||||
export declare function secretbox(key: Uint8Array, nonce: Uint8Array): {
|
||||
seal: (plaintext: Uint8Array) => Uint8Array;
|
||||
open: (ciphertext: Uint8Array) => Uint8Array;
|
||||
};
|
||||
export declare function randomKey(): Uint8Array;
|
||||
/**
|
||||
* Encrypt plaintext under key with random nonce, using xchacha20poly1305.
|
||||
* User never touches nonce: it is prepended to ciphertext.
|
||||
*/
|
||||
export declare function encrypt(key: Uint8Array, plaintext: Uint8Array): Uint8Array;
|
||||
/**
|
||||
* Decrypt plaintext under key with random nonce, using xchacha20poly1305.
|
||||
* User never touches nonce: it is prepended to ciphertext.
|
||||
*/
|
||||
export declare function decrypt(key: Uint8Array, ciphertext: Uint8Array): Uint8Array;
|
||||
export declare function aes_encrypt(key: Uint8Array, plaintext: Uint8Array): Promise<Uint8Array>;
|
||||
export declare function aes_decrypt(key: Uint8Array, ciphertext: Uint8Array): Promise<Uint8Array>;
|
||||
//# sourceMappingURL=simple.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"simple.d.ts","sourceRoot":"","sources":["src/simple.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,WAAW,EAAE,MAAM,YAAY,CAAC;AAInE,OAAO,EAAE,WAAW,EAAE,CAAC;AAEvB;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU;;;EAK3D;AAED,wBAAgB,SAAS,IAAI,UAAU,CAEtC;AACD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG,UAAU,CAK1E;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,CAO3E;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAM7F;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAO9F"}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.aes_decrypt = exports.aes_encrypt = exports.decrypt = exports.encrypt = exports.randomKey = exports.secretbox = exports.utf8ToBytes = void 0;
|
||||
const chacha_js_1 = require("./chacha.js");
|
||||
const salsa_js_1 = require("./salsa.js");
|
||||
const utils_js_1 = require("./utils.js");
|
||||
Object.defineProperty(exports, "utf8ToBytes", { enumerable: true, get: function () { return utils_js_1.utf8ToBytes; } });
|
||||
const aes_js_1 = require("./webcrypto/aes.js");
|
||||
const utils_js_2 = require("./webcrypto/utils.js");
|
||||
/**
|
||||
* Alias to xsalsa20poly1305, for compatibility with libsodium / nacl
|
||||
*/
|
||||
function secretbox(key, nonce) {
|
||||
(0, utils_js_1.ensureBytes)(key);
|
||||
(0, utils_js_1.ensureBytes)(nonce);
|
||||
const xs = (0, salsa_js_1.xsalsa20poly1305)(key, nonce);
|
||||
return { seal: xs.encrypt, open: xs.decrypt };
|
||||
}
|
||||
exports.secretbox = secretbox;
|
||||
function randomKey() {
|
||||
return (0, utils_js_2.randomBytes)(32);
|
||||
}
|
||||
exports.randomKey = randomKey;
|
||||
/**
|
||||
* Encrypt plaintext under key with random nonce, using xchacha20poly1305.
|
||||
* User never touches nonce: it is prepended to ciphertext.
|
||||
*/
|
||||
function encrypt(key, plaintext) {
|
||||
(0, utils_js_1.ensureBytes)(key);
|
||||
const nonce = (0, utils_js_2.randomBytes)(24);
|
||||
const ciphertext = (0, chacha_js_1.xchacha20poly1305)(key, nonce).encrypt(plaintext);
|
||||
return (0, utils_js_1.concatBytes)(nonce, ciphertext);
|
||||
}
|
||||
exports.encrypt = encrypt;
|
||||
/**
|
||||
* Decrypt plaintext under key with random nonce, using xchacha20poly1305.
|
||||
* User never touches nonce: it is prepended to ciphertext.
|
||||
*/
|
||||
function decrypt(key, ciphertext) {
|
||||
const nonceLength = 24;
|
||||
(0, utils_js_1.ensureBytes)(ciphertext);
|
||||
if (ciphertext.length <= nonceLength)
|
||||
throw new Error('invalid ciphertext length');
|
||||
const nonce = ciphertext.subarray(0, nonceLength);
|
||||
const ciphertextWithoutNonce = ciphertext.subarray(nonceLength);
|
||||
return (0, chacha_js_1.xchacha20poly1305)(key, nonce).decrypt(ciphertextWithoutNonce);
|
||||
}
|
||||
exports.decrypt = decrypt;
|
||||
async function aes_encrypt(key, plaintext) {
|
||||
const nonceLength = 12;
|
||||
(0, utils_js_1.ensureBytes)(key);
|
||||
const nonce = (0, utils_js_2.randomBytes)(nonceLength);
|
||||
const ciphertext = await (0, aes_js_1.aes_256_gcm)(key, nonce).encrypt(plaintext);
|
||||
return (0, utils_js_1.concatBytes)(nonce, ciphertext);
|
||||
}
|
||||
exports.aes_encrypt = aes_encrypt;
|
||||
async function aes_decrypt(key, ciphertext) {
|
||||
const nonceLength = 12;
|
||||
(0, utils_js_1.ensureBytes)(ciphertext);
|
||||
if (ciphertext.length <= nonceLength)
|
||||
throw new Error('invalid ciphertext length');
|
||||
const nonce = ciphertext.subarray(0, nonceLength);
|
||||
const ciphertextWithoutNonce = ciphertext.subarray(nonceLength);
|
||||
return (0, aes_js_1.aes_256_gcm)(key, nonce).decrypt(ciphertextWithoutNonce);
|
||||
}
|
||||
exports.aes_decrypt = aes_decrypt;
|
||||
//# sourceMappingURL=simple.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"simple.js","sourceRoot":"","sources":["src/simple.ts"],"names":[],"mappings":";;;AAAA,2CAAgD;AAChD,yCAA8C;AAC9C,yCAAmE;AAI1D,4FAJ0B,sBAAW,OAI1B;AAHpB,+CAAiD;AACjD,mDAAmD;AAInD;;GAEG;AACH,SAAgB,SAAS,CAAC,GAAe,EAAE,KAAiB;IAC1D,IAAA,sBAAW,EAAC,GAAG,CAAC,CAAC;IACjB,IAAA,sBAAW,EAAC,KAAK,CAAC,CAAC;IACnB,MAAM,EAAE,GAAG,IAAA,2BAAgB,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC;AAChD,CAAC;AALD,8BAKC;AAED,SAAgB,SAAS;IACvB,OAAO,IAAA,sBAAW,EAAC,EAAE,CAAC,CAAC;AACzB,CAAC;AAFD,8BAEC;AACD;;;GAGG;AACH,SAAgB,OAAO,CAAC,GAAe,EAAE,SAAqB;IAC5D,IAAA,sBAAW,EAAC,GAAG,CAAC,CAAC;IACjB,MAAM,KAAK,GAAG,IAAA,sBAAW,EAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,UAAU,GAAG,IAAA,6BAAiB,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpE,OAAO,IAAA,sBAAW,EAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AALD,0BAKC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,GAAe,EAAE,UAAsB;IAC7D,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,IAAA,sBAAW,EAAC,UAAU,CAAC,CAAC;IACxB,IAAI,UAAU,CAAC,MAAM,IAAI,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACnF,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,sBAAsB,GAAG,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChE,OAAO,IAAA,6BAAiB,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;AACvE,CAAC;AAPD,0BAOC;AAEM,KAAK,UAAU,WAAW,CAAC,GAAe,EAAE,SAAqB;IACtE,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,IAAA,sBAAW,EAAC,GAAG,CAAC,CAAC;IACjB,MAAM,KAAK,GAAG,IAAA,sBAAW,EAAC,WAAW,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,MAAM,IAAA,oBAAW,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpE,OAAO,IAAA,sBAAW,EAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAND,kCAMC;AAEM,KAAK,UAAU,WAAW,CAAC,GAAe,EAAE,UAAsB;IACvE,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,IAAA,sBAAW,EAAC,UAAU,CAAC,CAAC;IACxB,IAAI,UAAU,CAAC,MAAM,IAAI,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACnF,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,sBAAsB,GAAG,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChE,OAAO,IAAA,oBAAW,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;AACjE,CAAC;AAPD,kCAOC"}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
function number(n: number) {
|
||||
if (!Number.isSafeInteger(n) || n < 0) throw new Error(`Wrong positive integer: ${n}`);
|
||||
}
|
||||
|
||||
function bool(b: boolean) {
|
||||
if (typeof b !== 'boolean') throw new Error(`Expected boolean, not ${b}`);
|
||||
}
|
||||
|
||||
function bytes(b: Uint8Array | undefined, ...lengths: number[]) {
|
||||
if (!(b instanceof Uint8Array)) throw new Error('Expected Uint8Array');
|
||||
if (lengths.length > 0 && !lengths.includes(b.length))
|
||||
throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`);
|
||||
}
|
||||
|
||||
export type Hash = {
|
||||
(data: Uint8Array): Uint8Array;
|
||||
blockLen: number;
|
||||
outputLen: number;
|
||||
create: any;
|
||||
};
|
||||
function hash(hash: Hash) {
|
||||
if (typeof hash !== 'function' || typeof hash.create !== 'function')
|
||||
throw new Error('hash must be wrapped by utils.wrapConstructor');
|
||||
number(hash.outputLen);
|
||||
number(hash.blockLen);
|
||||
}
|
||||
|
||||
function exists(instance: any, checkFinished = true) {
|
||||
if (instance.destroyed) throw new Error('Hash instance has been destroyed');
|
||||
if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');
|
||||
}
|
||||
function output(out: any, instance: any) {
|
||||
bytes(out);
|
||||
const min = instance.outputLen;
|
||||
if (out.length < min) {
|
||||
throw new Error(`digestInto() expects output buffer of length at least ${min}`);
|
||||
}
|
||||
}
|
||||
|
||||
export { number, bool, bytes, hash, exists, output };
|
||||
const assert = { number, bool, bytes, hash, exists, output };
|
||||
export default assert;
|
||||
-313
@@ -1,313 +0,0 @@
|
||||
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
||||
|
||||
// micro-noble-ciphers: more auditable, but slower version of salsa20, chacha & poly1305.
|
||||
// Implements the same algorithms that are present in other files,
|
||||
// but without unrolled loops (https://en.wikipedia.org/wiki/Loop_unrolling).
|
||||
|
||||
import * as u from './utils.js';
|
||||
import { salsaBasic } from './_salsa.js';
|
||||
// Utils
|
||||
function hexToNumber(hex: string): bigint {
|
||||
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||
// Big Endian
|
||||
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
||||
}
|
||||
function bytesToNumberLE(bytes: Uint8Array): bigint {
|
||||
return hexToNumber(u.bytesToHex(Uint8Array.from(bytes).reverse()));
|
||||
}
|
||||
function numberToBytesLE(n: number | bigint, len: number): Uint8Array {
|
||||
return u.hexToBytes(n.toString(16).padStart(len * 2, '0')).reverse();
|
||||
}
|
||||
|
||||
const rotl = (a: number, b: number) => (a << b) | (a >>> (32 - b));
|
||||
// /Utils
|
||||
|
||||
function salsaQR(x: Uint32Array, a: number, b: number, c: number, d: number) {
|
||||
x[b] ^= rotl((x[a] + x[d]) | 0, 7);
|
||||
x[c] ^= rotl((x[b] + x[a]) | 0, 9);
|
||||
x[d] ^= rotl((x[c] + x[b]) | 0, 13);
|
||||
x[a] ^= rotl((x[d] + x[c]) | 0, 18);
|
||||
}
|
||||
// prettier-ignore
|
||||
function chachaQR(x: Uint32Array, a: number, b: number, c: number, d: number) {
|
||||
x[a] = (x[a] + x[b]) | 0; x[d] = rotl(x[d] ^ x[a], 16);
|
||||
x[c] = (x[c] + x[d]) | 0; x[b] = rotl(x[b] ^ x[c], 12);
|
||||
x[a] = (x[a] + x[b]) | 0; x[d] = rotl(x[d] ^ x[a], 8);
|
||||
x[c] = (x[c] + x[d]) | 0; x[b] = rotl(x[b] ^ x[c], 7);
|
||||
}
|
||||
|
||||
function salsaRound(x: Uint32Array, rounds = 20) {
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
salsaQR(x, 0, 4, 8, 12);
|
||||
salsaQR(x, 5, 9, 13, 1);
|
||||
salsaQR(x, 10, 14, 2, 6);
|
||||
salsaQR(x, 15, 3, 7, 11);
|
||||
salsaQR(x, 0, 1, 2, 3);
|
||||
salsaQR(x, 5, 6, 7, 4);
|
||||
salsaQR(x, 10, 11, 8, 9);
|
||||
salsaQR(x, 15, 12, 13, 14);
|
||||
}
|
||||
}
|
||||
|
||||
function chachaRound(x: Uint32Array, rounds = 20) {
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
chachaQR(x, 0, 4, 8, 12);
|
||||
chachaQR(x, 1, 5, 9, 13);
|
||||
chachaQR(x, 2, 6, 10, 14);
|
||||
chachaQR(x, 3, 7, 11, 15);
|
||||
chachaQR(x, 0, 5, 10, 15);
|
||||
chachaQR(x, 1, 6, 11, 12);
|
||||
chachaQR(x, 2, 7, 8, 13);
|
||||
chachaQR(x, 3, 4, 9, 14);
|
||||
}
|
||||
}
|
||||
|
||||
function salsaCore(
|
||||
c: Uint32Array,
|
||||
k: Uint32Array,
|
||||
n: Uint32Array,
|
||||
out: Uint32Array,
|
||||
cnt: number,
|
||||
rounds = 20
|
||||
): void {
|
||||
// prettier-ignore
|
||||
const y = new Uint32Array([
|
||||
c[0], k[0], k[1], k[2], // "expa" Key Key Key
|
||||
k[3], c[1], n[0], n[1], // Key "nd 3" Nonce Nonce
|
||||
cnt, 0 , c[2], k[4], // Pos. Pos. "2-by" Key
|
||||
k[5], k[6], k[7], c[3], // Key Key Key "te k"
|
||||
]);
|
||||
const x = y.slice();
|
||||
salsaRound(x, rounds);
|
||||
for (let i = 0; i < 16; i++) out[i] = (y[i] + x[i]) | 0;
|
||||
}
|
||||
|
||||
export function hsalsa(c: Uint32Array, key: Uint8Array, nonce: Uint8Array): Uint8Array {
|
||||
const k = u.u32(key);
|
||||
const i = u.u32(nonce);
|
||||
// prettier-ignore
|
||||
const x = new Uint32Array([
|
||||
c[0], k[0], k[1], k[2],
|
||||
k[3], c[1], i[0], i[1],
|
||||
i[2], i[3], c[2], k[4],
|
||||
k[5], k[6], k[7], c[3]
|
||||
]);
|
||||
salsaRound(x);
|
||||
return u.u8(new Uint32Array([x[0], x[5], x[10], x[15], x[6], x[7], x[8], x[9]]));
|
||||
}
|
||||
|
||||
function chachaCore(
|
||||
c: Uint32Array,
|
||||
k: Uint32Array,
|
||||
n: Uint32Array,
|
||||
out: Uint32Array,
|
||||
cnt: number,
|
||||
rounds = 20
|
||||
): void {
|
||||
// prettier-ignore
|
||||
const y = new Uint32Array([
|
||||
c[0], c[1], c[2], c[3], // "expa" "nd 3" "2-by" "te k"
|
||||
k[0], k[1], k[2], k[3], // Key Key Key Key
|
||||
k[4], k[5], k[6], k[7], // Key Key Key Key
|
||||
cnt, n[0], n[1], n[2], // Counter Counter Nonce Nonce
|
||||
]);
|
||||
const x = y.slice();
|
||||
chachaRound(x, rounds);
|
||||
for (let i = 0; i < 16; i++) out[i] = (y[i] + x[i]) | 0;
|
||||
}
|
||||
|
||||
export function hchacha(c: Uint32Array, key: Uint8Array, nonce: Uint8Array): Uint8Array {
|
||||
const k = u.u32(key);
|
||||
const i = u.u32(nonce);
|
||||
// prettier-ignore
|
||||
const x = new Uint32Array([
|
||||
c[0], c[1], c[2], c[3],
|
||||
k[0], k[1], k[2], k[3],
|
||||
k[4], k[5], k[6], k[7],
|
||||
i[0], i[1], i[2], i[3],
|
||||
]);
|
||||
chachaRound(x);
|
||||
return u.u8(new Uint32Array([x[0], x[1], x[2], x[3], x[12], x[13], x[14], x[15]]));
|
||||
}
|
||||
|
||||
/**
|
||||
* salsa20, 12-byte nonce.
|
||||
*/
|
||||
export const salsa20 = salsaBasic({ core: salsaCore, counterRight: true });
|
||||
|
||||
/**
|
||||
* xsalsa20, 24-byte nonce.
|
||||
*/
|
||||
export const xsalsa20 = salsaBasic({
|
||||
core: salsaCore,
|
||||
counterRight: true,
|
||||
extendNonceFn: hsalsa,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* chacha20 non-RFC, original version by djb. 8-byte nonce, 8-byte counter.
|
||||
*/
|
||||
export const chacha20orig = salsaBasic({ core: chachaCore, counterRight: false, counterLen: 8 });
|
||||
/**
|
||||
* chacha20 RFC 8439 (IETF / TLS). 12-byte nonce, 4-byte counter.
|
||||
*/
|
||||
export const chacha20 = salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* xchacha20 eXtended-nonce. https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
*/
|
||||
export const xchacha20 = salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 8,
|
||||
extendNonceFn: hchacha,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* 8-round chacha from the original paper.
|
||||
*/
|
||||
export const chacha8 = salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 8,
|
||||
});
|
||||
|
||||
/**
|
||||
* 12-round chacha from the original paper.
|
||||
*/
|
||||
export const chacha12 = salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 12,
|
||||
});
|
||||
|
||||
const POW_2_130_5 = 2n ** 130n - 5n;
|
||||
const POW_2_128_1 = 2n ** (16n * 8n) - 1n;
|
||||
// Can be speed-up using BigUint64Array, but would be more complicated
|
||||
export function poly1305(msg: Uint8Array, key: Uint8Array): Uint8Array {
|
||||
u.ensureBytes(msg);
|
||||
u.ensureBytes(key);
|
||||
let acc = 0n;
|
||||
const r = bytesToNumberLE(key.subarray(0, 16)) & 0x0ffffffc0ffffffc0ffffffc0fffffffn;
|
||||
const s = bytesToNumberLE(key.subarray(16));
|
||||
// Process by 16 byte chunks
|
||||
for (let i = 0; i < msg.length; i += 16) {
|
||||
const m = msg.subarray(i, i + 16);
|
||||
const n = bytesToNumberLE(m) | (1n << BigInt(8 * m.length));
|
||||
acc = ((acc + n) * r) % POW_2_130_5;
|
||||
}
|
||||
const res = (acc + s) & POW_2_128_1;
|
||||
return numberToBytesLE(res, 16);
|
||||
}
|
||||
|
||||
function computeTag(
|
||||
fn: typeof chacha20,
|
||||
key: Uint8Array,
|
||||
nonce: Uint8Array,
|
||||
ciphertext: Uint8Array,
|
||||
AAD?: Uint8Array
|
||||
): Uint8Array {
|
||||
const res = [];
|
||||
if (AAD) {
|
||||
res.push(AAD);
|
||||
const leftover = AAD.length % 16;
|
||||
if (leftover > 0) res.push(new Uint8Array(16 - leftover));
|
||||
}
|
||||
res.push(ciphertext);
|
||||
const leftover = ciphertext.length % 16;
|
||||
if (leftover > 0) res.push(new Uint8Array(16 - leftover));
|
||||
// Lengths
|
||||
const num = new Uint8Array(16);
|
||||
const view = u.createView(num);
|
||||
u.setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
|
||||
u.setBigUint64(view, 8, BigInt(ciphertext.length), true);
|
||||
res.push(num);
|
||||
const authKey = fn(key, nonce, new Uint8Array(32));
|
||||
return poly1305(u.concatBytes(...res), authKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* xsalsa20-poly1305 eXtended-nonce (24 bytes) salsa.
|
||||
*/
|
||||
export function xsalsa20poly1305(key: Uint8Array, nonce: Uint8Array) {
|
||||
u.ensureBytes(key);
|
||||
u.ensureBytes(nonce);
|
||||
return {
|
||||
encrypt: (plaintext: Uint8Array) => {
|
||||
u.ensureBytes(plaintext);
|
||||
const m = u.concatBytes(new Uint8Array(32), plaintext);
|
||||
const c = xsalsa20(key, nonce, m);
|
||||
const authKey = c.subarray(0, 32);
|
||||
const data = c.subarray(32);
|
||||
const tag = poly1305(data, authKey);
|
||||
return u.concatBytes(tag, data);
|
||||
},
|
||||
decrypt: (ciphertext: Uint8Array) => {
|
||||
u.ensureBytes(ciphertext);
|
||||
if (ciphertext.length < 16) throw new Error('encrypted data must be at least 16 bytes');
|
||||
const c = u.concatBytes(new Uint8Array(16), ciphertext);
|
||||
const authKey = xsalsa20(key, nonce, new Uint8Array(32));
|
||||
const tag = poly1305(c.subarray(32), authKey);
|
||||
if (!u.equalBytes(c.subarray(16, 32), tag)) throw new Error('invalid poly1305 tag');
|
||||
return xsalsa20(key, nonce, c).subarray(32);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias to xsalsa20-poly1305
|
||||
*/
|
||||
export function secretbox(key: Uint8Array, nonce: Uint8Array) {
|
||||
u.ensureBytes(key);
|
||||
u.ensureBytes(nonce);
|
||||
const xs = xsalsa20poly1305(key, nonce);
|
||||
return { seal: xs.encrypt, open: xs.decrypt };
|
||||
}
|
||||
|
||||
export const _poly1305_aead =
|
||||
(fn: typeof chacha20) =>
|
||||
(key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array): u.Cipher => {
|
||||
const tagLength = 16;
|
||||
const keyLength = 32;
|
||||
u.ensureBytes(key, keyLength);
|
||||
u.ensureBytes(nonce);
|
||||
return {
|
||||
tagLength,
|
||||
encrypt: (plaintext: Uint8Array) => {
|
||||
u.ensureBytes(plaintext);
|
||||
const res = fn(key, nonce, plaintext, undefined, 1);
|
||||
const tag = computeTag(fn, key, nonce, res, AAD);
|
||||
return u.concatBytes(res, tag);
|
||||
},
|
||||
decrypt: (ciphertext: Uint8Array) => {
|
||||
u.ensureBytes(ciphertext);
|
||||
if (ciphertext.length < tagLength)
|
||||
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
|
||||
const passedTag = ciphertext.subarray(-tagLength);
|
||||
const data = ciphertext.subarray(0, -tagLength);
|
||||
const tag = computeTag(fn, key, nonce, data, AAD);
|
||||
if (!u.equalBytes(passedTag, tag)) throw new Error('invalid poly1305 tag');
|
||||
return fn(key, nonce, data, undefined, 1);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* chacha20-poly1305 12-byte-nonce chacha.
|
||||
*/
|
||||
export const chacha20poly1305 = _poly1305_aead(chacha20);
|
||||
|
||||
/**
|
||||
* xchacha20-poly1305 eXtended-nonce (24 bytes) chacha.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
export const xchacha20poly1305 = _poly1305_aead(xchacha20);
|
||||
-286
@@ -1,286 +0,0 @@
|
||||
import { toBytes, Input, ensureBytes, Hash } from './utils.js';
|
||||
import assert from './_assert.js';
|
||||
|
||||
// Poly1305 is a fast and parallel secret-key message-authentication code.
|
||||
// https://cr.yp.to/mac.html, https://cr.yp.to/mac/poly1305-20050329.pdf
|
||||
// https://datatracker.ietf.org/doc/html/rfc8439
|
||||
|
||||
// Based on Public Domain poly1305-donna https://github.com/floodyberry/poly1305-donna
|
||||
const u8to16 = (a: Uint8Array, i: number) => (a[i++] & 0xff) | ((a[i++] & 0xff) << 8);
|
||||
class Poly1305 implements Hash<Poly1305> {
|
||||
readonly blockLen = 16;
|
||||
readonly outputLen = 16;
|
||||
private buffer = new Uint8Array(16);
|
||||
private r = new Uint16Array(10);
|
||||
private h = new Uint16Array(10);
|
||||
private pad = new Uint16Array(8);
|
||||
private pos = 0;
|
||||
protected finished = false;
|
||||
|
||||
constructor(key: Input) {
|
||||
key = toBytes(key);
|
||||
ensureBytes(key, 32);
|
||||
const t0 = u8to16(key, 0);
|
||||
const t1 = u8to16(key, 2);
|
||||
const t2 = u8to16(key, 4);
|
||||
const t3 = u8to16(key, 6);
|
||||
const t4 = u8to16(key, 8);
|
||||
const t5 = u8to16(key, 10);
|
||||
const t6 = u8to16(key, 12);
|
||||
const t7 = u8to16(key, 14);
|
||||
|
||||
// https://github.com/floodyberry/poly1305-donna/blob/e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781/poly1305-donna-16.h#L47
|
||||
this.r[0] = t0 & 0x1fff;
|
||||
this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
|
||||
this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
|
||||
this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
|
||||
this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
|
||||
this.r[5] = (t4 >>> 1) & 0x1ffe;
|
||||
this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
|
||||
this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
|
||||
this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
|
||||
this.r[9] = (t7 >>> 5) & 0x007f;
|
||||
for (let i = 0; i < 8; i++) this.pad[i] = u8to16(key, 16 + 2 * i);
|
||||
}
|
||||
|
||||
private process(data: Uint8Array, offset: number, isLast = false) {
|
||||
const hibit = isLast ? 0 : 1 << 11;
|
||||
const { h, r } = this;
|
||||
const r0 = r[0];
|
||||
const r1 = r[1];
|
||||
const r2 = r[2];
|
||||
const r3 = r[3];
|
||||
const r4 = r[4];
|
||||
const r5 = r[5];
|
||||
const r6 = r[6];
|
||||
const r7 = r[7];
|
||||
const r8 = r[8];
|
||||
const r9 = r[9];
|
||||
|
||||
const t0 = u8to16(data, offset + 0);
|
||||
const t1 = u8to16(data, offset + 2);
|
||||
const t2 = u8to16(data, offset + 4);
|
||||
const t3 = u8to16(data, offset + 6);
|
||||
const t4 = u8to16(data, offset + 8);
|
||||
const t5 = u8to16(data, offset + 10);
|
||||
const t6 = u8to16(data, offset + 12);
|
||||
const t7 = u8to16(data, offset + 14);
|
||||
|
||||
let h0 = h[0] + (t0 & 0x1fff);
|
||||
let h1 = h[1] + (((t0 >>> 13) | (t1 << 3)) & 0x1fff);
|
||||
let h2 = h[2] + (((t1 >>> 10) | (t2 << 6)) & 0x1fff);
|
||||
let h3 = h[3] + (((t2 >>> 7) | (t3 << 9)) & 0x1fff);
|
||||
let h4 = h[4] + (((t3 >>> 4) | (t4 << 12)) & 0x1fff);
|
||||
let h5 = h[5] + ((t4 >>> 1) & 0x1fff);
|
||||
let h6 = h[6] + (((t4 >>> 14) | (t5 << 2)) & 0x1fff);
|
||||
let h7 = h[7] + (((t5 >>> 11) | (t6 << 5)) & 0x1fff);
|
||||
let h8 = h[8] + (((t6 >>> 8) | (t7 << 8)) & 0x1fff);
|
||||
let h9 = h[9] + ((t7 >>> 5) | hibit);
|
||||
|
||||
let c = 0;
|
||||
|
||||
let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);
|
||||
c = d0 >>> 13;
|
||||
d0 &= 0x1fff;
|
||||
d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);
|
||||
c += d0 >>> 13;
|
||||
d0 &= 0x1fff;
|
||||
|
||||
let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);
|
||||
c = d1 >>> 13;
|
||||
d1 &= 0x1fff;
|
||||
d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);
|
||||
c += d1 >>> 13;
|
||||
d1 &= 0x1fff;
|
||||
|
||||
let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);
|
||||
c = d2 >>> 13;
|
||||
d2 &= 0x1fff;
|
||||
d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);
|
||||
c += d2 >>> 13;
|
||||
d2 &= 0x1fff;
|
||||
|
||||
let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);
|
||||
c = d3 >>> 13;
|
||||
d3 &= 0x1fff;
|
||||
d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);
|
||||
c += d3 >>> 13;
|
||||
d3 &= 0x1fff;
|
||||
|
||||
let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;
|
||||
c = d4 >>> 13;
|
||||
d4 &= 0x1fff;
|
||||
d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);
|
||||
c += d4 >>> 13;
|
||||
d4 &= 0x1fff;
|
||||
|
||||
let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;
|
||||
c = d5 >>> 13;
|
||||
d5 &= 0x1fff;
|
||||
d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);
|
||||
c += d5 >>> 13;
|
||||
d5 &= 0x1fff;
|
||||
|
||||
let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;
|
||||
c = d6 >>> 13;
|
||||
d6 &= 0x1fff;
|
||||
d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);
|
||||
c += d6 >>> 13;
|
||||
d6 &= 0x1fff;
|
||||
|
||||
let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;
|
||||
c = d7 >>> 13;
|
||||
d7 &= 0x1fff;
|
||||
d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);
|
||||
c += d7 >>> 13;
|
||||
d7 &= 0x1fff;
|
||||
|
||||
let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;
|
||||
c = d8 >>> 13;
|
||||
d8 &= 0x1fff;
|
||||
d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);
|
||||
c += d8 >>> 13;
|
||||
d8 &= 0x1fff;
|
||||
|
||||
let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;
|
||||
c = d9 >>> 13;
|
||||
d9 &= 0x1fff;
|
||||
d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;
|
||||
c += d9 >>> 13;
|
||||
d9 &= 0x1fff;
|
||||
|
||||
c = ((c << 2) + c) | 0;
|
||||
c = (c + d0) | 0;
|
||||
d0 = c & 0x1fff;
|
||||
c = c >>> 13;
|
||||
d1 += c;
|
||||
|
||||
h[0] = d0;
|
||||
h[1] = d1;
|
||||
h[2] = d2;
|
||||
h[3] = d3;
|
||||
h[4] = d4;
|
||||
h[5] = d5;
|
||||
h[6] = d6;
|
||||
h[7] = d7;
|
||||
h[8] = d8;
|
||||
h[9] = d9;
|
||||
}
|
||||
|
||||
private finalize() {
|
||||
const { h, pad } = this;
|
||||
const g = new Uint16Array(10);
|
||||
let c = h[1] >>> 13;
|
||||
h[1] &= 0x1fff;
|
||||
for (let i = 2; i < 10; i++) {
|
||||
h[i] += c;
|
||||
c = h[i] >>> 13;
|
||||
h[i] &= 0x1fff;
|
||||
}
|
||||
h[0] += c * 5;
|
||||
c = h[0] >>> 13;
|
||||
h[0] &= 0x1fff;
|
||||
h[1] += c;
|
||||
c = h[1] >>> 13;
|
||||
h[1] &= 0x1fff;
|
||||
h[2] += c;
|
||||
|
||||
g[0] = h[0] + 5;
|
||||
c = g[0] >>> 13;
|
||||
g[0] &= 0x1fff;
|
||||
for (let i = 1; i < 10; i++) {
|
||||
g[i] = h[i] + c;
|
||||
c = g[i] >>> 13;
|
||||
g[i] &= 0x1fff;
|
||||
}
|
||||
g[9] -= 1 << 13;
|
||||
|
||||
let mask = (c ^ 1) - 1;
|
||||
for (let i = 0; i < 10; i++) g[i] &= mask;
|
||||
mask = ~mask;
|
||||
for (let i = 0; i < 10; i++) h[i] = (h[i] & mask) | g[i];
|
||||
h[0] = (h[0] | (h[1] << 13)) & 0xffff;
|
||||
h[1] = ((h[1] >>> 3) | (h[2] << 10)) & 0xffff;
|
||||
h[2] = ((h[2] >>> 6) | (h[3] << 7)) & 0xffff;
|
||||
h[3] = ((h[3] >>> 9) | (h[4] << 4)) & 0xffff;
|
||||
h[4] = ((h[4] >>> 12) | (h[5] << 1) | (h[6] << 14)) & 0xffff;
|
||||
h[5] = ((h[6] >>> 2) | (h[7] << 11)) & 0xffff;
|
||||
h[6] = ((h[7] >>> 5) | (h[8] << 8)) & 0xffff;
|
||||
h[7] = ((h[8] >>> 8) | (h[9] << 5)) & 0xffff;
|
||||
|
||||
let f = h[0] + pad[0];
|
||||
h[0] = f & 0xffff;
|
||||
for (let i = 1; i < 8; i++) {
|
||||
f = (((h[i] + pad[i]) | 0) + (f >>> 16)) | 0;
|
||||
h[i] = f & 0xffff;
|
||||
}
|
||||
}
|
||||
update(data: Input): this {
|
||||
assert.exists(this);
|
||||
const { buffer, blockLen } = this;
|
||||
data = toBytes(data);
|
||||
const len = data.length;
|
||||
|
||||
for (let pos = 0; pos < len; ) {
|
||||
const take = Math.min(blockLen - this.pos, len - pos);
|
||||
// Fast path: we have at least one block in input
|
||||
if (take === blockLen) {
|
||||
for (; blockLen <= len - pos; pos += blockLen) this.process(data, pos);
|
||||
continue;
|
||||
}
|
||||
buffer.set(data.subarray(pos, pos + take), this.pos);
|
||||
this.pos += take;
|
||||
pos += take;
|
||||
if (this.pos === blockLen) {
|
||||
this.process(buffer, 0, false);
|
||||
this.pos = 0;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
destroy() {
|
||||
this.h.fill(0);
|
||||
this.r.fill(0);
|
||||
this.buffer.fill(0);
|
||||
this.pad.fill(0);
|
||||
}
|
||||
digestInto(out: Uint8Array) {
|
||||
assert.exists(this);
|
||||
assert.output(out, this);
|
||||
this.finished = true;
|
||||
const { buffer, h } = this;
|
||||
let { pos } = this;
|
||||
if (pos) {
|
||||
buffer[pos++] = 1;
|
||||
// buffer.subarray(pos).fill(0);
|
||||
for (; pos < 16; pos++) buffer[pos] = 0;
|
||||
this.process(buffer, 0, true);
|
||||
}
|
||||
this.finalize();
|
||||
let opos = 0;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
out[opos++] = h[i] >>> 0;
|
||||
out[opos++] = h[i] >>> 8;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
digest(): Uint8Array {
|
||||
const { buffer, outputLen } = this;
|
||||
this.digestInto(buffer);
|
||||
const res = buffer.slice(0, outputLen);
|
||||
this.destroy();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export type CHash = ReturnType<typeof wrapConstructorWithKey>;
|
||||
export function wrapConstructorWithKey<H extends Hash<H>>(hashCons: (key: Input) => Hash<H>) {
|
||||
const hashC = (msg: Input, key: Input): Uint8Array => hashCons(key).update(toBytes(msg)).digest();
|
||||
const tmp = hashCons(new Uint8Array(32));
|
||||
hashC.outputLen = tmp.outputLen;
|
||||
hashC.blockLen = tmp.blockLen;
|
||||
hashC.create = (key: Input) => hashCons(key);
|
||||
return hashC;
|
||||
}
|
||||
|
||||
export const poly1305 = wrapConstructorWithKey((key) => new Poly1305(key));
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
import { u8, u32, ensureBytes } from './utils.js';
|
||||
|
||||
// AES-SIV polyval, little-endian "mirror image" of AES-GCM GHash
|
||||
// polynomial hash function. Defined in RFC 8452.
|
||||
|
||||
// Reverse bits in u32, constant-time, precompute will be faster, but non-constant time
|
||||
function rev32(x: number) {
|
||||
x = ((x & 0x5555_5555) << 1) | ((x >>> 1) & 0x5555_5555);
|
||||
x = ((x & 0x3333_3333) << 2) | ((x >>> 2) & 0x3333_3333);
|
||||
x = ((x & 0x0f0f_0f0f) << 4) | ((x >>> 4) & 0x0f0f_0f0f);
|
||||
x = ((x & 0x00ff_00ff) << 8) | ((x >>> 8) & 0x00ff_00ff);
|
||||
return (x << 16) | (x >>> 16);
|
||||
}
|
||||
|
||||
// wrapped 32 bit multiplication
|
||||
const wrapMul = (a: number, b: number) => Math.imul(a, b) >>> 0;
|
||||
|
||||
// https://timtaubert.de/blog/2017/06/verified-binary-multiplication-for-ghash/
|
||||
function bmul32(x: number, y: number) {
|
||||
const x0 = x & 0x1111_1111;
|
||||
const x1 = x & 0x2222_2222;
|
||||
const x2 = x & 0x4444_4444;
|
||||
const x3 = x & 0x8888_8888;
|
||||
const y0 = y & 0x1111_1111;
|
||||
const y1 = y & 0x2222_2222;
|
||||
const y2 = y & 0x4444_4444;
|
||||
const y3 = y & 0x8888_8888;
|
||||
let res = (wrapMul(x0, y0) ^ wrapMul(x1, y3) ^ wrapMul(x2, y2) ^ wrapMul(x3, y1)) & 0x1111_1111;
|
||||
res |= (wrapMul(x0, y1) ^ wrapMul(x1, y0) ^ wrapMul(x2, y3) ^ wrapMul(x3, y2)) & 0x2222_2222;
|
||||
res |= (wrapMul(x0, y2) ^ wrapMul(x1, y1) ^ wrapMul(x2, y0) ^ wrapMul(x3, y3)) & 0x4444_4444;
|
||||
res |= (wrapMul(x0, y3) ^ wrapMul(x1, y2) ^ wrapMul(x2, y1) ^ wrapMul(x3, y0)) & 0x8888_8888;
|
||||
return res >>> 0;
|
||||
}
|
||||
|
||||
function mulPart(arr: Uint32Array) {
|
||||
const a = new Uint32Array(18);
|
||||
a[0] = arr[0];
|
||||
a[1] = arr[1];
|
||||
a[2] = arr[2];
|
||||
a[3] = arr[3];
|
||||
a[4] = a[0] ^ a[1];
|
||||
a[5] = a[2] ^ a[3];
|
||||
a[6] = a[0] ^ a[2];
|
||||
a[7] = a[1] ^ a[3];
|
||||
a[8] = a[6] ^ a[7];
|
||||
a[9] = rev32(arr[0]);
|
||||
a[10] = rev32(arr[1]);
|
||||
a[11] = rev32(arr[2]);
|
||||
a[12] = rev32(arr[3]);
|
||||
a[13] = a[9] ^ a[10];
|
||||
a[14] = a[11] ^ a[12];
|
||||
a[15] = a[9] ^ a[11];
|
||||
a[16] = a[10] ^ a[12];
|
||||
a[17] = a[15] ^ a[16];
|
||||
return a;
|
||||
}
|
||||
|
||||
export function polyval(h: Uint8Array, data: Uint8Array) {
|
||||
ensureBytes(h);
|
||||
ensureBytes(data);
|
||||
const s = new Uint32Array(4);
|
||||
// Precompute for multiplication
|
||||
const a = mulPart(u32(h));
|
||||
if (data.length % 16) throw new Error('polyval: data must be padded to 16 bytes');
|
||||
const data32 = u32(data);
|
||||
for (let i = 0; i < data32.length; i += 4) {
|
||||
// Xor
|
||||
s[0] ^= data32[i + 0];
|
||||
s[1] ^= data32[i + 1];
|
||||
s[2] ^= data32[i + 2];
|
||||
s[3] ^= data32[i + 3];
|
||||
|
||||
// Dot via Karatsuba multiplication, based on MIT-licensed
|
||||
// https://bearssl.org/gitweb/?p=BearSSL;a=blob;f=src/hash/ghash_ctmul32.c;hb=4b6046412
|
||||
const b = mulPart(s);
|
||||
|
||||
const c = new Uint32Array(18);
|
||||
for (let i = 0; i < 18; i++) c[i] = bmul32(a[i], b[i]);
|
||||
c[4] ^= c[0] ^ c[1];
|
||||
c[5] ^= c[2] ^ c[3];
|
||||
c[8] ^= c[6] ^ c[7];
|
||||
c[13] ^= c[9] ^ c[10];
|
||||
c[14] ^= c[11] ^ c[12];
|
||||
c[17] ^= c[15] ^ c[16];
|
||||
|
||||
const zw = new Uint32Array(8);
|
||||
zw[0] = c[0];
|
||||
zw[1] = c[4] ^ (rev32(c[9]) >>> 1);
|
||||
zw[2] = c[1] ^ c[0] ^ c[2] ^ c[6] ^ (rev32(c[13]) >>> 1);
|
||||
zw[3] = c[4] ^ c[5] ^ c[8] ^ (rev32(c[10] ^ c[9] ^ c[11] ^ c[15]) >>> 1);
|
||||
zw[4] = c[2] ^ c[1] ^ c[3] ^ c[7] ^ (rev32(c[13] ^ c[14] ^ c[17]) >>> 1);
|
||||
zw[5] = c[5] ^ (rev32(c[11] ^ c[10] ^ c[12] ^ c[16]) >>> 1);
|
||||
zw[6] = c[3] ^ (rev32(c[14]) >>> 1);
|
||||
zw[7] = rev32(c[12]) >>> 1;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const lw = zw[i];
|
||||
zw[i + 4] ^= lw ^ (lw >>> 1) ^ (lw >>> 2) ^ (lw >>> 7);
|
||||
zw[i + 3] ^= (lw << 31) ^ (lw << 30) ^ (lw << 25);
|
||||
}
|
||||
|
||||
s[0] = zw[4];
|
||||
s[1] = zw[5];
|
||||
s[2] = zw[6];
|
||||
s[3] = zw[7];
|
||||
}
|
||||
return u8(s);
|
||||
}
|
||||
-175
@@ -1,175 +0,0 @@
|
||||
// Basic utils for salsa-like ciphers
|
||||
// Check out _micro.ts for descriptive documentation.
|
||||
import assert from './_assert.js';
|
||||
import { u32, utf8ToBytes, checkOpts } from './utils.js';
|
||||
|
||||
/*
|
||||
RFC8439 requires multi-step cipher stream, where
|
||||
authKey starts with counter: 0, actual msg with counter: 1.
|
||||
|
||||
For this, we need a way to re-use nonce / counter:
|
||||
|
||||
const counter = new Uint8Array(4);
|
||||
chacha(..., counter, ...); // counter is now 1
|
||||
chacha(..., counter, ...); // counter is now 2
|
||||
|
||||
This is complicated:
|
||||
|
||||
- Original papers don't allow mutating counters
|
||||
- Counter overflow is undefined: https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/
|
||||
- 3rd-party library stablelib implementation uses an approach where you can provide
|
||||
nonce and counter instead of just nonce - and it will re-use it
|
||||
- We could have did something similar, but ChaCha has different counter position
|
||||
(counter | nonce), which is not composable with XChaCha, because full counter
|
||||
is (nonce16 | counter | nonce16). Stablelib doesn't support in-place counter for XChaCha.
|
||||
- We could separate nonce & counter and provide separate API for counter re-use, but
|
||||
there are different counter sizes depending on an algorithm.
|
||||
- Salsa & ChaCha also differ in structures of key / sigma:
|
||||
|
||||
salsa: c0 | k(4) | c1 | nonce(2) | ctr(2) | c2 | k(4) | c4
|
||||
chacha: c(4) | k(8) | ctr(1) | nonce(3)
|
||||
chachaDJB: c(4) | k(8) | ctr(2) | nonce(2)
|
||||
- Creating function such as `setSalsaState(key, nonce, sigma, data)` won't work,
|
||||
because we can't re-use counter array
|
||||
- 32-bit nonce is `2 ** 32 * 64` = 256GB with 32-bit counter
|
||||
- JS does not allow UintArrays bigger than 4GB, so supporting 64-bit counters doesn't matter
|
||||
|
||||
Structure is as following:
|
||||
|
||||
key=16 -> sigma16, k=key|key
|
||||
key=32 -> sigma32, k=key
|
||||
|
||||
nonces:
|
||||
salsa20: 8 (8-byte counter)
|
||||
chacha20djb: 8 (8-byte counter)
|
||||
chacha20tls: 12 (4-byte counter)
|
||||
xsalsa: 24 (16 -> hsalsa, 8 -> old nonce)
|
||||
xchacha: 24 (16 -> hchacha, 8 -> old nonce)
|
||||
|
||||
https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2
|
||||
Use the subkey and remaining 8 byte nonce with ChaCha20 as normal
|
||||
(prefixed by 4 NUL bytes, since [RFC8439] specifies a 12-byte nonce).
|
||||
*/
|
||||
|
||||
const sigma16 = utf8ToBytes('expand 16-byte k');
|
||||
const sigma32 = utf8ToBytes('expand 32-byte k');
|
||||
const sigma16_32 = u32(sigma16);
|
||||
const sigma32_32 = u32(sigma32);
|
||||
|
||||
export type SalsaOpts = {
|
||||
core: (
|
||||
c: Uint32Array,
|
||||
key: Uint32Array,
|
||||
nonce: Uint32Array,
|
||||
out: Uint32Array,
|
||||
counter: number,
|
||||
rounds?: number
|
||||
) => void;
|
||||
rounds?: number;
|
||||
counterRight?: boolean; // counterRight ? nonce | counter : counter | nonce;
|
||||
counterLen?: number;
|
||||
blockLen?: number; // NOTE: not tested with different blockLens!
|
||||
allow128bitKeys?: boolean; // Original salsa/chacha allows these, but not tested!
|
||||
extendNonceFn?: (c: Uint32Array, key: Uint8Array, src: Uint8Array, dst: Uint8Array) => Uint8Array;
|
||||
};
|
||||
|
||||
// Is byte array aligned to 4 byte offset (u32)?
|
||||
const isAligned32 = (b: Uint8Array) => !(b.byteOffset % 4);
|
||||
|
||||
export const salsaBasic = (opts: SalsaOpts) => {
|
||||
const { core, rounds, counterRight, counterLen, allow128bitKeys, extendNonceFn, blockLen } =
|
||||
checkOpts(
|
||||
{ rounds: 20, counterRight: false, counterLen: 8, allow128bitKeys: true, blockLen: 64 },
|
||||
opts
|
||||
);
|
||||
assert.number(counterLen);
|
||||
assert.number(rounds);
|
||||
assert.number(blockLen);
|
||||
assert.bool(counterRight);
|
||||
assert.bool(allow128bitKeys);
|
||||
const blockLen32 = blockLen / 4;
|
||||
if (blockLen % 4 !== 0) throw new Error('Salsa/ChaCha: blockLen must be aligned to 4 bytes');
|
||||
return (
|
||||
key: Uint8Array,
|
||||
nonce: Uint8Array,
|
||||
data: Uint8Array,
|
||||
output?: Uint8Array,
|
||||
counter = 0
|
||||
): Uint8Array => {
|
||||
assert.bytes(key);
|
||||
assert.bytes(nonce);
|
||||
assert.bytes(data);
|
||||
if (!output) output = new Uint8Array(data.length);
|
||||
assert.bytes(output);
|
||||
assert.number(counter);
|
||||
// > new Uint32Array([2**32])
|
||||
// Uint32Array(1) [ 0 ]
|
||||
// > new Uint32Array([2**32-1])
|
||||
// Uint32Array(1) [ 4294967295 ]
|
||||
if (counter < 0 || counter >= 2 ** 32 - 1) throw new Error('Salsa/ChaCha: counter overflow');
|
||||
if (output.length < data.length) {
|
||||
throw new Error(
|
||||
`Salsa/ChaCha: output (${output.length}) is shorter than data (${data.length})`
|
||||
);
|
||||
}
|
||||
const toClean = [];
|
||||
let k, sigma;
|
||||
// Handle 128 byte keys
|
||||
if (key.length === 32) {
|
||||
k = key;
|
||||
sigma = sigma32_32;
|
||||
} else if (key.length === 16 && allow128bitKeys) {
|
||||
k = new Uint8Array(32);
|
||||
k.set(key);
|
||||
k.set(key, 16);
|
||||
sigma = sigma16_32;
|
||||
toClean.push(k);
|
||||
} else throw new Error(`Salsa/ChaCha: invalid 32-byte key, got length=${key.length}`);
|
||||
// Handle extended nonce (HChaCha/HSalsa)
|
||||
if (extendNonceFn) {
|
||||
if (nonce.length <= 16)
|
||||
throw new Error(`Salsa/ChaCha: extended nonce must be bigger than 16 bytes`);
|
||||
k = extendNonceFn(sigma, k, nonce.subarray(0, 16), new Uint8Array(32));
|
||||
toClean.push(k);
|
||||
nonce = nonce.subarray(16);
|
||||
}
|
||||
// Handle nonce counter
|
||||
const nonceLen = 16 - counterLen;
|
||||
if (nonce.length !== nonceLen)
|
||||
throw new Error(`Salsa/ChaCha: nonce must be ${nonceLen} or 16 bytes`);
|
||||
// Pad counter when nonce is 64 bit
|
||||
if (nonceLen !== 12) {
|
||||
const nc = new Uint8Array(12);
|
||||
nc.set(nonce, counterRight ? 0 : 12 - nonce.length);
|
||||
toClean.push((nonce = nc));
|
||||
}
|
||||
// Counter positions
|
||||
const block = new Uint8Array(blockLen);
|
||||
// Cast to Uint32Array for speed
|
||||
const b32 = u32(block);
|
||||
const k32 = u32(k);
|
||||
const n32 = u32(nonce);
|
||||
// Make sure that buffers aligned to 4 bytes
|
||||
const d32 = isAligned32(data) && u32(data);
|
||||
const o32 = isAligned32(output) && u32(output);
|
||||
toClean.push(b32);
|
||||
const len = data.length;
|
||||
for (let pos = 0, ctr = counter; pos < len; ctr++) {
|
||||
core(sigma, k32, n32, b32, ctr, rounds);
|
||||
if (ctr >= 2 ** 32 - 1) throw new Error('Salsa/ChaCha: counter overflow');
|
||||
const take = Math.min(blockLen, len - pos);
|
||||
// full block && aligned to 4 bytes
|
||||
if (take === blockLen && o32 && d32) {
|
||||
const pos32 = pos / 4;
|
||||
if (pos % 4 !== 0) throw new Error('Salsa/ChaCha: invalid block position');
|
||||
for (let j = 0; j < blockLen32; j++) o32[pos32 + j] = d32[pos32 + j] ^ b32[j];
|
||||
pos += blockLen;
|
||||
continue;
|
||||
}
|
||||
for (let j = 0; j < take; j++) output[pos + j] = data[pos + j] ^ block[j];
|
||||
pos += take;
|
||||
}
|
||||
for (let i = 0; i < toClean.length; i++) toClean[i].fill(0);
|
||||
return output;
|
||||
};
|
||||
};
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
import { Cipher, createView, ensureBytes, equalBytes, setBigUint64, u32 } from './utils.js';
|
||||
import { poly1305 } from './_poly1305.js';
|
||||
import { salsaBasic } from './_salsa.js';
|
||||
|
||||
// ChaCha20 stream cipher was released in 2008. ChaCha aims to increase
|
||||
// the diffusion per round, but had slightly less cryptanalysis.
|
||||
// https://cr.yp.to/chacha.html, http://cr.yp.to/chacha/chacha-20080128.pdf
|
||||
|
||||
// Left rotate for uint32
|
||||
const rotl = (a: number, b: number) => (a << b) | (a >>> (32 - b));
|
||||
|
||||
/**
|
||||
* ChaCha core function.
|
||||
*/
|
||||
// prettier-ignore
|
||||
function chachaCore(
|
||||
c: Uint32Array, k: Uint32Array, n: Uint32Array, out: Uint32Array, cnt: number, rounds = 20
|
||||
): void {
|
||||
let y00 = c[0], y01 = c[1], y02 = c[2], y03 = c[3]; // "expa" "nd 3" "2-by" "te k"
|
||||
let y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3]; // Key Key Key Key
|
||||
let y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7]; // Key Key Key Key
|
||||
let y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2]; // Counter Counter Nonce Nonce
|
||||
// Save state to temporary variables
|
||||
let x00 = y00, x01 = y01, x02 = y02, x03 = y03,
|
||||
x04 = y04, x05 = y05, x06 = y06, x07 = y07,
|
||||
x08 = y08, x09 = y09, x10 = y10, x11 = y11,
|
||||
x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
||||
// Main loop
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 16);
|
||||
x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 12);
|
||||
x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 8);
|
||||
x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 7);
|
||||
|
||||
x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 16);
|
||||
x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 12);
|
||||
x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 8);
|
||||
x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 7);
|
||||
|
||||
x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 16);
|
||||
x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 12);
|
||||
x02 = (x02 + x06) | 0; x14 = rotl(x14 ^x02, 8);
|
||||
x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 7);
|
||||
|
||||
x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 16);
|
||||
x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 12);
|
||||
x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 8)
|
||||
x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 7);
|
||||
|
||||
x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 16);
|
||||
x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 12);
|
||||
x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 8);
|
||||
x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 7);
|
||||
|
||||
x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 16);
|
||||
x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 12);
|
||||
x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 8);
|
||||
x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 7);
|
||||
|
||||
x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 16);
|
||||
x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 12);
|
||||
x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 8);
|
||||
x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 7);
|
||||
|
||||
x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 16)
|
||||
x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 12);
|
||||
x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 8);
|
||||
x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 7);
|
||||
}
|
||||
// Write output
|
||||
let oi = 0;
|
||||
out[oi++] = (y00 + x00) | 0; out[oi++] = (y01 + x01) | 0;
|
||||
out[oi++] = (y02 + x02) | 0; out[oi++] = (y03 + x03) | 0;
|
||||
out[oi++] = (y04 + x04) | 0; out[oi++] = (y05 + x05) | 0;
|
||||
out[oi++] = (y06 + x06) | 0; out[oi++] = (y07 + x07) | 0;
|
||||
out[oi++] = (y08 + x08) | 0; out[oi++] = (y09 + x09) | 0;
|
||||
out[oi++] = (y10 + x10) | 0; out[oi++] = (y11 + x11) | 0;
|
||||
out[oi++] = (y12 + x12) | 0; out[oi++] = (y13 + x13) | 0;
|
||||
out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0;
|
||||
}
|
||||
/**
|
||||
* hchacha helper method, used primarily in xchacha, to hash
|
||||
* key and nonce into key' and nonce'.
|
||||
* Same as chachaCore, but there doesn't seem to be a way to move the block
|
||||
* out without 25% performance hit.
|
||||
*/
|
||||
// prettier-ignore
|
||||
export function hchacha(
|
||||
c: Uint32Array, key: Uint8Array, src: Uint8Array, out: Uint8Array
|
||||
): Uint8Array {
|
||||
const k32 = u32(key);
|
||||
const i32 = u32(src);
|
||||
const o32 = u32(out);
|
||||
let x00 = c[0], x01 = c[1], x02 = c[2], x03 = c[3];
|
||||
let x04 = k32[0], x05 = k32[1], x06 = k32[2], x07 = k32[3];
|
||||
let x08 = k32[4], x09 = k32[5], x10 = k32[6], x11 = k32[7]
|
||||
let x12 = i32[0], x13 = i32[1], x14 = i32[2], x15 = i32[3];
|
||||
for (let i = 0; i < 20; i += 2) {
|
||||
x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 16);
|
||||
x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 12);
|
||||
x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 8);
|
||||
x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 7);
|
||||
|
||||
x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 16);
|
||||
x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 12);
|
||||
x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 8);
|
||||
x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 7);
|
||||
|
||||
x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 16);
|
||||
x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 12);
|
||||
x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 8);
|
||||
x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 7);
|
||||
|
||||
x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 16);
|
||||
x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 12);
|
||||
x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 8)
|
||||
x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 7);
|
||||
|
||||
x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 16);
|
||||
x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 12);
|
||||
x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 8);
|
||||
x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 7);
|
||||
|
||||
x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 16);
|
||||
x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 12);
|
||||
x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 8);
|
||||
x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 7);
|
||||
|
||||
x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 16);
|
||||
x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 12);
|
||||
x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 8);
|
||||
x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 7);
|
||||
|
||||
x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 16)
|
||||
x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 12);
|
||||
x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 8);
|
||||
x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 7);
|
||||
}
|
||||
o32[0] = x00;
|
||||
o32[1] = x01;
|
||||
o32[2] = x02;
|
||||
o32[3] = x03;
|
||||
o32[4] = x12;
|
||||
o32[5] = x13;
|
||||
o32[6] = x14;
|
||||
o32[7] = x15;
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.
|
||||
*/
|
||||
export const chacha20orig = /* @__PURE__ */ salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 8,
|
||||
});
|
||||
/**
|
||||
* ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
export const chacha20 = /* @__PURE__ */ salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* XChaCha eXtended-nonce ChaCha. 24-byte nonce.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
*/
|
||||
export const xchacha20 = /* @__PURE__ */ salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 8,
|
||||
extendNonceFn: hchacha,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* Reduced 8-round chacha, described in original paper.
|
||||
*/
|
||||
export const chacha8 = /* @__PURE__ */ salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 8,
|
||||
});
|
||||
|
||||
/**
|
||||
* Reduced 12-round chacha, described in original paper.
|
||||
*/
|
||||
export const chacha12 = /* @__PURE__ */ salsaBasic({
|
||||
core: chachaCore,
|
||||
counterRight: false,
|
||||
counterLen: 4,
|
||||
rounds: 12,
|
||||
});
|
||||
|
||||
const ZERO = /* @__PURE__ */ new Uint8Array(16);
|
||||
// Pad to digest size with zeros
|
||||
const updatePadded = (h: ReturnType<typeof poly1305.create>, msg: Uint8Array) => {
|
||||
h.update(msg);
|
||||
const left = msg.length % 16;
|
||||
if (left) h.update(ZERO.subarray(left));
|
||||
};
|
||||
|
||||
const computeTag = (
|
||||
fn: typeof chacha20,
|
||||
key: Uint8Array,
|
||||
nonce: Uint8Array,
|
||||
data: Uint8Array,
|
||||
AAD?: Uint8Array
|
||||
) => {
|
||||
const authKey = fn(key, nonce, new Uint8Array(32));
|
||||
const h = poly1305.create(authKey);
|
||||
if (AAD) updatePadded(h, AAD);
|
||||
updatePadded(h, data);
|
||||
const num = new Uint8Array(16);
|
||||
const view = createView(num);
|
||||
setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
|
||||
setBigUint64(view, 8, BigInt(data.length), true);
|
||||
h.update(num);
|
||||
const res = h.digest();
|
||||
authKey.fill(0);
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* AEAD algorithm from RFC 8439.
|
||||
* Salsa20 and chacha (RFC 8439) use poly1305 differently.
|
||||
* We could have composed them similar to:
|
||||
* https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250
|
||||
* But it's hard because of authKey:
|
||||
* In salsa20, authKey changes position in salsa stream.
|
||||
* In chacha, authKey can't be computed inside computeTag, it modifies the counter.
|
||||
*/
|
||||
export const _poly1305_aead =
|
||||
(xorStream: typeof chacha20) =>
|
||||
(key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array): Cipher => {
|
||||
const tagLength = 16;
|
||||
ensureBytes(key, 32);
|
||||
ensureBytes(nonce);
|
||||
return {
|
||||
tagLength,
|
||||
encrypt: (plaintext: Uint8Array, output?: Uint8Array) => {
|
||||
const plength = plaintext.length;
|
||||
const clength = plength + tagLength;
|
||||
if (output) {
|
||||
ensureBytes(output, clength);
|
||||
} else {
|
||||
output = new Uint8Array(clength);
|
||||
}
|
||||
xorStream(key, nonce, plaintext, output, 1);
|
||||
const tag = computeTag(xorStream, key, nonce, output.subarray(0, -tagLength), AAD);
|
||||
output.set(tag, plength); // append tag
|
||||
return output;
|
||||
},
|
||||
decrypt: (ciphertext: Uint8Array, output?: Uint8Array) => {
|
||||
const clength = ciphertext.length;
|
||||
const plength = clength - tagLength;
|
||||
if (clength < tagLength)
|
||||
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
|
||||
if (output) {
|
||||
ensureBytes(output, plength);
|
||||
} else {
|
||||
output = new Uint8Array(plength);
|
||||
}
|
||||
const data = ciphertext.subarray(0, -tagLength);
|
||||
const passedTag = ciphertext.subarray(-tagLength);
|
||||
const tag = computeTag(xorStream, key, nonce, data, AAD);
|
||||
if (!equalBytes(passedTag, tag)) throw new Error('invalid tag');
|
||||
xorStream(key, nonce, data, output, 1);
|
||||
return output;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* ChaCha20-Poly1305 from RFC 8439.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
export const chacha20poly1305 = /* @__PURE__ */ _poly1305_aead(chacha20);
|
||||
/**
|
||||
* XChaCha20-Poly1305 extended-nonce chacha.
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
export const xchacha20poly1305 = /* @__PURE__ */ _poly1305_aead(xchacha20);
|
||||
-1
@@ -1 +0,0 @@
|
||||
throw new Error('noble-ciphers have no entry-point: consult README for usage');
|
||||
-176
@@ -1,176 +0,0 @@
|
||||
import { ensureBytes, u32, equalBytes, Cipher } from './utils.js';
|
||||
import { salsaBasic } from './_salsa.js';
|
||||
import { poly1305 } from './_poly1305.js';
|
||||
|
||||
// Salsa20 stream cipher was released in 2005.
|
||||
// Salsa's goal was to implement AES replacement that does not rely on S-Boxes,
|
||||
// which are hard to implement in a constant-time manner.
|
||||
// https://cr.yp.to/snuffle.html, https://cr.yp.to/snuffle/salsafamily-20071225.pdf
|
||||
|
||||
// Left rotate for uint32
|
||||
const rotl = (a: number, b: number) => (a << b) | (a >>> (32 - b));
|
||||
|
||||
/**
|
||||
* Salsa20 core function.
|
||||
*/
|
||||
// prettier-ignore
|
||||
function salsaCore(
|
||||
c: Uint32Array, k: Uint32Array, i: Uint32Array, out: Uint32Array, cnt: number, rounds = 20
|
||||
): void {
|
||||
// Based on https://cr.yp.to/salsa20.html
|
||||
let y00 = c[0], y01 = k[0], y02 = k[1], y03 = k[2]; // "expa" Key Key Key
|
||||
let y04 = k[3], y05 = c[1], y06 = i[0], y07 = i[1]; // Key "nd 3" Nonce Nonce
|
||||
let y08 = cnt, y09 = 0 , y10 = c[2], y11 = k[4]; // Pos. Pos. "2-by" Key
|
||||
let y12 = k[5], y13 = k[6], y14 = k[7], y15 = c[3]; // Key Key Key "te k"
|
||||
// Save state to temporary variables
|
||||
let x00 = y00, x01 = y01, x02 = y02, x03 = y03,
|
||||
x04 = y04, x05 = y05, x06 = y06, x07 = y07,
|
||||
x08 = y08, x09 = y09, x10 = y10, x11 = y11,
|
||||
x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
||||
// Main loop
|
||||
for (let i = 0; i < rounds; i += 2) {
|
||||
x04 ^= rotl(x00 + x12 | 0, 7); x08 ^= rotl(x04 + x00 | 0, 9);
|
||||
x12 ^= rotl(x08 + x04 | 0, 13); x00 ^= rotl(x12 + x08 | 0, 18);
|
||||
x09 ^= rotl(x05 + x01 | 0, 7); x13 ^= rotl(x09 + x05 | 0, 9);
|
||||
x01 ^= rotl(x13 + x09 | 0, 13); x05 ^= rotl(x01 + x13 | 0, 18);
|
||||
x14 ^= rotl(x10 + x06 | 0, 7); x02 ^= rotl(x14 + x10 | 0, 9);
|
||||
x06 ^= rotl(x02 + x14 | 0, 13); x10 ^= rotl(x06 + x02 | 0, 18);
|
||||
x03 ^= rotl(x15 + x11 | 0, 7); x07 ^= rotl(x03 + x15 | 0, 9);
|
||||
x11 ^= rotl(x07 + x03 | 0, 13); x15 ^= rotl(x11 + x07 | 0, 18);
|
||||
x01 ^= rotl(x00 + x03 | 0, 7); x02 ^= rotl(x01 + x00 | 0, 9);
|
||||
x03 ^= rotl(x02 + x01 | 0, 13); x00 ^= rotl(x03 + x02 | 0, 18);
|
||||
x06 ^= rotl(x05 + x04 | 0, 7); x07 ^= rotl(x06 + x05 | 0, 9);
|
||||
x04 ^= rotl(x07 + x06 | 0, 13); x05 ^= rotl(x04 + x07 | 0, 18);
|
||||
x11 ^= rotl(x10 + x09 | 0, 7); x08 ^= rotl(x11 + x10 | 0, 9);
|
||||
x09 ^= rotl(x08 + x11 | 0, 13); x10 ^= rotl(x09 + x08 | 0, 18);
|
||||
x12 ^= rotl(x15 + x14 | 0, 7); x13 ^= rotl(x12 + x15 | 0, 9);
|
||||
x14 ^= rotl(x13 + x12 | 0, 13); x15 ^= rotl(x14 + x13 | 0, 18);
|
||||
}
|
||||
// Write output
|
||||
let oi = 0;
|
||||
out[oi++] = (y00 + x00) | 0; out[oi++] = (y01 + x01) | 0;
|
||||
out[oi++] = (y02 + x02) | 0; out[oi++] = (y03 + x03) | 0;
|
||||
out[oi++] = (y04 + x04) | 0; out[oi++] = (y05 + x05) | 0;
|
||||
out[oi++] = (y06 + x06) | 0; out[oi++] = (y07 + x07) | 0;
|
||||
out[oi++] = (y08 + x08) | 0; out[oi++] = (y09 + x09) | 0;
|
||||
out[oi++] = (y10 + x10) | 0; out[oi++] = (y11 + x11) | 0;
|
||||
out[oi++] = (y12 + x12) | 0; out[oi++] = (y13 + x13) | 0;
|
||||
out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hsalsa hashing function, used primarily in xsalsa, to hash
|
||||
* key and nonce into key' and nonce'.
|
||||
* Same as salsaCore, but there doesn't seem to be a way to move the block
|
||||
* out without 25% performance hit.
|
||||
*/
|
||||
// prettier-ignore
|
||||
export function hsalsa(c: Uint32Array, key: Uint8Array, nonce: Uint8Array, out: Uint8Array): Uint8Array {
|
||||
const k32 = u32(key);
|
||||
const i32 = u32(nonce);
|
||||
const o32 = u32(out);
|
||||
let x00 = c[0], x01 = k32[0], x02 = k32[1], x03 = k32[2], x04 = k32[3];
|
||||
let x05 = c[1], x06 = i32[0], x07 = i32[1], x08 = i32[2], x09 = i32[3];
|
||||
let x10 = c[2], x11 = k32[4], x12 = k32[5], x13 = k32[6], x14 = k32[7];
|
||||
let x15 = c[3];
|
||||
// Main loop
|
||||
for (let i = 0; i < 20; i += 2) {
|
||||
x04 ^= rotl(x00 + x12 | 0, 7); x08 ^= rotl(x04 + x00 | 0, 9);
|
||||
x12 ^= rotl(x08 + x04 | 0, 13); x00 ^= rotl(x12 + x08 | 0, 18);
|
||||
x09 ^= rotl(x05 + x01 | 0, 7); x13 ^= rotl(x09 + x05 | 0, 9);
|
||||
x01 ^= rotl(x13 + x09 | 0, 13); x05 ^= rotl(x01 + x13 | 0, 18);
|
||||
x14 ^= rotl(x10 + x06 | 0, 7); x02 ^= rotl(x14 + x10 | 0, 9);
|
||||
x06 ^= rotl(x02 + x14 | 0, 13); x10 ^= rotl(x06 + x02 | 0, 18);
|
||||
x03 ^= rotl(x15 + x11 | 0, 7); x07 ^= rotl(x03 + x15 | 0, 9);
|
||||
x11 ^= rotl(x07 + x03 | 0, 13); x15 ^= rotl(x11 + x07 | 0, 18);
|
||||
x01 ^= rotl(x00 + x03 | 0, 7); x02 ^= rotl(x01 + x00 | 0, 9);
|
||||
x03 ^= rotl(x02 + x01 | 0, 13); x00 ^= rotl(x03 + x02 | 0, 18);
|
||||
x06 ^= rotl(x05 + x04 | 0, 7); x07 ^= rotl(x06 + x05 | 0, 9);
|
||||
x04 ^= rotl(x07 + x06 | 0, 13); x05 ^= rotl(x04 + x07 | 0, 18);
|
||||
x11 ^= rotl(x10 + x09 | 0, 7); x08 ^= rotl(x11 + x10 | 0, 9);
|
||||
x09 ^= rotl(x08 + x11 | 0, 13); x10 ^= rotl(x09 + x08 | 0, 18);
|
||||
x12 ^= rotl(x15 + x14 | 0, 7); x13 ^= rotl(x12 + x15 | 0, 9);
|
||||
x14 ^= rotl(x13 + x12 | 0, 13); x15 ^= rotl(x14 + x13 | 0, 18);
|
||||
}
|
||||
o32[0] = x00;
|
||||
o32[1] = x05;
|
||||
o32[2] = x10;
|
||||
o32[3] = x15;
|
||||
o32[4] = x06;
|
||||
o32[5] = x07;
|
||||
o32[6] = x08;
|
||||
o32[7] = x09;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Salsa20 from original paper.
|
||||
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
|
||||
*/
|
||||
export const salsa20 = /* @__PURE__ */ salsaBasic({ core: salsaCore, counterRight: true });
|
||||
|
||||
/**
|
||||
* xsalsa20 eXtended-nonce salsa.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
*/
|
||||
export const xsalsa20 = /* @__PURE__ */ salsaBasic({
|
||||
core: salsaCore,
|
||||
counterRight: true,
|
||||
extendNonceFn: hsalsa,
|
||||
allow128bitKeys: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* xsalsa20-poly1305 eXtended-nonce salsa.
|
||||
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
||||
* Also known as secretbox from libsodium / nacl.
|
||||
*/
|
||||
export const xsalsa20poly1305 = (key: Uint8Array, nonce: Uint8Array): Cipher => {
|
||||
const tagLength = 16;
|
||||
ensureBytes(key, 32);
|
||||
ensureBytes(nonce, 24);
|
||||
return {
|
||||
tagLength,
|
||||
encrypt: (plaintext: Uint8Array, output?: Uint8Array) => {
|
||||
ensureBytes(plaintext);
|
||||
// This is small optimization (calculate auth key with same call as encryption itself) makes it hard
|
||||
// to separate tag calculation and encryption itself, since 32 byte is half-block of salsa (64 byte)
|
||||
const clength = plaintext.length + 32;
|
||||
if (output) {
|
||||
ensureBytes(output, clength);
|
||||
} else {
|
||||
output = new Uint8Array(clength);
|
||||
}
|
||||
output.set(plaintext, 32);
|
||||
xsalsa20(key, nonce, output, output);
|
||||
const authKey = output.subarray(0, 32);
|
||||
const tag = poly1305(output.subarray(32), authKey);
|
||||
// Clean auth key, even though JS provides no guarantees about memory cleaning
|
||||
output.set(tag, tagLength);
|
||||
output.subarray(0, tagLength).fill(0);
|
||||
return output.subarray(tagLength);
|
||||
},
|
||||
decrypt: (ciphertext: Uint8Array) => {
|
||||
ensureBytes(ciphertext);
|
||||
const clength = ciphertext.length;
|
||||
if (clength < tagLength) throw new Error('encrypted data should be at least 16 bytes');
|
||||
// Create new ciphertext array:
|
||||
// auth tag auth tag from ciphertext ciphertext
|
||||
// [bytes 0..16] [bytes 16..32] [bytes 32..]
|
||||
// 16 instead of 32, because we already have 16 byte tag
|
||||
const ciphertext_ = new Uint8Array(clength + tagLength); // alloc
|
||||
ciphertext_.set(ciphertext, tagLength);
|
||||
// Each xsalsa20 calls to hsalsa to calculate key, but seems not much perf difference
|
||||
// Separate call to calculate authkey, since first bytes contains tag
|
||||
const authKey = xsalsa20(key, nonce, new Uint8Array(32)); // alloc(32)
|
||||
const tag = poly1305(ciphertext_.subarray(32), authKey);
|
||||
if (!equalBytes(ciphertext_.subarray(16, 32), tag)) throw new Error('invalid tag');
|
||||
|
||||
const plaintext = xsalsa20(key, nonce, ciphertext_); // alloc
|
||||
// Clean auth key, even though JS provides no guarantees about memory cleaning
|
||||
plaintext.subarray(0, 32).fill(0);
|
||||
authKey.fill(0);
|
||||
return plaintext.subarray(32);
|
||||
},
|
||||
};
|
||||
};
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
import { xchacha20poly1305 } from './chacha.js';
|
||||
import { xsalsa20poly1305 } from './salsa.js';
|
||||
import { concatBytes, ensureBytes, utf8ToBytes } from './utils.js';
|
||||
import { aes_256_gcm } from './webcrypto/aes.js';
|
||||
import { randomBytes } from './webcrypto/utils.js';
|
||||
|
||||
export { utf8ToBytes };
|
||||
|
||||
/**
|
||||
* Alias to xsalsa20poly1305, for compatibility with libsodium / nacl
|
||||
*/
|
||||
export function secretbox(key: Uint8Array, nonce: Uint8Array) {
|
||||
ensureBytes(key);
|
||||
ensureBytes(nonce);
|
||||
const xs = xsalsa20poly1305(key, nonce);
|
||||
return { seal: xs.encrypt, open: xs.decrypt };
|
||||
}
|
||||
|
||||
export function randomKey(): Uint8Array {
|
||||
return randomBytes(32);
|
||||
}
|
||||
/**
|
||||
* Encrypt plaintext under key with random nonce, using xchacha20poly1305.
|
||||
* User never touches nonce: it is prepended to ciphertext.
|
||||
*/
|
||||
export function encrypt(key: Uint8Array, plaintext: Uint8Array): Uint8Array {
|
||||
ensureBytes(key);
|
||||
const nonce = randomBytes(24);
|
||||
const ciphertext = xchacha20poly1305(key, nonce).encrypt(plaintext);
|
||||
return concatBytes(nonce, ciphertext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt plaintext under key with random nonce, using xchacha20poly1305.
|
||||
* User never touches nonce: it is prepended to ciphertext.
|
||||
*/
|
||||
export function decrypt(key: Uint8Array, ciphertext: Uint8Array): Uint8Array {
|
||||
const nonceLength = 24;
|
||||
ensureBytes(ciphertext);
|
||||
if (ciphertext.length <= nonceLength) throw new Error('invalid ciphertext length');
|
||||
const nonce = ciphertext.subarray(0, nonceLength);
|
||||
const ciphertextWithoutNonce = ciphertext.subarray(nonceLength);
|
||||
return xchacha20poly1305(key, nonce).decrypt(ciphertextWithoutNonce);
|
||||
}
|
||||
|
||||
export async function aes_encrypt(key: Uint8Array, plaintext: Uint8Array): Promise<Uint8Array> {
|
||||
const nonceLength = 12;
|
||||
ensureBytes(key);
|
||||
const nonce = randomBytes(nonceLength);
|
||||
const ciphertext = await aes_256_gcm(key, nonce).encrypt(plaintext);
|
||||
return concatBytes(nonce, ciphertext);
|
||||
}
|
||||
|
||||
export async function aes_decrypt(key: Uint8Array, ciphertext: Uint8Array): Promise<Uint8Array> {
|
||||
const nonceLength = 12;
|
||||
ensureBytes(ciphertext);
|
||||
if (ciphertext.length <= nonceLength) throw new Error('invalid ciphertext length');
|
||||
const nonce = ciphertext.subarray(0, nonceLength);
|
||||
const ciphertextWithoutNonce = ciphertext.subarray(nonceLength);
|
||||
return aes_256_gcm(key, nonce).decrypt(ciphertextWithoutNonce);
|
||||
}
|
||||
-195
@@ -1,195 +0,0 @@
|
||||
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
||||
|
||||
// prettier-ignore
|
||||
export type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |
|
||||
Uint16Array | Int16Array | Uint32Array | Int32Array;
|
||||
|
||||
const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
|
||||
// Cast array to different type
|
||||
export const u8 = (arr: TypedArray) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
||||
export const u16 = (arr: TypedArray) =>
|
||||
new Uint16Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 2));
|
||||
export const u32 = (arr: TypedArray) =>
|
||||
new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
||||
|
||||
// Cast array to view
|
||||
export const createView = (arr: TypedArray) =>
|
||||
new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
||||
|
||||
// big-endian hardware is rare. Just in case someone still decides to run ciphers:
|
||||
// early-throw an error because we don't support BE yet.
|
||||
export const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;
|
||||
if (!isLE) throw new Error('Non little-endian hardware is not supported');
|
||||
|
||||
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>
|
||||
i.toString(16).padStart(2, '0')
|
||||
);
|
||||
/**
|
||||
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
|
||||
*/
|
||||
export function bytesToHex(bytes: Uint8Array): string {
|
||||
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
||||
// pre-caching improves the speed 6x
|
||||
let hex = '';
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
hex += hexes[bytes[i]];
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
|
||||
*/
|
||||
export function hexToBytes(hex: string): Uint8Array {
|
||||
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||
const len = hex.length;
|
||||
if (len % 2) throw new Error('padded hex string expected, got unpadded hex of length ' + len);
|
||||
const array = new Uint8Array(len / 2);
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const j = i * 2;
|
||||
const hexByte = hex.slice(j, j + 2);
|
||||
const byte = Number.parseInt(hexByte, 16);
|
||||
if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence');
|
||||
array[i] = byte;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
// There is no setImmediate in browser and setTimeout is slow.
|
||||
// call of async fn will return Promise, which will be fullfiled only on
|
||||
// next scheduler queue processing step and this is exactly what we need.
|
||||
export const nextTick = async () => {};
|
||||
|
||||
// Returns control to thread each 'tick' ms to avoid blocking
|
||||
export async function asyncLoop(iters: number, tick: number, cb: (i: number) => void) {
|
||||
let ts = Date.now();
|
||||
for (let i = 0; i < iters; i++) {
|
||||
cb(i);
|
||||
// Date.now() is not monotonic, so in case if clock goes backwards we return return control too
|
||||
const diff = Date.now() - ts;
|
||||
if (diff >= 0 && diff < tick) continue;
|
||||
await nextTick();
|
||||
ts += diff;
|
||||
}
|
||||
}
|
||||
|
||||
// Global symbols in both browsers and Node.js since v11
|
||||
// See https://github.com/microsoft/TypeScript/issues/31535
|
||||
declare const TextEncoder: any;
|
||||
declare const TextDecoder: any;
|
||||
|
||||
/**
|
||||
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
|
||||
*/
|
||||
export function utf8ToBytes(str: string): Uint8Array {
|
||||
if (typeof str !== 'string') throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
|
||||
}
|
||||
|
||||
export function bytesToUtf8(bytes: Uint8Array): string {
|
||||
return new TextDecoder().decode(bytes);
|
||||
}
|
||||
|
||||
export type Input = Uint8Array | string;
|
||||
/**
|
||||
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
|
||||
* Warning: when Uint8Array is passed, it would NOT get copied.
|
||||
* Keep in mind for future mutable operations.
|
||||
*/
|
||||
export function toBytes(data: Input): Uint8Array {
|
||||
if (typeof data === 'string') data = utf8ToBytes(data);
|
||||
if (!u8a(data)) throw new Error(`expected Uint8Array, got ${typeof data}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies several Uint8Arrays into one.
|
||||
*/
|
||||
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
||||
const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
|
||||
let pad = 0; // walk through each item, ensure they have proper type
|
||||
arrays.forEach((a) => {
|
||||
if (!u8a(a)) throw new Error('Uint8Array expected');
|
||||
r.set(a, pad);
|
||||
pad += a.length;
|
||||
});
|
||||
return r;
|
||||
}
|
||||
|
||||
// Check if object doens't have custom constructor (like Uint8Array/Array)
|
||||
const isPlainObject = (obj: any) =>
|
||||
Object.prototype.toString.call(obj) === '[object Object]' && obj.constructor === Object;
|
||||
|
||||
type EmptyObj = {};
|
||||
export function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(
|
||||
defaults: T1,
|
||||
opts?: T2
|
||||
): T1 & T2 {
|
||||
if (opts !== undefined && (typeof opts !== 'object' || !isPlainObject(opts)))
|
||||
throw new Error('options must be object or undefined');
|
||||
const merged = Object.assign(defaults, opts);
|
||||
return merged as T1 & T2;
|
||||
}
|
||||
|
||||
export function ensureBytes(b: any, len?: number) {
|
||||
if (!(b instanceof Uint8Array)) throw new Error('Uint8Array expected');
|
||||
if (typeof len === 'number')
|
||||
if (b.length !== len) throw new Error(`Uint8Array length ${len} expected`);
|
||||
}
|
||||
|
||||
// Constant-time equality
|
||||
export function equalBytes(a: Uint8Array, b: Uint8Array): boolean {
|
||||
// Should not happen
|
||||
if (a.length !== b.length) throw new Error('equalBytes: Different size of Uint8Arrays');
|
||||
let isSame = true;
|
||||
for (let i = 0; i < a.length; i++) isSame &&= a[i] === b[i]; // Lets hope JIT won't optimize away.
|
||||
return isSame;
|
||||
}
|
||||
|
||||
// For runtime check if class implements interface
|
||||
export abstract class Hash<T extends Hash<T>> {
|
||||
abstract blockLen: number; // Bytes per block
|
||||
abstract outputLen: number; // Bytes in output
|
||||
abstract update(buf: Input): this;
|
||||
// Writes digest into buf
|
||||
abstract digestInto(buf: Uint8Array): void;
|
||||
abstract digest(): Uint8Array;
|
||||
/**
|
||||
* Resets internal state. Makes Hash instance unusable.
|
||||
* Reset is impossible for keyed hashes if key is consumed into state. If digest is not consumed
|
||||
* by user, they will need to manually call `destroy()` when zeroing is necessary.
|
||||
*/
|
||||
abstract destroy(): void;
|
||||
}
|
||||
|
||||
// This will allow to re-use with composable things like packed & base encoders
|
||||
// Also, we probably can make tags composable
|
||||
export type Cipher = {
|
||||
tagLength?: number;
|
||||
encrypt(plaintext: Uint8Array): Uint8Array;
|
||||
decrypt(ciphertext: Uint8Array): Uint8Array;
|
||||
};
|
||||
|
||||
export type AsyncCipher = {
|
||||
tagLength?: number;
|
||||
encrypt(plaintext: Uint8Array): Promise<Uint8Array>;
|
||||
decrypt(ciphertext: Uint8Array): Promise<Uint8Array>;
|
||||
};
|
||||
|
||||
// Polyfill for Safari 14
|
||||
export function setBigUint64(
|
||||
view: DataView,
|
||||
byteOffset: number,
|
||||
value: bigint,
|
||||
isLE: boolean
|
||||
): void {
|
||||
if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE);
|
||||
const _32n = BigInt(32);
|
||||
const _u32_max = BigInt(0xffffffff);
|
||||
const wh = Number((value >> _32n) & _u32_max);
|
||||
const wl = Number(value & _u32_max);
|
||||
const h = isLE ? 4 : 0;
|
||||
const l = isLE ? 0 : 4;
|
||||
view.setUint32(byteOffset + h, wh, isLE);
|
||||
view.setUint32(byteOffset + l, wl, isLE);
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
import { ensureBytes } from '../utils.js';
|
||||
import { getWebcryptoSubtle } from './utils.js';
|
||||
|
||||
function generate(algo: string, length: number) {
|
||||
const keyLength = length / 8;
|
||||
const keyParams = { name: algo, length };
|
||||
const cryptParams: Record<string, any> = { name: algo };
|
||||
// const params: Record<string, any> = ({ e: algo, i: { name: algo, length } });
|
||||
|
||||
return (key: Uint8Array, nonce: Uint8Array) => {
|
||||
ensureBytes(key, keyLength);
|
||||
if (algo === 'AES-CTR') {
|
||||
cryptParams.counter = nonce;
|
||||
cryptParams.length = 64;
|
||||
} else {
|
||||
cryptParams.iv = nonce;
|
||||
}
|
||||
|
||||
return {
|
||||
keyLength,
|
||||
|
||||
async encrypt(plaintext: Uint8Array): Promise<Uint8Array> {
|
||||
ensureBytes(plaintext);
|
||||
const cr = getWebcryptoSubtle();
|
||||
const iKey = await cr.importKey('raw', key, keyParams, true, ['encrypt']);
|
||||
const cipher = await cr.encrypt(cryptParams, iKey, plaintext);
|
||||
return new Uint8Array(cipher);
|
||||
},
|
||||
|
||||
async decrypt(ciphertext: Uint8Array): Promise<Uint8Array> {
|
||||
ensureBytes(ciphertext);
|
||||
const cr = getWebcryptoSubtle();
|
||||
const iKey = await cr.importKey('raw', key, keyParams, true, ['decrypt']);
|
||||
const plaintext = await cr.decrypt(cryptParams, iKey, ciphertext);
|
||||
return new Uint8Array(plaintext);
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const aes_128_ctr = generate('AES-CTR', 128);
|
||||
export const aes_256_ctr = generate('AES-CTR', 256);
|
||||
|
||||
export const aes_128_cbc = generate('AES-CBC', 128);
|
||||
export const aes_256_cbc = generate('AES-CBC', 256);
|
||||
|
||||
export const aes_128_gcm = generate('AES-GCM', 128);
|
||||
export const aes_256_gcm = generate('AES-GCM', 256);
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
|
||||
// See utils.ts for details.
|
||||
declare const globalThis: Record<string, any> | undefined;
|
||||
export const crypto =
|
||||
typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
|
||||
// See utils.ts for details.
|
||||
// The file will throw on node.js 14 and earlier.
|
||||
// @ts-ignore
|
||||
import * as nc from 'node:crypto';
|
||||
export const crypto =
|
||||
nc && typeof nc === 'object' && 'webcrypto' in nc ? (nc.webcrypto as any) : undefined;
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
import type { AsyncCipher } from '../utils.js';
|
||||
import { getWebcryptoSubtle } from './utils.js';
|
||||
|
||||
// Format-preserving encryption algorithm (FPE-FF1) specified in NIST Special Publication 800-38G.
|
||||
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf
|
||||
|
||||
// Utils
|
||||
function toBytesBE(num: bigint, length?: number): Uint8Array {
|
||||
let hex = num.toString(16);
|
||||
hex = hex.length & 1 ? `0${hex}` : hex;
|
||||
if (length) hex = hex.padStart(length * 2, '00');
|
||||
const len = hex.length / 2;
|
||||
const u8 = new Uint8Array(len);
|
||||
for (let j = 0, i = 0; i < hex.length && i < len * 2; i += 2, j++)
|
||||
u8[j] = parseInt(hex[i] + hex[i + 1], 16);
|
||||
return u8;
|
||||
}
|
||||
|
||||
function fromBytesBE(bytes: Uint8Array): bigint {
|
||||
let value = 0n;
|
||||
for (let i = bytes.length - 1, j = 0; i >= 0; i--, j++)
|
||||
value += (BigInt(bytes[i]) & 255n) << (8n * BigInt(j));
|
||||
return value;
|
||||
}
|
||||
|
||||
// Calculates a modulo b
|
||||
function mod(a: number, b: number): number;
|
||||
function mod(a: bigint, b: bigint): bigint;
|
||||
function mod(a: any, b: any): number | bigint {
|
||||
const result = a % b;
|
||||
return result >= 0 ? result : b + result;
|
||||
}
|
||||
// AES stuff
|
||||
const BLOCK_LEN = 16;
|
||||
const IV = new Uint8Array(BLOCK_LEN);
|
||||
export async function encryptBlock(msg: Uint8Array, key: Uint8Array): Promise<Uint8Array> {
|
||||
if (key.length !== 16 && key.length !== 32) throw new Error('Invalid key length');
|
||||
const cr = getWebcryptoSubtle();
|
||||
const mode = { name: `AES-CBC`, length: key.length * 8 };
|
||||
const wKey = await cr.importKey('raw', key, mode, true, ['encrypt']);
|
||||
const cipher = await cr.encrypt({ name: `aes-cbc`, iv: IV, counter: IV, length: 64 }, wKey, msg);
|
||||
return new Uint8Array(cipher).subarray(0, 16);
|
||||
}
|
||||
|
||||
function NUMradix(radix: number, data: number[]): bigint {
|
||||
let res = 0n;
|
||||
for (let i of data) res = res * BigInt(radix) + BigInt(i);
|
||||
return res;
|
||||
}
|
||||
|
||||
async function getRound(radix: number, key: Uint8Array, tweak: Uint8Array, x: number[]) {
|
||||
if (radix > 2 ** 16 - 1) throw new Error(`Invalid radix: ${radix}`);
|
||||
// radix**minlen ≥ 100
|
||||
const minLen = Math.ceil(Math.log(100) / Math.log(radix));
|
||||
const maxLen = 2 ** 32 - 1;
|
||||
// 2 ≤ minlen ≤ maxlen < 2**32
|
||||
if (2 > minLen || minLen > maxLen || maxLen >= 2 ** 32)
|
||||
throw new Error('Invalid radix: 2 ≤ minlen ≤ maxlen < 2**32');
|
||||
if (x.length < minLen || x.length > maxLen) throw new Error('X is outside minLen..maxLen bounds');
|
||||
const u = Math.floor(x.length / 2);
|
||||
const v = x.length - u;
|
||||
const b = Math.ceil(Math.ceil(v * Math.log2(radix)) / 8);
|
||||
const d = 4 * Math.ceil(b / 4) + 4;
|
||||
const padding = mod(-tweak.length - b - 1, 16);
|
||||
// P = [1]1 || [2]1 || [1]1 || [radix]3 || [10]1 || [u mod 256]1 || [n]4 || [t]4.
|
||||
const P = new Uint8Array([1, 2, 1, 0, 0, 0, 10, u, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
const view = new DataView(P.buffer);
|
||||
view.setUint16(4, radix, false);
|
||||
view.setUint32(8, x.length, false);
|
||||
view.setUint32(12, tweak.length, false);
|
||||
// Q = T || [0](−t−b−1) mod 16 || [i]1 || [NUMradix(B)]b.
|
||||
const PQ = new Uint8Array(P.length + tweak.length + padding + 1 + b);
|
||||
PQ.set(P);
|
||||
P.fill(0);
|
||||
PQ.set(tweak, P.length);
|
||||
const round = async (A: number[], B: number[], i: number, decrypt = false) => {
|
||||
// Q = ... || [i]1 || [NUMradix(B)]b.
|
||||
PQ[PQ.length - b - 1] = i;
|
||||
if (b) PQ.set(toBytesBE(NUMradix(radix, B), b), PQ.length - b);
|
||||
// PRF
|
||||
let r = new Uint8Array(16);
|
||||
for (let j = 0; j < PQ.length / BLOCK_LEN; j++) {
|
||||
for (let i = 0; i < BLOCK_LEN; i++) r[i] ^= PQ[j * BLOCK_LEN + i];
|
||||
r.set(await encryptBlock(r, key));
|
||||
}
|
||||
// Let S be the first d bytes of the following string of ⎡d/16⎤ blocks:
|
||||
// R || CIPHK(R ⊕[1]16) || CIPHK(R ⊕[2]16) ...CIPHK(R ⊕[⎡d / 16⎤ – 1]16).
|
||||
let s = Array.from(r);
|
||||
for (let j = 1; s.length < d; j++) {
|
||||
const block = toBytesBE(BigInt(j), 16);
|
||||
for (let k = 0; k < BLOCK_LEN; k++) block[k] ^= r[k];
|
||||
s.push(...Array.from(await encryptBlock(block, key)));
|
||||
}
|
||||
let y = fromBytesBE(Uint8Array.from(s.slice(0, d)));
|
||||
s.fill(0);
|
||||
if (decrypt) y = -y;
|
||||
const m = i % 2 === 0 ? u : v;
|
||||
let c = mod(NUMradix(radix, A) + y, BigInt(radix) ** BigInt(m));
|
||||
// STR(radix, m, c)
|
||||
const C = Array(m).fill(0);
|
||||
for (let i = 0; i < m; i++, c /= BigInt(radix)) C[m - 1 - i] = Number(c % BigInt(radix));
|
||||
A.fill(0);
|
||||
A = B;
|
||||
B = C;
|
||||
return [A, B];
|
||||
};
|
||||
const destroy = () => PQ.fill(0);
|
||||
return { u, round, destroy };
|
||||
}
|
||||
|
||||
const EMPTY_BUF = new Uint8Array([]);
|
||||
|
||||
export function FF1(radix: number, key: Uint8Array, tweak: Uint8Array = EMPTY_BUF) {
|
||||
const PQ = getRound.bind(null, radix, key, tweak);
|
||||
return {
|
||||
async encrypt(x: number[]) {
|
||||
const { u, round, destroy } = await PQ(x);
|
||||
let [A, B] = [x.slice(0, u), x.slice(u)];
|
||||
for (let i = 0; i < 10; i++) [A, B] = await round(A, B, i);
|
||||
destroy();
|
||||
const res = A.concat(B);
|
||||
A.fill(0);
|
||||
B.fill(0);
|
||||
return res;
|
||||
},
|
||||
async decrypt(x: number[]) {
|
||||
const { u, round, destroy } = await PQ(x);
|
||||
// The FF1.Decrypt algorithm is similar to the FF1.Encrypt algorithm;
|
||||
// the differences are in Step 6, where:
|
||||
// 1) the order of the indices is reversed,
|
||||
// 2) the roles of A and B are swapped
|
||||
// 3) modular addition is replaced by modular subtraction, in Step 6vi.
|
||||
let [B, A] = [x.slice(0, u), x.slice(u)];
|
||||
for (let i = 9; i >= 0; i--) [A, B] = await round(A, B, i, true);
|
||||
destroy();
|
||||
const res = B.concat(A);
|
||||
A.fill(0);
|
||||
B.fill(0);
|
||||
return res;
|
||||
},
|
||||
};
|
||||
}
|
||||
// Binary string which encodes each byte in little-endian byte order
|
||||
const binLE = {
|
||||
encode(bytes: Uint8Array): number[] {
|
||||
const x = [];
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
for (let j = 0, tmp = bytes[i]; j < 8; j++, tmp >>= 1) x.push(tmp & 1);
|
||||
}
|
||||
return x;
|
||||
},
|
||||
decode(b: number[]): Uint8Array {
|
||||
if (b.length % 8) throw new Error('Invalid binary string');
|
||||
const res = new Uint8Array(b.length / 8);
|
||||
for (let i = 0, j = 0; i < res.length; i++) {
|
||||
res[i] = b[j++] | (b[j++] << 1) | (b[j++] << 2) | (b[j++] << 3);
|
||||
res[i] |= (b[j++] << 4) | (b[j++] << 5) | (b[j++] << 6) | (b[j++] << 7);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
};
|
||||
|
||||
export function BinaryFF1(key: Uint8Array, tweak: Uint8Array = EMPTY_BUF): AsyncCipher {
|
||||
const ff1 = FF1(2, key, tweak);
|
||||
return {
|
||||
encrypt: async (x: Uint8Array) => binLE.decode(await ff1.encrypt(binLE.encode(x))),
|
||||
decrypt: async (x: Uint8Array) => binLE.decode(await ff1.decrypt(binLE.encode(x))),
|
||||
};
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
import { AsyncCipher, createView, setBigUint64 } from '../utils.js';
|
||||
import { polyval } from '../_polyval.js';
|
||||
import { getWebcryptoSubtle } from './utils.js';
|
||||
/**
|
||||
* AES-GCM-SIV: classic AES-GCM with nonce-misuse resistance.
|
||||
* RFC 8452, https://datatracker.ietf.org/doc/html/rfc8452
|
||||
*/
|
||||
|
||||
// AES stuff (same as ff1)
|
||||
const BLOCK_LEN = 16;
|
||||
const IV = new Uint8Array(BLOCK_LEN);
|
||||
async function encryptBlock(msg: Uint8Array, key: Uint8Array): Promise<Uint8Array> {
|
||||
if (key.length !== 16 && key.length !== 32) throw new Error('Invalid key length');
|
||||
const mode = { name: `AES-CBC`, length: key.length * 8 };
|
||||
const cr = getWebcryptoSubtle();
|
||||
const wKey = await cr.importKey('raw', key, mode, true, ['encrypt']);
|
||||
const cipher = await cr.encrypt({ name: `aes-cbc`, iv: IV, counter: IV, length: 64 }, wKey, msg);
|
||||
return new Uint8Array(cipher).subarray(0, 16);
|
||||
}
|
||||
|
||||
// Kinda constant-time equality
|
||||
function equalBytes(a: Uint8Array, b: Uint8Array): boolean {
|
||||
// Should not happen
|
||||
if (a.length !== b.length) throw new Error('equalBytes: Different size of Uint8Arrays');
|
||||
let flag = true;
|
||||
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) flag &&= false;
|
||||
return flag;
|
||||
}
|
||||
// Wrap position so it will be in padded to blockSize
|
||||
const wrapPos = (pos: number, blockSize: number) => Math.ceil(pos / blockSize) * blockSize;
|
||||
|
||||
const limit = (name: string, min: number, max: number) => (value: number) => {
|
||||
if (!Number.isSafeInteger(value) || min > value || value > max)
|
||||
throw new Error(`${name}: invalid value=${value}, must be [${min}..${max}]`);
|
||||
};
|
||||
|
||||
// From RFC 8452: Section 6
|
||||
const AAD_LIMIT = limit('AAD', 0, 2 ** 36);
|
||||
const PLAIN_LIMIT = limit('Plaintext', 0, 2 ** 36);
|
||||
const NONCE_LIMIT = limit('Nonce', 12, 12);
|
||||
const CIPHER_LIMIT = limit('Ciphertext', 16, 2 ** 36 + 16);
|
||||
|
||||
// nodejs api doesn't support 32bit counters, browser does
|
||||
async function ctr(key: Uint8Array, tag: Uint8Array, input: Uint8Array) {
|
||||
// The initial counter block is the tag with the most significant bit of the last byte set to one.
|
||||
let block = tag.slice();
|
||||
block[15] |= 0x80;
|
||||
let view = createView(block);
|
||||
let output = new Uint8Array(input.length);
|
||||
for (let pos = 0; pos < input.length; ) {
|
||||
const encryptedBlock = await encryptBlock(block, key);
|
||||
view.setUint32(0, view.getUint32(0, true) + 1, true);
|
||||
const take = Math.min(input.length, encryptedBlock.length);
|
||||
for (let j = 0; j < take; j++, pos++) output[pos] = encryptedBlock[j] ^ input[pos];
|
||||
}
|
||||
return new Uint8Array(output);
|
||||
}
|
||||
|
||||
export async function deriveKeys(key: Uint8Array, nonce: Uint8Array) {
|
||||
NONCE_LIMIT(nonce.length);
|
||||
const len = key.length;
|
||||
if (len !== 16 && len !== 32)
|
||||
throw new Error(`key length must be 16 or 32 bytes, got: ${len} bytes`);
|
||||
const encKey = new Uint8Array(len);
|
||||
const authKey = new Uint8Array(16);
|
||||
let counter = 0;
|
||||
const deriveBlock = new Uint8Array(nonce.length + 4);
|
||||
deriveBlock.set(nonce, 4);
|
||||
const view = createView(deriveBlock);
|
||||
for (const derivedKey of [authKey, encKey]) {
|
||||
for (let i = 0; i < derivedKey.length; i += 8) {
|
||||
view.setUint32(0, counter++, true);
|
||||
const block = await encryptBlock(deriveBlock, key);
|
||||
derivedKey.set(block.subarray(0, 8), i);
|
||||
}
|
||||
}
|
||||
return { authKey, encKey };
|
||||
}
|
||||
|
||||
export async function aes_256_gcm_siv(
|
||||
key: Uint8Array,
|
||||
nonce: Uint8Array,
|
||||
AAD: Uint8Array
|
||||
): Promise<AsyncCipher> {
|
||||
const { encKey, authKey } = await deriveKeys(key, nonce);
|
||||
const computeTag = async (data: Uint8Array, AAD: Uint8Array) => {
|
||||
const dataPos = wrapPos(AAD.length, 16);
|
||||
const lenPos = wrapPos(dataPos + data.length, 16);
|
||||
const block = new Uint8Array(lenPos + 16);
|
||||
const view = createView(block);
|
||||
block.set(AAD);
|
||||
block.set(data, dataPos);
|
||||
setBigUint64(view, lenPos, BigInt(AAD.length * 8), true);
|
||||
setBigUint64(view, lenPos + 8, BigInt(data.length * 8), true);
|
||||
// Compute the expected tag by XORing S_s and the nonce, clearing the
|
||||
// most significant bit of the last byte and encrypting with the
|
||||
// message-encryption key.
|
||||
const tag = polyval(authKey, block);
|
||||
for (let i = 0; i < 12; i++) tag[i] ^= nonce[i];
|
||||
// Clear the highest bit
|
||||
tag[15] &= 0x7f;
|
||||
return await encryptBlock(tag, encKey);
|
||||
};
|
||||
return {
|
||||
// computeTag,
|
||||
encrypt: async (plaintext: Uint8Array) => {
|
||||
AAD_LIMIT(AAD.length);
|
||||
PLAIN_LIMIT(plaintext.length);
|
||||
const tag = await computeTag(plaintext, AAD);
|
||||
const out = new Uint8Array(plaintext.length + 16);
|
||||
out.set(tag, plaintext.length);
|
||||
out.set(await ctr(encKey, tag, plaintext));
|
||||
return out;
|
||||
},
|
||||
decrypt: async (ciphertext: Uint8Array) => {
|
||||
CIPHER_LIMIT(ciphertext.length);
|
||||
AAD_LIMIT(AAD.length);
|
||||
const tag = ciphertext.subarray(-16);
|
||||
const plaintext = await ctr(encKey, tag, ciphertext.subarray(0, -16));
|
||||
const expectedTag = await computeTag(plaintext, AAD);
|
||||
if (!equalBytes(tag, expectedTag)) throw new Error('invalid poly1305 tag');
|
||||
return plaintext;
|
||||
},
|
||||
};
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
|
||||
// node.js versions earlier than v19 don't declare it in global scope.
|
||||
// For node.js, package.js on#exports field mapping rewrites import
|
||||
// from `crypto` to `cryptoNode`, which imports native module.
|
||||
// Makes the utils un-importable in browsers without a bundler.
|
||||
// Once node.js 18 is deprecated, we can just drop the import.
|
||||
import { crypto } from '@noble/ciphers/webcrypto/crypto';
|
||||
|
||||
/**
|
||||
* Secure PRNG. Uses `crypto.getRandomValues`, which defers to OS.
|
||||
*/
|
||||
export function randomBytes(bytesLength = 32): Uint8Array {
|
||||
if (crypto && typeof crypto.getRandomValues === 'function') {
|
||||
return crypto.getRandomValues(new Uint8Array(bytesLength));
|
||||
}
|
||||
throw new Error('crypto.getRandomValues must be defined');
|
||||
}
|
||||
|
||||
export function getWebcryptoSubtle() {
|
||||
if (crypto && typeof crypto.subtle === 'object' && crypto.subtle != null) {
|
||||
return crypto.subtle;
|
||||
}
|
||||
throw new Error('crypto.subtle must be defined');
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
||||
export type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array | Uint16Array | Int16Array | Uint32Array | Int32Array;
|
||||
export declare const u8: (arr: TypedArray) => Uint8Array;
|
||||
export declare const u16: (arr: TypedArray) => Uint16Array;
|
||||
export declare const u32: (arr: TypedArray) => Uint32Array;
|
||||
export declare const createView: (arr: TypedArray) => DataView;
|
||||
export declare const isLE: boolean;
|
||||
/**
|
||||
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
|
||||
*/
|
||||
export declare function bytesToHex(bytes: Uint8Array): string;
|
||||
/**
|
||||
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
|
||||
*/
|
||||
export declare function hexToBytes(hex: string): Uint8Array;
|
||||
export declare const nextTick: () => Promise<void>;
|
||||
export declare function asyncLoop(iters: number, tick: number, cb: (i: number) => void): Promise<void>;
|
||||
/**
|
||||
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
|
||||
*/
|
||||
export declare function utf8ToBytes(str: string): Uint8Array;
|
||||
export declare function bytesToUtf8(bytes: Uint8Array): string;
|
||||
export type Input = Uint8Array | string;
|
||||
/**
|
||||
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
|
||||
* Warning: when Uint8Array is passed, it would NOT get copied.
|
||||
* Keep in mind for future mutable operations.
|
||||
*/
|
||||
export declare function toBytes(data: Input): Uint8Array;
|
||||
/**
|
||||
* Copies several Uint8Arrays into one.
|
||||
*/
|
||||
export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
|
||||
type EmptyObj = {};
|
||||
export declare function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(defaults: T1, opts?: T2): T1 & T2;
|
||||
export declare function ensureBytes(b: any, len?: number): void;
|
||||
export declare function equalBytes(a: Uint8Array, b: Uint8Array): boolean;
|
||||
export declare abstract class Hash<T extends Hash<T>> {
|
||||
abstract blockLen: number;
|
||||
abstract outputLen: number;
|
||||
abstract update(buf: Input): this;
|
||||
abstract digestInto(buf: Uint8Array): void;
|
||||
abstract digest(): Uint8Array;
|
||||
/**
|
||||
* Resets internal state. Makes Hash instance unusable.
|
||||
* Reset is impossible for keyed hashes if key is consumed into state. If digest is not consumed
|
||||
* by user, they will need to manually call `destroy()` when zeroing is necessary.
|
||||
*/
|
||||
abstract destroy(): void;
|
||||
}
|
||||
export type Cipher = {
|
||||
tagLength?: number;
|
||||
encrypt(plaintext: Uint8Array): Uint8Array;
|
||||
decrypt(ciphertext: Uint8Array): Uint8Array;
|
||||
};
|
||||
export type AsyncCipher = {
|
||||
tagLength?: number;
|
||||
encrypt(plaintext: Uint8Array): Promise<Uint8Array>;
|
||||
decrypt(ciphertext: Uint8Array): Promise<Uint8Array>;
|
||||
};
|
||||
export declare function setBigUint64(view: DataView, byteOffset: number, value: bigint, isLE: boolean): void;
|
||||
export {};
|
||||
//# sourceMappingURL=utils.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["src/utils.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAGvE,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,iBAAiB,GAAG,UAAU,GACjE,WAAW,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;AAItD,eAAO,MAAM,EAAE,QAAS,UAAU,eAA+D,CAAC;AAClG,eAAO,MAAM,GAAG,QAAS,UAAU,gBAC0C,CAAC;AAC9E,eAAO,MAAM,GAAG,QAAS,UAAU,gBAC0C,CAAC;AAG9E,eAAO,MAAM,UAAU,QAAS,UAAU,aACgB,CAAC;AAI3D,eAAO,MAAM,IAAI,SAAmE,CAAC;AAMrF;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAQpD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAalD;AAKD,eAAO,MAAM,QAAQ,qBAAiB,CAAC;AAGvC,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,iBAUnF;AAOD;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAGnD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAErD;AAED,MAAM,MAAM,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC;AACxC;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK,GAAG,UAAU,CAI/C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAS/D;AAMD,KAAK,QAAQ,GAAG,EAAE,CAAC;AACnB,wBAAgB,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,EAAE,SAAS,QAAQ,EAChE,QAAQ,EAAE,EAAE,EACZ,IAAI,CAAC,EAAE,EAAE,GACR,EAAE,GAAG,EAAE,CAKT;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,QAI/C;AAGD,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAMhE;AAGD,8BAAsB,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI;IAEjC,QAAQ,CAAC,UAAU,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAC1C,QAAQ,CAAC,MAAM,IAAI,UAAU;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,IAAI,IAAI;CACzB;AAID,MAAM,MAAM,MAAM,GAAG;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IAC3C,OAAO,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACpD,OAAO,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACtD,CAAC;AAGF,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,OAAO,GACZ,IAAI,CAUN"}
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
"use strict";
|
||||
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setBigUint64 = exports.Hash = exports.equalBytes = exports.ensureBytes = exports.checkOpts = exports.concatBytes = exports.toBytes = exports.bytesToUtf8 = exports.utf8ToBytes = exports.asyncLoop = exports.nextTick = exports.hexToBytes = exports.bytesToHex = exports.isLE = exports.createView = exports.u32 = exports.u16 = exports.u8 = void 0;
|
||||
const u8a = (a) => a instanceof Uint8Array;
|
||||
// Cast array to different type
|
||||
const u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
||||
exports.u8 = u8;
|
||||
const u16 = (arr) => new Uint16Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 2));
|
||||
exports.u16 = u16;
|
||||
const u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
||||
exports.u32 = u32;
|
||||
// Cast array to view
|
||||
const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
||||
exports.createView = createView;
|
||||
// big-endian hardware is rare. Just in case someone still decides to run ciphers:
|
||||
// early-throw an error because we don't support BE yet.
|
||||
exports.isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;
|
||||
if (!exports.isLE)
|
||||
throw new Error('Non little-endian hardware is not supported');
|
||||
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
|
||||
/**
|
||||
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
|
||||
*/
|
||||
function bytesToHex(bytes) {
|
||||
if (!u8a(bytes))
|
||||
throw new Error('Uint8Array expected');
|
||||
// pre-caching improves the speed 6x
|
||||
let hex = '';
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
hex += hexes[bytes[i]];
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
exports.bytesToHex = bytesToHex;
|
||||
/**
|
||||
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
|
||||
*/
|
||||
function hexToBytes(hex) {
|
||||
if (typeof hex !== 'string')
|
||||
throw new Error('hex string expected, got ' + typeof hex);
|
||||
const len = hex.length;
|
||||
if (len % 2)
|
||||
throw new Error('padded hex string expected, got unpadded hex of length ' + len);
|
||||
const array = new Uint8Array(len / 2);
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const j = i * 2;
|
||||
const hexByte = hex.slice(j, j + 2);
|
||||
const byte = Number.parseInt(hexByte, 16);
|
||||
if (Number.isNaN(byte) || byte < 0)
|
||||
throw new Error('Invalid byte sequence');
|
||||
array[i] = byte;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
exports.hexToBytes = hexToBytes;
|
||||
// There is no setImmediate in browser and setTimeout is slow.
|
||||
// call of async fn will return Promise, which will be fullfiled only on
|
||||
// next scheduler queue processing step and this is exactly what we need.
|
||||
const nextTick = async () => { };
|
||||
exports.nextTick = nextTick;
|
||||
// Returns control to thread each 'tick' ms to avoid blocking
|
||||
async function asyncLoop(iters, tick, cb) {
|
||||
let ts = Date.now();
|
||||
for (let i = 0; i < iters; i++) {
|
||||
cb(i);
|
||||
// Date.now() is not monotonic, so in case if clock goes backwards we return return control too
|
||||
const diff = Date.now() - ts;
|
||||
if (diff >= 0 && diff < tick)
|
||||
continue;
|
||||
await (0, exports.nextTick)();
|
||||
ts += diff;
|
||||
}
|
||||
}
|
||||
exports.asyncLoop = asyncLoop;
|
||||
/**
|
||||
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
|
||||
*/
|
||||
function utf8ToBytes(str) {
|
||||
if (typeof str !== 'string')
|
||||
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
|
||||
}
|
||||
exports.utf8ToBytes = utf8ToBytes;
|
||||
function bytesToUtf8(bytes) {
|
||||
return new TextDecoder().decode(bytes);
|
||||
}
|
||||
exports.bytesToUtf8 = bytesToUtf8;
|
||||
/**
|
||||
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
|
||||
* Warning: when Uint8Array is passed, it would NOT get copied.
|
||||
* Keep in mind for future mutable operations.
|
||||
*/
|
||||
function toBytes(data) {
|
||||
if (typeof data === 'string')
|
||||
data = utf8ToBytes(data);
|
||||
if (!u8a(data))
|
||||
throw new Error(`expected Uint8Array, got ${typeof data}`);
|
||||
return data;
|
||||
}
|
||||
exports.toBytes = toBytes;
|
||||
/**
|
||||
* Copies several Uint8Arrays into one.
|
||||
*/
|
||||
function concatBytes(...arrays) {
|
||||
const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
|
||||
let pad = 0; // walk through each item, ensure they have proper type
|
||||
arrays.forEach((a) => {
|
||||
if (!u8a(a))
|
||||
throw new Error('Uint8Array expected');
|
||||
r.set(a, pad);
|
||||
pad += a.length;
|
||||
});
|
||||
return r;
|
||||
}
|
||||
exports.concatBytes = concatBytes;
|
||||
// Check if object doens't have custom constructor (like Uint8Array/Array)
|
||||
const isPlainObject = (obj) => Object.prototype.toString.call(obj) === '[object Object]' && obj.constructor === Object;
|
||||
function checkOpts(defaults, opts) {
|
||||
if (opts !== undefined && (typeof opts !== 'object' || !isPlainObject(opts)))
|
||||
throw new Error('options must be object or undefined');
|
||||
const merged = Object.assign(defaults, opts);
|
||||
return merged;
|
||||
}
|
||||
exports.checkOpts = checkOpts;
|
||||
function ensureBytes(b, len) {
|
||||
if (!(b instanceof Uint8Array))
|
||||
throw new Error('Uint8Array expected');
|
||||
if (typeof len === 'number')
|
||||
if (b.length !== len)
|
||||
throw new Error(`Uint8Array length ${len} expected`);
|
||||
}
|
||||
exports.ensureBytes = ensureBytes;
|
||||
// Constant-time equality
|
||||
function equalBytes(a, b) {
|
||||
// Should not happen
|
||||
if (a.length !== b.length)
|
||||
throw new Error('equalBytes: Different size of Uint8Arrays');
|
||||
let isSame = true;
|
||||
for (let i = 0; i < a.length; i++)
|
||||
isSame && (isSame = a[i] === b[i]); // Lets hope JIT won't optimize away.
|
||||
return isSame;
|
||||
}
|
||||
exports.equalBytes = equalBytes;
|
||||
// For runtime check if class implements interface
|
||||
class Hash {
|
||||
}
|
||||
exports.Hash = Hash;
|
||||
// Polyfill for Safari 14
|
||||
function setBigUint64(view, byteOffset, value, isLE) {
|
||||
if (typeof view.setBigUint64 === 'function')
|
||||
return view.setBigUint64(byteOffset, value, isLE);
|
||||
const _32n = BigInt(32);
|
||||
const _u32_max = BigInt(0xffffffff);
|
||||
const wh = Number((value >> _32n) & _u32_max);
|
||||
const wl = Number(value & _u32_max);
|
||||
const h = isLE ? 4 : 0;
|
||||
const l = isLE ? 0 : 4;
|
||||
view.setUint32(byteOffset + h, wh, isLE);
|
||||
view.setUint32(byteOffset + l, wl, isLE);
|
||||
}
|
||||
exports.setBigUint64 = setBigUint64;
|
||||
//# sourceMappingURL=utils.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
-31
@@ -1,31 +0,0 @@
|
||||
export declare const aes_128_ctr: (key: Uint8Array, nonce: Uint8Array) => {
|
||||
keyLength: number;
|
||||
encrypt(plaintext: Uint8Array): Promise<Uint8Array>;
|
||||
decrypt(ciphertext: Uint8Array): Promise<Uint8Array>;
|
||||
};
|
||||
export declare const aes_256_ctr: (key: Uint8Array, nonce: Uint8Array) => {
|
||||
keyLength: number;
|
||||
encrypt(plaintext: Uint8Array): Promise<Uint8Array>;
|
||||
decrypt(ciphertext: Uint8Array): Promise<Uint8Array>;
|
||||
};
|
||||
export declare const aes_128_cbc: (key: Uint8Array, nonce: Uint8Array) => {
|
||||
keyLength: number;
|
||||
encrypt(plaintext: Uint8Array): Promise<Uint8Array>;
|
||||
decrypt(ciphertext: Uint8Array): Promise<Uint8Array>;
|
||||
};
|
||||
export declare const aes_256_cbc: (key: Uint8Array, nonce: Uint8Array) => {
|
||||
keyLength: number;
|
||||
encrypt(plaintext: Uint8Array): Promise<Uint8Array>;
|
||||
decrypt(ciphertext: Uint8Array): Promise<Uint8Array>;
|
||||
};
|
||||
export declare const aes_128_gcm: (key: Uint8Array, nonce: Uint8Array) => {
|
||||
keyLength: number;
|
||||
encrypt(plaintext: Uint8Array): Promise<Uint8Array>;
|
||||
decrypt(ciphertext: Uint8Array): Promise<Uint8Array>;
|
||||
};
|
||||
export declare const aes_256_gcm: (key: Uint8Array, nonce: Uint8Array) => {
|
||||
keyLength: number;
|
||||
encrypt(plaintext: Uint8Array): Promise<Uint8Array>;
|
||||
decrypt(ciphertext: Uint8Array): Promise<Uint8Array>;
|
||||
};
|
||||
//# sourceMappingURL=aes.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"aes.d.ts","sourceRoot":"","sources":["../src/webcrypto/aes.ts"],"names":[],"mappings":"AAwCA,eAAO,MAAM,WAAW,QA/BT,UAAU,SAAS,UAAU;;uBAYb,UAAU,GAAG,QAAQ,UAAU,CAAC;wBAQ/B,UAAU,GAAG,QAAQ,UAAU,CAAC;CAWb,CAAC;AACpD,eAAO,MAAM,WAAW,QAhCT,UAAU,SAAS,UAAU;;uBAYb,UAAU,GAAG,QAAQ,UAAU,CAAC;wBAQ/B,UAAU,GAAG,QAAQ,UAAU,CAAC;CAYb,CAAC;AAEpD,eAAO,MAAM,WAAW,QAlCT,UAAU,SAAS,UAAU;;uBAYb,UAAU,GAAG,QAAQ,UAAU,CAAC;wBAQ/B,UAAU,GAAG,QAAQ,UAAU,CAAC;CAcb,CAAC;AACpD,eAAO,MAAM,WAAW,QAnCT,UAAU,SAAS,UAAU;;uBAYb,UAAU,GAAG,QAAQ,UAAU,CAAC;wBAQ/B,UAAU,GAAG,QAAQ,UAAU,CAAC;CAeb,CAAC;AAEpD,eAAO,MAAM,WAAW,QArCT,UAAU,SAAS,UAAU;;uBAYb,UAAU,GAAG,QAAQ,UAAU,CAAC;wBAQ/B,UAAU,GAAG,QAAQ,UAAU,CAAC;CAiBb,CAAC;AACpD,eAAO,MAAM,WAAW,QAtCT,UAAU,SAAS,UAAU;;uBAYb,UAAU,GAAG,QAAQ,UAAU,CAAC;wBAQ/B,UAAU,GAAG,QAAQ,UAAU,CAAC;CAkBb,CAAC"}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.aes_256_gcm = exports.aes_128_gcm = exports.aes_256_cbc = exports.aes_128_cbc = exports.aes_256_ctr = exports.aes_128_ctr = void 0;
|
||||
const utils_js_1 = require("../utils.js");
|
||||
const utils_js_2 = require("./utils.js");
|
||||
function generate(algo, length) {
|
||||
const keyLength = length / 8;
|
||||
const keyParams = { name: algo, length };
|
||||
const cryptParams = { name: algo };
|
||||
// const params: Record<string, any> = ({ e: algo, i: { name: algo, length } });
|
||||
return (key, nonce) => {
|
||||
(0, utils_js_1.ensureBytes)(key, keyLength);
|
||||
if (algo === 'AES-CTR') {
|
||||
cryptParams.counter = nonce;
|
||||
cryptParams.length = 64;
|
||||
}
|
||||
else {
|
||||
cryptParams.iv = nonce;
|
||||
}
|
||||
return {
|
||||
keyLength,
|
||||
async encrypt(plaintext) {
|
||||
(0, utils_js_1.ensureBytes)(plaintext);
|
||||
const cr = (0, utils_js_2.getWebcryptoSubtle)();
|
||||
const iKey = await cr.importKey('raw', key, keyParams, true, ['encrypt']);
|
||||
const cipher = await cr.encrypt(cryptParams, iKey, plaintext);
|
||||
return new Uint8Array(cipher);
|
||||
},
|
||||
async decrypt(ciphertext) {
|
||||
(0, utils_js_1.ensureBytes)(ciphertext);
|
||||
const cr = (0, utils_js_2.getWebcryptoSubtle)();
|
||||
const iKey = await cr.importKey('raw', key, keyParams, true, ['decrypt']);
|
||||
const plaintext = await cr.decrypt(cryptParams, iKey, ciphertext);
|
||||
return new Uint8Array(plaintext);
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
exports.aes_128_ctr = generate('AES-CTR', 128);
|
||||
exports.aes_256_ctr = generate('AES-CTR', 256);
|
||||
exports.aes_128_cbc = generate('AES-CBC', 128);
|
||||
exports.aes_256_cbc = generate('AES-CBC', 256);
|
||||
exports.aes_128_gcm = generate('AES-GCM', 128);
|
||||
exports.aes_256_gcm = generate('AES-GCM', 256);
|
||||
//# sourceMappingURL=aes.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"aes.js","sourceRoot":"","sources":["../src/webcrypto/aes.ts"],"names":[],"mappings":";;;AAAA,0CAA0C;AAC1C,yCAAgD;AAEhD,SAAS,QAAQ,CAAC,IAAY,EAAE,MAAc;IAC5C,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC;IAC7B,MAAM,SAAS,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACzC,MAAM,WAAW,GAAwB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxD,gFAAgF;IAEhF,OAAO,CAAC,GAAe,EAAE,KAAiB,EAAE,EAAE;QAC5C,IAAA,sBAAW,EAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC5B,IAAI,IAAI,KAAK,SAAS,EAAE;YACtB,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;YAC5B,WAAW,CAAC,MAAM,GAAG,EAAE,CAAC;SACzB;aAAM;YACL,WAAW,CAAC,EAAE,GAAG,KAAK,CAAC;SACxB;QAED,OAAO;YACL,SAAS;YAET,KAAK,CAAC,OAAO,CAAC,SAAqB;gBACjC,IAAA,sBAAW,EAAC,SAAS,CAAC,CAAC;gBACvB,MAAM,EAAE,GAAG,IAAA,6BAAkB,GAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;gBAC1E,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;gBAC9D,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;YAED,KAAK,CAAC,OAAO,CAAC,UAAsB;gBAClC,IAAA,sBAAW,EAAC,UAAU,CAAC,CAAC;gBACxB,MAAM,EAAE,GAAG,IAAA,6BAAkB,GAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;gBAC1E,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;gBAClE,OAAO,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;YACnC,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAEY,QAAA,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACvC,QAAA,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAEvC,QAAA,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACvC,QAAA,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAEvC,QAAA,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACvC,QAAA,WAAW,GAAG,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC"}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
export declare const crypto: any;
|
||||
//# sourceMappingURL=crypto.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/webcrypto/crypto.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,MAAM,KACuE,CAAC"}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.crypto = void 0;
|
||||
exports.crypto = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
|
||||
//# sourceMappingURL=crypto.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/webcrypto/crypto.ts"],"names":[],"mappings":";;;AAGa,QAAA,MAAM,GACjB,OAAO,UAAU,KAAK,QAAQ,IAAI,QAAQ,IAAI,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC"}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user