leviathan-crypto 1.3.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +129 -76
- package/README.md +166 -221
- package/SECURITY.md +89 -37
- package/dist/chacha20/cipher-suite.d.ts +4 -0
- package/dist/chacha20/cipher-suite.js +78 -0
- package/dist/chacha20/embedded.d.ts +1 -0
- package/dist/chacha20/embedded.js +27 -0
- package/dist/chacha20/index.d.ts +20 -7
- package/dist/chacha20/index.js +41 -14
- package/dist/chacha20/ops.d.ts +1 -1
- package/dist/chacha20/ops.js +19 -18
- package/dist/chacha20/pool-worker.js +77 -0
- package/dist/ct-wasm.d.ts +1 -0
- package/dist/ct-wasm.js +3 -0
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +320 -0
- package/dist/docs/architecture.md +419 -285
- package/dist/docs/argon2id.md +42 -30
- package/dist/docs/chacha20.md +218 -150
- package/dist/docs/exports.md +241 -0
- package/dist/docs/fortuna.md +65 -74
- package/dist/docs/init.md +172 -178
- package/dist/docs/loader.md +87 -132
- package/dist/docs/serpent.md +134 -565
- package/dist/docs/sha2.md +91 -103
- package/dist/docs/sha3.md +70 -36
- package/dist/docs/types.md +93 -16
- package/dist/docs/utils.md +114 -41
- package/dist/embedded/chacha20.d.ts +1 -1
- package/dist/embedded/chacha20.js +2 -1
- package/dist/embedded/kyber.d.ts +1 -0
- package/dist/embedded/kyber.js +3 -0
- package/dist/embedded/serpent.d.ts +1 -1
- package/dist/embedded/serpent.js +2 -1
- package/dist/embedded/sha2.d.ts +1 -1
- package/dist/embedded/sha2.js +2 -1
- package/dist/embedded/sha3.d.ts +1 -1
- package/dist/embedded/sha3.js +2 -1
- package/dist/errors.d.ts +10 -0
- package/dist/{serpent/seal.js → errors.js} +14 -46
- package/dist/fortuna.d.ts +2 -8
- package/dist/fortuna.js +11 -9
- package/dist/index.d.ts +25 -9
- package/dist/index.js +36 -7
- package/dist/init.d.ts +3 -7
- package/dist/init.js +18 -35
- package/dist/keccak/embedded.d.ts +1 -0
- package/dist/keccak/embedded.js +27 -0
- package/dist/keccak/index.d.ts +4 -0
- package/dist/keccak/index.js +31 -0
- package/dist/kyber/embedded.d.ts +1 -0
- package/dist/kyber/embedded.js +27 -0
- package/dist/kyber/indcpa.d.ts +49 -0
- package/dist/kyber/indcpa.js +352 -0
- package/dist/kyber/index.d.ts +38 -0
- package/dist/kyber/index.js +150 -0
- package/dist/kyber/kem.d.ts +21 -0
- package/dist/kyber/kem.js +160 -0
- package/dist/kyber/params.d.ts +14 -0
- package/dist/kyber/params.js +37 -0
- package/dist/kyber/suite.d.ts +13 -0
- package/dist/kyber/suite.js +93 -0
- package/dist/kyber/types.d.ts +98 -0
- package/dist/kyber/types.js +25 -0
- package/dist/kyber/validate.d.ts +19 -0
- package/dist/kyber/validate.js +68 -0
- package/dist/kyber.wasm +0 -0
- package/dist/loader.d.ts +19 -4
- package/dist/loader.js +91 -25
- package/dist/serpent/cipher-suite.d.ts +4 -0
- package/dist/serpent/cipher-suite.js +121 -0
- package/dist/serpent/embedded.d.ts +1 -0
- package/dist/serpent/embedded.js +27 -0
- package/dist/serpent/index.d.ts +6 -37
- package/dist/serpent/index.js +9 -118
- package/dist/serpent/pool-worker.d.ts +1 -0
- package/dist/serpent/pool-worker.js +202 -0
- package/dist/serpent/serpent-cbc.d.ts +30 -0
- package/dist/serpent/serpent-cbc.js +136 -0
- package/dist/sha2/embedded.d.ts +1 -0
- package/dist/sha2/embedded.js +27 -0
- package/dist/sha2/hkdf.js +6 -2
- package/dist/sha2/index.d.ts +3 -2
- package/dist/sha2/index.js +3 -4
- package/dist/sha3/embedded.d.ts +1 -0
- package/dist/sha3/embedded.js +27 -0
- package/dist/sha3/index.d.ts +3 -2
- package/dist/sha3/index.js +3 -4
- package/dist/stream/constants.d.ts +6 -0
- package/dist/stream/constants.js +30 -0
- package/dist/stream/header.d.ts +9 -0
- package/dist/stream/header.js +77 -0
- package/dist/stream/index.d.ts +7 -0
- package/dist/stream/index.js +27 -0
- package/dist/stream/open-stream.d.ts +21 -0
- package/dist/stream/open-stream.js +146 -0
- package/dist/stream/seal-stream-pool.d.ts +38 -0
- package/dist/stream/seal-stream-pool.js +391 -0
- package/dist/stream/seal-stream.d.ts +20 -0
- package/dist/stream/seal-stream.js +142 -0
- package/dist/stream/seal.d.ts +9 -0
- package/dist/stream/seal.js +75 -0
- package/dist/stream/types.d.ts +24 -0
- package/dist/stream/types.js +26 -0
- package/dist/utils.d.ts +12 -7
- package/dist/utils.js +75 -19
- package/dist/wasm-source.d.ts +12 -0
- package/dist/wasm-source.js +26 -0
- package/package.json +13 -5
- package/dist/chacha20/pool.d.ts +0 -52
- package/dist/chacha20/pool.js +0 -188
- package/dist/chacha20/pool.worker.js +0 -37
- package/dist/docs/chacha20_pool.md +0 -309
- package/dist/docs/wasm.md +0 -194
- package/dist/serpent/seal.d.ts +0 -8
- package/dist/serpent/stream-pool.d.ts +0 -48
- package/dist/serpent/stream-pool.js +0 -285
- package/dist/serpent/stream-sealer.d.ts +0 -50
- package/dist/serpent/stream-sealer.js +0 -341
- package/dist/serpent/stream.d.ts +0 -28
- package/dist/serpent/stream.js +0 -205
- package/dist/serpent/stream.worker.d.ts +0 -32
- package/dist/serpent/stream.worker.js +0 -117
- /package/dist/chacha20/{pool.worker.d.ts → pool-worker.d.ts} +0 -0
package/dist/docs/chacha20.md
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
# ChaCha20
|
|
1
|
+
# ChaCha20 TypeScript API
|
|
2
2
|
|
|
3
3
|
> [!NOTE]
|
|
4
|
-
>
|
|
4
|
+
> API reference for `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, and `XChaCha20Cipher`. Covers initialization, all class methods, usage examples, and error conditions.
|
|
5
|
+
|
|
6
|
+
> ### Table of Contents
|
|
7
|
+
> - [Overview](#overview)
|
|
8
|
+
> - [Security Notes](#security-notes)
|
|
9
|
+
> - [Module Init](#module-init)
|
|
10
|
+
> - [API Reference](#api-reference)
|
|
11
|
+
> - [`ChaCha20`](#chacha20)
|
|
12
|
+
> - [`Poly1305`](#poly1305)
|
|
13
|
+
> - [`ChaCha20Poly1305`](#chacha20poly1305)
|
|
14
|
+
> - [`XChaCha20Poly1305`](#xchacha20poly1305)
|
|
15
|
+
> - [XChaCha20Cipher](#xchacha20cipher)
|
|
16
|
+
> - [Usage Examples](#usage-examples)
|
|
17
|
+
> - [Error Conditions](#error-conditions)
|
|
18
|
+
|
|
19
|
+
---
|
|
5
20
|
|
|
6
21
|
## Overview
|
|
7
22
|
|
|
@@ -9,37 +24,40 @@
|
|
|
9
24
|
on all platforms (including those without hardware AES), resistant to timing attacks
|
|
10
25
|
by design, and widely deployed in TLS, SSH, and WireGuard. ChaCha20 encrypts data
|
|
11
26
|
by generating a pseudorandom keystream from a 256-bit key and a nonce, then XORing
|
|
12
|
-
it with the plaintext. It does **not** provide authentication on its own
|
|
13
|
-
modified message will decrypt to garbage with no warning.
|
|
27
|
+
it with the plaintext. It does **not** provide authentication on its own. A modified message will decrypt to garbage with no warning.
|
|
14
28
|
|
|
15
29
|
**Poly1305** is a one-time message authentication code (MAC). Given a unique 256-bit
|
|
16
30
|
key and a message, it produces a 16-byte tag that proves the message has not been
|
|
17
31
|
tampered with. The critical requirement is that each Poly1305 key is used **exactly
|
|
18
|
-
once
|
|
32
|
+
once**. Reusing a key completely breaks its security. You almost never need to use
|
|
19
33
|
Poly1305 directly; the AEAD constructions below handle key derivation for you.
|
|
20
34
|
|
|
21
35
|
**ChaCha20-Poly1305** (RFC 8439) combines both primitives into an AEAD
|
|
22
36
|
(Authenticated Encryption with Associated Data). It encrypts your data and
|
|
23
37
|
produces an authentication tag in a single operation. On decryption, it verifies
|
|
24
|
-
the tag before returning any plaintext
|
|
38
|
+
the tag before returning any plaintext. If someone tampered with the ciphertext,
|
|
25
39
|
you get an error instead of corrupted data. The nonce is 96 bits (12 bytes).
|
|
26
40
|
|
|
27
41
|
**XChaCha20-Poly1305** extends the nonce to 192 bits (24 bytes) using the HChaCha20
|
|
28
|
-
subkey derivation step. This makes random nonce generation completely safe
|
|
42
|
+
subkey derivation step. This makes random nonce generation completely safe. With a
|
|
29
43
|
24-byte nonce, the probability of a collision is negligible even after billions of
|
|
30
|
-
messages. **For most users,
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
messages. **For most users, `Seal` with `XChaCha20Cipher` is the recommended choice.** It
|
|
45
|
+
produces a self-contained authenticated blob with no nonce management, no
|
|
46
|
+
instantiation, and no `dispose()`. For protocol interop requiring explicit nonce
|
|
47
|
+
control, use `XChaCha20Poly1305` directly.
|
|
48
|
+
|
|
49
|
+
---
|
|
33
50
|
|
|
34
51
|
## Security Notes
|
|
35
52
|
|
|
36
53
|
> [!IMPORTANT]
|
|
37
|
-
> Read this section before writing any code. These are not theoretical concerns
|
|
38
|
-
>
|
|
54
|
+
> Read this section before writing any code. These are not theoretical concerns.
|
|
55
|
+
> They are the mistakes that cause real-world breaches.
|
|
39
56
|
|
|
40
|
-
- **Use `
|
|
41
|
-
safest default: authenticated encryption
|
|
42
|
-
|
|
57
|
+
- **Use `Seal` with `XChaCha20Cipher` unless you need explicit nonce control.**
|
|
58
|
+
It is the safest default: authenticated encryption in a single static call.
|
|
59
|
+
If you are unsure which class to pick, pick this one. Use `XChaCha20Poly1305`
|
|
60
|
+
when protocol interop requires you to manage nonces yourself.
|
|
43
61
|
|
|
44
62
|
- **Never reuse a nonce with the same key.** This is the single most important
|
|
45
63
|
rule. If you encrypt two different messages with the same key and the same nonce,
|
|
@@ -47,7 +65,7 @@ the most common footgun (accidental nonce reuse).
|
|
|
47
65
|
With `ChaCha20Poly1305` (12-byte nonce), random generation has a meaningful
|
|
48
66
|
collision risk after roughly 2^32 messages under one key. With
|
|
49
67
|
`XChaCha20Poly1305` (24-byte nonce), random generation is safe for any practical
|
|
50
|
-
message count
|
|
68
|
+
message count. Just call `randomBytes(24)` for each message.
|
|
51
69
|
|
|
52
70
|
- **Poly1305 keys are single-use.** Each Poly1305 key must be used to authenticate
|
|
53
71
|
exactly one message. The AEAD classes (`ChaCha20Poly1305` and
|
|
@@ -56,13 +74,12 @@ the most common footgun (accidental nonce reuse).
|
|
|
56
74
|
`Poly1305` class directly, it is your responsibility to never reuse a key.
|
|
57
75
|
|
|
58
76
|
- **AEAD protects both confidentiality and authenticity.** If authentication fails
|
|
59
|
-
during decryption, the plaintext is never returned
|
|
60
|
-
intentional. Do not try to work around it. If decryption fails, the ciphertext
|
|
61
|
-
was corrupted or tampered with.
|
|
77
|
+
during decryption, the plaintext is never returned. You get an error. This is
|
|
78
|
+
intentional. Do not try to work around it. If decryption fails, the ciphertext was corrupted or tampered with.
|
|
62
79
|
|
|
63
80
|
- **Associated data (AAD) is authenticated but not encrypted.** Use AAD for data
|
|
64
81
|
that must travel in the clear (headers, routing metadata, user IDs) but must be
|
|
65
|
-
verified as unmodified. If someone changes the AAD, decryption will fail
|
|
82
|
+
verified as unmodified. If someone changes the AAD, decryption will fail even
|
|
66
83
|
if the ciphertext itself is untouched.
|
|
67
84
|
|
|
68
85
|
- **Always call `dispose()` when you are done.** This wipes key material and
|
|
@@ -75,29 +92,32 @@ the most common footgun (accidental nonce reuse).
|
|
|
75
92
|
building your own authenticated construction (and you probably should not be),
|
|
76
93
|
use one of the AEAD classes instead.
|
|
77
94
|
|
|
95
|
+
---
|
|
96
|
+
|
|
78
97
|
## Module Init
|
|
79
98
|
|
|
80
99
|
Each module subpath exports its own init function for consumers who want
|
|
81
100
|
tree-shakeable imports.
|
|
82
101
|
|
|
83
|
-
### `chacha20Init(
|
|
102
|
+
### `chacha20Init(source)`
|
|
84
103
|
|
|
85
104
|
Initializes only the chacha20 WASM binary. Equivalent to calling the
|
|
86
|
-
root `init(
|
|
105
|
+
root `init({ chacha20: chacha20Wasm })` but without pulling the other three
|
|
87
106
|
modules into the bundle.
|
|
88
107
|
|
|
89
108
|
**Signature:**
|
|
90
109
|
|
|
91
110
|
```typescript
|
|
92
|
-
async function chacha20Init(
|
|
111
|
+
async function chacha20Init(source: WasmSource): Promise<void>
|
|
93
112
|
```
|
|
94
113
|
|
|
95
114
|
**Usage:**
|
|
96
115
|
|
|
97
116
|
```typescript
|
|
98
117
|
import { chacha20Init, XChaCha20Poly1305 } from 'leviathan-crypto/chacha20'
|
|
118
|
+
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
99
119
|
|
|
100
|
-
await chacha20Init()
|
|
120
|
+
await chacha20Init(chacha20Wasm)
|
|
101
121
|
const aead = new XChaCha20Poly1305()
|
|
102
122
|
```
|
|
103
123
|
|
|
@@ -105,17 +125,17 @@ const aead = new XChaCha20Poly1305()
|
|
|
105
125
|
|
|
106
126
|
## API Reference
|
|
107
127
|
|
|
108
|
-
All classes require calling `await init(
|
|
128
|
+
All classes require calling `await init({ chacha20: chacha20Wasm })` or the subpath `chacha20Init()`
|
|
109
129
|
before construction. If you construct a class before initialization, it throws:
|
|
110
130
|
```
|
|
111
|
-
Error: leviathan-crypto: call init(
|
|
131
|
+
Error: leviathan-crypto: call init({ chacha20: ... }) before using this class
|
|
112
132
|
```
|
|
113
133
|
|
|
114
134
|
---
|
|
115
135
|
|
|
116
136
|
### `ChaCha20`
|
|
117
137
|
|
|
118
|
-
Raw ChaCha20 stream cipher. **No authentication
|
|
138
|
+
Raw ChaCha20 stream cipher. **No authentication.** Use `XChaCha20Poly1305` instead
|
|
119
139
|
unless you are building a custom protocol and understand the risks.
|
|
120
140
|
|
|
121
141
|
#### Constructor
|
|
@@ -124,7 +144,7 @@ unless you are building a custom protocol and understand the risks.
|
|
|
124
144
|
new ChaCha20()
|
|
125
145
|
```
|
|
126
146
|
|
|
127
|
-
Throws if `init(
|
|
147
|
+
Throws if `init({ chacha20: chacha20Wasm })` has not been called.
|
|
128
148
|
|
|
129
149
|
---
|
|
130
150
|
|
|
@@ -156,8 +176,7 @@ a new `Uint8Array` containing the ciphertext (same length as input).
|
|
|
156
176
|
|
|
157
177
|
#### `beginDecrypt(key: Uint8Array, nonce: Uint8Array): void`
|
|
158
178
|
|
|
159
|
-
Prepares the cipher for decryption. Identical to `beginEncrypt
|
|
160
|
-
symmetric (encryption and decryption are the same XOR operation).
|
|
179
|
+
Prepares the cipher for decryption. Identical to `beginEncrypt`. ChaCha20 is symmetric; encryption and decryption are the same XOR operation.
|
|
161
180
|
|
|
162
181
|
| Parameter | Type | Description |
|
|
163
182
|
|-----------|------|-------------|
|
|
@@ -191,7 +210,7 @@ when you are done with the instance.
|
|
|
191
210
|
### `Poly1305`
|
|
192
211
|
|
|
193
212
|
Standalone Poly1305 one-time MAC. **Each key must be used exactly once.** You
|
|
194
|
-
almost certainly want `ChaCha20Poly1305` or `XChaCha20Poly1305` instead
|
|
213
|
+
almost certainly want `ChaCha20Poly1305` or `XChaCha20Poly1305` instead. They
|
|
195
214
|
handle Poly1305 key derivation automatically.
|
|
196
215
|
|
|
197
216
|
#### Constructor
|
|
@@ -200,7 +219,7 @@ handle Poly1305 key derivation automatically.
|
|
|
200
219
|
new Poly1305()
|
|
201
220
|
```
|
|
202
221
|
|
|
203
|
-
Throws if `init(
|
|
222
|
+
Throws if `init({ chacha20: chacha20Wasm })` has not been called.
|
|
204
223
|
|
|
205
224
|
---
|
|
206
225
|
|
|
@@ -210,10 +229,10 @@ Computes a 16-byte Poly1305 authentication tag over the given message.
|
|
|
210
229
|
|
|
211
230
|
| Parameter | Type | Description |
|
|
212
231
|
|-----------|------|-------------|
|
|
213
|
-
| `key` | `Uint8Array` | 32 bytes
|
|
232
|
+
| `key` | `Uint8Array` | 32 bytes. Must be unique per message. |
|
|
214
233
|
| `msg` | `Uint8Array` | The message to authenticate (any length) |
|
|
215
234
|
|
|
216
|
-
**Returns** `Uint8Array
|
|
235
|
+
**Returns** `Uint8Array`: a 16-byte authentication tag.
|
|
217
236
|
|
|
218
237
|
**Throws** `RangeError` if `key` is not 32 bytes.
|
|
219
238
|
|
|
@@ -240,54 +259,59 @@ to avoid collision risk.
|
|
|
240
259
|
new ChaCha20Poly1305()
|
|
241
260
|
```
|
|
242
261
|
|
|
243
|
-
Throws if `init(
|
|
262
|
+
Throws if `init({ chacha20: chacha20Wasm })` has not been called.
|
|
244
263
|
|
|
245
264
|
---
|
|
246
265
|
|
|
247
|
-
#### `encrypt(key, nonce, plaintext, aad?):
|
|
266
|
+
#### `encrypt(key, nonce, plaintext, aad?): Uint8Array`
|
|
267
|
+
|
|
268
|
+
Encrypts plaintext and returns the ciphertext with the 16-byte Poly1305 tag
|
|
269
|
+
appended.
|
|
248
270
|
|
|
249
|
-
|
|
271
|
+
> [!WARNING]
|
|
272
|
+
> Each `ChaCha20Poly1305` instance allows only **one** `encrypt()` call. A second
|
|
273
|
+
> call throws to prevent accidental nonce reuse. Create a new instance for each
|
|
274
|
+
> encryption, or use `Seal` with `XChaCha20Cipher` for automatic nonce management.
|
|
250
275
|
|
|
251
276
|
| Parameter | Type | Default | Description |
|
|
252
277
|
|-----------|------|---------|-------------|
|
|
253
278
|
| `key` | `Uint8Array` | | 32 bytes (256-bit key) |
|
|
254
279
|
| `nonce` | `Uint8Array` | | 12 bytes (96-bit nonce) |
|
|
255
280
|
| `plaintext` | `Uint8Array` | | Data to encrypt (up to the module's chunk size limit) |
|
|
256
|
-
| `aad` | `Uint8Array` | `new Uint8Array(0)` | Associated data
|
|
281
|
+
| `aad` | `Uint8Array` | `new Uint8Array(0)` | Associated data. Authenticated but not encrypted. |
|
|
257
282
|
|
|
258
|
-
**Returns** `
|
|
259
|
-
length as plaintext) and a 16-byte authentication tag. You need both to decrypt.
|
|
283
|
+
**Returns** `Uint8Array`: ciphertext + 16-byte tag (length = plaintext.length + 16).
|
|
260
284
|
|
|
261
285
|
**Throws:**
|
|
262
286
|
- `RangeError` if `key` is not 32 bytes
|
|
263
287
|
- `RangeError` if `nonce` is not 12 bytes
|
|
264
288
|
- `RangeError` if `plaintext` exceeds the maximum chunk size
|
|
289
|
+
- `Error` if `encrypt()` has already been called on this instance
|
|
265
290
|
|
|
266
291
|
---
|
|
267
292
|
|
|
268
|
-
#### `decrypt(key, nonce, ciphertext,
|
|
293
|
+
#### `decrypt(key, nonce, ciphertext, aad?): Uint8Array`
|
|
269
294
|
|
|
270
|
-
Verifies the authentication tag and decrypts the ciphertext.
|
|
271
|
-
|
|
295
|
+
Verifies the authentication tag and decrypts the ciphertext. The `ciphertext`
|
|
296
|
+
parameter must include the appended 16-byte tag (i.e., the exact output of
|
|
297
|
+
`encrypt()`). If authentication fails, an error is thrown and no plaintext is
|
|
298
|
+
returned.
|
|
272
299
|
|
|
273
|
-
Tag comparison uses a constant-time XOR-accumulate pattern
|
|
274
|
-
channel leaks whether the tag was "close" to correct.
|
|
300
|
+
Tag comparison uses a constant-time XOR-accumulate pattern; no timing side channel leaks whether the tag was "close" to correct.
|
|
275
301
|
|
|
276
302
|
| Parameter | Type | Default | Description |
|
|
277
303
|
|-----------|------|---------|-------------|
|
|
278
304
|
| `key` | `Uint8Array` | | 32 bytes (same key used for encryption) |
|
|
279
305
|
| `nonce` | `Uint8Array` | | 12 bytes (same nonce used for encryption) |
|
|
280
|
-
| `ciphertext` | `Uint8Array` | | Encrypted data |
|
|
281
|
-
| `tag` | `Uint8Array` | | 16-byte authentication tag from `encrypt()` |
|
|
306
|
+
| `ciphertext` | `Uint8Array` | | Encrypted data with appended tag (output of `encrypt()`) |
|
|
282
307
|
| `aad` | `Uint8Array` | `new Uint8Array(0)` | Associated data (must match what was passed to `encrypt()`) |
|
|
283
308
|
|
|
284
|
-
**Returns** `Uint8Array
|
|
309
|
+
**Returns** `Uint8Array`: the decrypted plaintext.
|
|
285
310
|
|
|
286
311
|
**Throws:**
|
|
287
312
|
- `RangeError` if `key` is not 32 bytes
|
|
288
313
|
- `RangeError` if `nonce` is not 12 bytes
|
|
289
|
-
- `RangeError` if `
|
|
290
|
-
- `RangeError` if `ciphertext` exceeds the maximum chunk size
|
|
314
|
+
- `RangeError` if `ciphertext` is shorter than 16 bytes (no room for a tag)
|
|
291
315
|
- `Error('ChaCha20Poly1305: authentication failed')` if the tag does not match
|
|
292
316
|
|
|
293
317
|
---
|
|
@@ -300,14 +324,16 @@ Wipes all key material and intermediate state from WASM memory.
|
|
|
300
324
|
|
|
301
325
|
### `XChaCha20Poly1305`
|
|
302
326
|
|
|
303
|
-
XChaCha20-Poly1305 AEAD (draft-irtf-cfrg-xchacha).
|
|
304
|
-
|
|
327
|
+
XChaCha20-Poly1305 AEAD (draft-irtf-cfrg-xchacha). RFC-faithful stateless
|
|
328
|
+
primitive. Key and nonce are passed per-call. Use when protocol interop
|
|
329
|
+
requires explicit nonce control. For most use cases, prefer `Seal` with
|
|
330
|
+
`XChaCha20Cipher` (automatic nonce management, no instantiation).
|
|
305
331
|
|
|
306
332
|
It uses a 24-byte (192-bit) nonce, which is large enough that randomly generated
|
|
307
333
|
nonces will never collide in practice. Internally, it derives a subkey via
|
|
308
334
|
HChaCha20 and delegates to `ChaCha20Poly1305`.
|
|
309
335
|
|
|
310
|
-
|
|
336
|
+
Like `ChaCha20Poly1305`, the `encrypt()` method returns a single `Uint8Array`
|
|
311
337
|
with the tag appended to the ciphertext. The `decrypt()` method expects this
|
|
312
338
|
combined format and splits it internally.
|
|
313
339
|
|
|
@@ -317,7 +343,7 @@ combined format and splits it internally.
|
|
|
317
343
|
new XChaCha20Poly1305()
|
|
318
344
|
```
|
|
319
345
|
|
|
320
|
-
Throws if `init(
|
|
346
|
+
Throws if `init({ chacha20: chacha20Wasm })` has not been called.
|
|
321
347
|
|
|
322
348
|
---
|
|
323
349
|
|
|
@@ -330,9 +356,9 @@ Encrypts plaintext and returns the ciphertext with the 16-byte tag appended.
|
|
|
330
356
|
| `key` | `Uint8Array` | | 32 bytes (256-bit key) |
|
|
331
357
|
| `nonce` | `Uint8Array` | | 24 bytes (192-bit nonce) |
|
|
332
358
|
| `plaintext` | `Uint8Array` | | Data to encrypt |
|
|
333
|
-
| `aad` | `Uint8Array` | `new Uint8Array(0)` | Associated data
|
|
359
|
+
| `aad` | `Uint8Array` | `new Uint8Array(0)` | Associated data. Authenticated but not encrypted. |
|
|
334
360
|
|
|
335
|
-
**Returns** `Uint8Array
|
|
361
|
+
**Returns** `Uint8Array`: ciphertext + 16-byte tag (length = plaintext.length + 16).
|
|
336
362
|
|
|
337
363
|
**Throws:**
|
|
338
364
|
- `RangeError` if `key` is not 32 bytes
|
|
@@ -353,7 +379,7 @@ parameter must include the appended 16-byte tag (i.e., the exact output of
|
|
|
353
379
|
| `ciphertext` | `Uint8Array` | | Encrypted data with appended tag (output of `encrypt()`) |
|
|
354
380
|
| `aad` | `Uint8Array` | `new Uint8Array(0)` | Associated data (must match what was passed to `encrypt()`) |
|
|
355
381
|
|
|
356
|
-
**Returns** `Uint8Array
|
|
382
|
+
**Returns** `Uint8Array`: the decrypted plaintext.
|
|
357
383
|
|
|
358
384
|
**Throws:**
|
|
359
385
|
- `RangeError` if `key` is not 32 bytes
|
|
@@ -369,119 +395,159 @@ Wipes all key material and intermediate state from WASM memory.
|
|
|
369
395
|
|
|
370
396
|
---
|
|
371
397
|
|
|
372
|
-
##
|
|
398
|
+
## XChaCha20Cipher
|
|
373
399
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
number of Web Workers, each holding an isolated `chacha20.wasm` instance in its own
|
|
377
|
-
linear memory. This removes the single-threaded bottleneck of a shared WASM
|
|
378
|
-
instance and allows encryption and decryption operations to proceed in parallel.
|
|
379
|
-
The pool requires `init(['chacha20'])` to be called before construction and is
|
|
380
|
-
created via the static factory `XChaCha20Poly1305Pool.create(opts?)` — see
|
|
381
|
-
[chacha20_pool.md](./chacha20_pool.md) for the full API reference, pool sizing
|
|
382
|
-
guidance, and lifecycle docs.
|
|
400
|
+
`CipherSuite` implementation for XChaCha20-Poly1305. Pass to `Seal`,
|
|
401
|
+
`SealStream`, or `OpenStream`. Never instantiated directly.
|
|
383
402
|
|
|
384
|
-
|
|
403
|
+
Requires `init({ chacha20: chacha20Wasm, sha2: sha2Wasm })`.
|
|
385
404
|
|
|
386
|
-
|
|
405
|
+
> [!NOTE]
|
|
406
|
+
> `sha2` is required by the stream layer for HKDF key derivation, not by
|
|
407
|
+
> `XChaCha20Poly1305` itself.
|
|
408
|
+
|
|
409
|
+
| Property | Value |
|
|
410
|
+
|----------|-------|
|
|
411
|
+
| `formatEnum` | `0x01` |
|
|
412
|
+
| `keySize` | `32` |
|
|
413
|
+
| `tagSize` | `16` (Poly1305) |
|
|
414
|
+
| `padded` | `false` |
|
|
415
|
+
| `wasmModules` | `['chacha20', 'sha2']` |
|
|
387
416
|
|
|
388
|
-
|
|
417
|
+
#### `XChaCha20Cipher.keygen(): Uint8Array`
|
|
389
418
|
|
|
390
|
-
|
|
391
|
-
|
|
419
|
+
Returns `randomBytes(32)`. Convenience method. Not on the `CipherSuite` interface.
|
|
420
|
+
|
|
421
|
+
#### Usage with `Seal`
|
|
392
422
|
|
|
393
423
|
```typescript
|
|
394
|
-
import { init,
|
|
424
|
+
import { init, Seal, XChaCha20Cipher } from 'leviathan-crypto'
|
|
425
|
+
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
426
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
395
427
|
|
|
396
|
-
|
|
397
|
-
await init(['chacha20'])
|
|
428
|
+
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
398
429
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const
|
|
430
|
+
const key = XChaCha20Cipher.keygen()
|
|
431
|
+
const blob = Seal.encrypt(XChaCha20Cipher, key, plaintext)
|
|
432
|
+
const pt = Seal.decrypt(XChaCha20Cipher, key, blob) // throws on tamper
|
|
433
|
+
```
|
|
402
434
|
|
|
403
|
-
|
|
404
|
-
const aead = new XChaCha20Poly1305()
|
|
435
|
+
#### Usage with `SealStream` / `OpenStream`
|
|
405
436
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const sealed = aead.encrypt(key, nonce, plaintext)
|
|
410
|
-
// `sealed` contains the ciphertext + 16-byte authentication tag
|
|
437
|
+
```typescript
|
|
438
|
+
import { SealStream, OpenStream } from 'leviathan-crypto/stream'
|
|
439
|
+
import { XChaCha20Cipher } from 'leviathan-crypto/chacha20'
|
|
411
440
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const
|
|
416
|
-
console.log(bytesToUtf8(decrypted)) // "Hello, world!"
|
|
441
|
+
const sealer = new SealStream(XChaCha20Cipher, key)
|
|
442
|
+
const preamble = sealer.preamble // 20 bytes, send before first chunk
|
|
443
|
+
const ct0 = sealer.push(chunk0)
|
|
444
|
+
const ctLast = sealer.finalize(lastChunk)
|
|
417
445
|
|
|
418
|
-
|
|
419
|
-
|
|
446
|
+
const opener = new OpenStream(XChaCha20Cipher, key, preamble)
|
|
447
|
+
const pt0 = opener.pull(ct0)
|
|
448
|
+
const ptLast = opener.finalize(ctLast)
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
See [aead.md](./aead.md) for the full `Seal`, `SealStream`, and `OpenStream` API.
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Usage Examples
|
|
456
|
+
|
|
457
|
+
### Example 1: Seal with XChaCha20Cipher (recommended)
|
|
458
|
+
|
|
459
|
+
One-shot AEAD. No instantiation, no `dispose()`. The blob format is
|
|
460
|
+
`preamble(20) || ciphertext || tag(16)`.
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
import { init, Seal, XChaCha20Cipher, utf8ToBytes, bytesToUtf8 } from 'leviathan-crypto'
|
|
464
|
+
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
465
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
466
|
+
|
|
467
|
+
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
468
|
+
|
|
469
|
+
const key = XChaCha20Cipher.keygen()
|
|
470
|
+
const blob = Seal.encrypt(XChaCha20Cipher, key, utf8ToBytes('Hello, world!'))
|
|
471
|
+
const pt = Seal.decrypt(XChaCha20Cipher, key, blob) // throws on tamper
|
|
472
|
+
|
|
473
|
+
console.log(bytesToUtf8(pt)) // "Hello, world!"
|
|
474
|
+
|
|
475
|
+
// Optional: bind metadata without encrypting it (AAD)
|
|
476
|
+
const aad = utf8ToBytes('document-v2')
|
|
477
|
+
const blob2 = Seal.encrypt(XChaCha20Cipher, key, utf8ToBytes('Hello, world!'), { aad })
|
|
478
|
+
const pt2 = Seal.decrypt(XChaCha20Cipher, key, blob2, { aad })
|
|
420
479
|
```
|
|
421
480
|
|
|
422
|
-
|
|
481
|
+
> For explicit nonce control, use `XChaCha20Poly1305` directly. Same
|
|
482
|
+
> API shape, 24-byte nonce passed per call. See Example 2.
|
|
483
|
+
|
|
484
|
+
### Example 2: ChaCha20Poly1305
|
|
423
485
|
|
|
424
486
|
Same idea as above, but with a 12-byte nonce. Use this if you are implementing
|
|
425
487
|
a protocol that specifies RFC 8439 ChaCha20-Poly1305 explicitly.
|
|
426
488
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
489
|
+
Both `ChaCha20Poly1305` and `XChaCha20Poly1305` return a single `Uint8Array`
|
|
490
|
+
with the tag appended, and `decrypt()` expects the same combined format.
|
|
491
|
+
The only difference is the nonce size (12 vs 24 bytes).
|
|
492
|
+
|
|
493
|
+
> [!NOTE]
|
|
494
|
+
> Each `ChaCha20Poly1305` instance allows only one `encrypt()` call. A second
|
|
495
|
+
> call throws to prevent accidental nonce reuse. Create a new instance for each
|
|
496
|
+
> encryption.
|
|
431
497
|
|
|
432
498
|
```typescript
|
|
433
499
|
import { init, ChaCha20Poly1305, randomBytes, utf8ToBytes, bytesToUtf8 } from 'leviathan-crypto'
|
|
500
|
+
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
434
501
|
|
|
435
|
-
await init(
|
|
502
|
+
await init({ chacha20: chacha20Wasm })
|
|
436
503
|
|
|
437
504
|
const key = randomBytes(32)
|
|
438
505
|
const aead = new ChaCha20Poly1305()
|
|
439
506
|
|
|
440
507
|
// Encrypt
|
|
441
|
-
const nonce = randomBytes(12) // 12 bytes
|
|
508
|
+
const nonce = randomBytes(12) // 12 bytes, use XChaCha20Poly1305 for high-volume random generation
|
|
442
509
|
const plaintext = utf8ToBytes('Sensitive data')
|
|
443
|
-
const
|
|
444
|
-
//
|
|
510
|
+
const sealed = aead.encrypt(key, nonce, plaintext)
|
|
511
|
+
// sealed = ciphertext || tag(16), store/transmit nonce and sealed together
|
|
445
512
|
|
|
446
|
-
// Decrypt
|
|
447
|
-
const
|
|
513
|
+
// Decrypt (new instance, encrypt is single-use)
|
|
514
|
+
const aead2 = new ChaCha20Poly1305()
|
|
515
|
+
const decrypted = aead2.decrypt(key, nonce, sealed)
|
|
448
516
|
console.log(bytesToUtf8(decrypted)) // "Sensitive data"
|
|
449
517
|
|
|
450
518
|
aead.dispose()
|
|
519
|
+
aead2.dispose()
|
|
451
520
|
```
|
|
452
521
|
|
|
453
522
|
### Example 3: Detecting Tampered Ciphertext
|
|
454
523
|
|
|
455
|
-
AEAD decryption fails loudly if anyone has modified the ciphertext
|
|
456
|
-
the associated data. This is a feature
|
|
457
|
-
or maliciously altered data.
|
|
524
|
+
AEAD decryption fails loudly if anyone has modified the ciphertext or
|
|
525
|
+
the associated data. This is a feature. It prevents you from processing
|
|
526
|
+
corrupted or maliciously altered data.
|
|
458
527
|
|
|
459
528
|
```typescript
|
|
460
|
-
import { init,
|
|
529
|
+
import { init, Seal, XChaCha20Cipher, randomBytes, utf8ToBytes } from 'leviathan-crypto'
|
|
530
|
+
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
531
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
461
532
|
|
|
462
|
-
await init(
|
|
533
|
+
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
463
534
|
|
|
464
|
-
const key
|
|
465
|
-
const
|
|
466
|
-
const aead = new XChaCha20Poly1305()
|
|
467
|
-
|
|
468
|
-
const sealed = aead.encrypt(key, nonce, utf8ToBytes('Original message'))
|
|
535
|
+
const key = XChaCha20Cipher.keygen()
|
|
536
|
+
const sealed = Seal.encrypt(XChaCha20Cipher, key, utf8ToBytes('Original message'))
|
|
469
537
|
|
|
470
538
|
// Simulate tampering: flip one bit in the ciphertext
|
|
471
539
|
const tampered = new Uint8Array(sealed)
|
|
472
|
-
tampered[
|
|
540
|
+
tampered[20] ^= 0x01 // byte 20 is the first ciphertext byte (after the 20-byte preamble)
|
|
473
541
|
|
|
474
542
|
try {
|
|
475
|
-
const plaintext =
|
|
543
|
+
const plaintext = Seal.decrypt(XChaCha20Cipher, key, tampered)
|
|
476
544
|
// This line is never reached
|
|
477
545
|
console.log(plaintext)
|
|
478
546
|
} catch (err) {
|
|
479
547
|
console.error(err.message)
|
|
480
548
|
// "ChaCha20Poly1305: authentication failed"
|
|
481
|
-
// The plaintext is never returned
|
|
549
|
+
// The plaintext is never returned. Decryption stops immediately on failure.
|
|
482
550
|
}
|
|
483
|
-
|
|
484
|
-
aead.dispose()
|
|
485
551
|
```
|
|
486
552
|
|
|
487
553
|
### Example 4: Using Associated Data (AAD)
|
|
@@ -491,72 +557,69 @@ not encrypt. Common uses: user IDs, message sequence numbers, protocol version
|
|
|
491
557
|
headers, routing information.
|
|
492
558
|
|
|
493
559
|
```typescript
|
|
494
|
-
import { init,
|
|
560
|
+
import { init, Seal, XChaCha20Cipher, randomBytes, utf8ToBytes, bytesToUtf8 } from 'leviathan-crypto'
|
|
561
|
+
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
562
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
495
563
|
|
|
496
|
-
await init(
|
|
564
|
+
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
497
565
|
|
|
498
|
-
const key
|
|
499
|
-
const nonce = randomBytes(24)
|
|
500
|
-
const aead = new XChaCha20Poly1305()
|
|
566
|
+
const key = XChaCha20Cipher.keygen()
|
|
501
567
|
|
|
502
568
|
// The user ID travels in the clear, but decryption will fail if anyone changes it
|
|
503
|
-
const userId
|
|
569
|
+
const userId = utf8ToBytes('user-12345')
|
|
504
570
|
const message = utf8ToBytes('Your account balance is $1,000,000')
|
|
505
571
|
|
|
506
|
-
const sealed =
|
|
572
|
+
const sealed = Seal.encrypt(XChaCha20Cipher, key, message, { aad: userId })
|
|
507
573
|
|
|
508
|
-
// Decrypt
|
|
509
|
-
const decrypted =
|
|
574
|
+
// Decrypt, pass the same AAD
|
|
575
|
+
const decrypted = Seal.decrypt(XChaCha20Cipher, key, sealed, { aad: userId })
|
|
510
576
|
console.log(bytesToUtf8(decrypted))
|
|
511
577
|
// "Your account balance is $1,000,000"
|
|
512
578
|
|
|
513
579
|
// If someone changes the AAD, decryption fails
|
|
514
580
|
const wrongUserId = utf8ToBytes('user-99999')
|
|
515
581
|
try {
|
|
516
|
-
|
|
582
|
+
Seal.decrypt(XChaCha20Cipher, key, sealed, { aad: wrongUserId })
|
|
517
583
|
} catch (err) {
|
|
518
584
|
console.error(err.message)
|
|
519
585
|
// "ChaCha20Poly1305: authentication failed"
|
|
520
586
|
// Even though the ciphertext was not modified, the AAD mismatch is detected.
|
|
521
587
|
}
|
|
522
|
-
|
|
523
|
-
aead.dispose()
|
|
524
588
|
```
|
|
525
589
|
|
|
526
590
|
### Example 5: Encrypting and Decrypting Binary Data
|
|
527
591
|
|
|
528
|
-
The API works with raw bytes
|
|
592
|
+
The API works with raw bytes, not just text. Here is an example encrypting
|
|
529
593
|
arbitrary binary content.
|
|
530
594
|
|
|
531
595
|
```typescript
|
|
532
|
-
import { init,
|
|
596
|
+
import { init, Seal, XChaCha20Cipher, randomBytes } from 'leviathan-crypto'
|
|
597
|
+
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
598
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
533
599
|
|
|
534
|
-
await init(
|
|
600
|
+
await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
|
|
535
601
|
|
|
536
|
-
const key =
|
|
537
|
-
const nonce = randomBytes(24)
|
|
538
|
-
const aead = new XChaCha20Poly1305()
|
|
602
|
+
const key = XChaCha20Cipher.keygen()
|
|
539
603
|
|
|
540
604
|
// Encrypt binary data (e.g., an image thumbnail, a protobuf, a file chunk)
|
|
541
605
|
const binaryData = new Uint8Array([0x89, 0x50, 0x4e, 0x47, /* ...more bytes... */])
|
|
542
|
-
const sealed =
|
|
606
|
+
const sealed = Seal.encrypt(XChaCha20Cipher, key, binaryData)
|
|
543
607
|
|
|
544
608
|
// Decrypt
|
|
545
|
-
const recovered =
|
|
609
|
+
const recovered = Seal.decrypt(XChaCha20Cipher, key, sealed)
|
|
546
610
|
// `recovered` is byte-identical to `binaryData`
|
|
547
|
-
|
|
548
|
-
aead.dispose()
|
|
549
611
|
```
|
|
550
612
|
|
|
551
613
|
### Example 6: Raw ChaCha20 Stream Cipher (Advanced)
|
|
552
614
|
|
|
553
615
|
Use this only if you are building a custom protocol and will add your own
|
|
554
|
-
authentication layer. For almost all use cases, use `
|
|
616
|
+
authentication layer. For almost all use cases, use `Seal` with `XChaCha20Cipher` instead.
|
|
555
617
|
|
|
556
618
|
```typescript
|
|
557
619
|
import { init, ChaCha20, randomBytes } from 'leviathan-crypto'
|
|
620
|
+
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
558
621
|
|
|
559
|
-
await init(
|
|
622
|
+
await init({ chacha20: chacha20Wasm })
|
|
560
623
|
|
|
561
624
|
const key = randomBytes(32)
|
|
562
625
|
const nonce = randomBytes(12)
|
|
@@ -567,7 +630,7 @@ cipher.beginEncrypt(key, nonce)
|
|
|
567
630
|
const ct1 = cipher.encryptChunk(new Uint8Array([1, 2, 3, 4]))
|
|
568
631
|
const ct2 = cipher.encryptChunk(new Uint8Array([5, 6, 7, 8]))
|
|
569
632
|
|
|
570
|
-
// Decrypt
|
|
633
|
+
// Decrypt, uses the same key and nonce
|
|
571
634
|
cipher.beginDecrypt(key, nonce)
|
|
572
635
|
const pt1 = cipher.decryptChunk(ct1)
|
|
573
636
|
const pt2 = cipher.decryptChunk(ct2)
|
|
@@ -580,27 +643,32 @@ const pt2 = cipher.decryptChunk(ct2)
|
|
|
580
643
|
cipher.dispose()
|
|
581
644
|
```
|
|
582
645
|
|
|
646
|
+
---
|
|
647
|
+
|
|
583
648
|
## Error Conditions
|
|
584
649
|
|
|
585
650
|
| Condition | Error Type | Message |
|
|
586
651
|
|-----------|-----------|---------|
|
|
587
|
-
| `init(
|
|
652
|
+
| `init({ chacha20: ... })` not called before constructing a class | `Error` | `leviathan-crypto: call init({ chacha20: ... }) before using this class` |
|
|
588
653
|
| Key is not 32 bytes | `RangeError` | `ChaCha20 key must be 32 bytes (got N)` / `key must be 32 bytes (got N)` / `Poly1305 key must be 32 bytes (got N)` |
|
|
589
654
|
| `ChaCha20` nonce is not 12 bytes | `RangeError` | `ChaCha20 nonce must be 12 bytes (got N)` |
|
|
590
655
|
| `ChaCha20Poly1305` nonce is not 12 bytes | `RangeError` | `nonce must be 12 bytes (got N)` |
|
|
591
656
|
| `XChaCha20Poly1305` nonce is not 24 bytes | `RangeError` | `XChaCha20 nonce must be 24 bytes (got N)` |
|
|
592
|
-
| `ChaCha20Poly1305`
|
|
657
|
+
| `ChaCha20Poly1305` ciphertext shorter than 16 bytes | `RangeError` | `ciphertext too short — must include 16-byte tag (got N)` |
|
|
593
658
|
| `XChaCha20Poly1305` ciphertext shorter than 16 bytes | `RangeError` | `ciphertext too short — must include 16-byte tag (got N)` |
|
|
659
|
+
| `ChaCha20Poly1305.encrypt()` called a second time | `Error` | Single-use encrypt guard. Create a new instance for each encryption. |
|
|
594
660
|
| Chunk or plaintext exceeds WASM buffer size | `RangeError` | `plaintext exceeds N bytes — split into smaller chunks` / `chunk exceeds maximum size of N bytes — split into smaller chunks` |
|
|
595
661
|
| Authentication tag does not match on decrypt | `Error` | `ChaCha20Poly1305: authentication failed` |
|
|
596
|
-
| Empty plaintext |
|
|
662
|
+
| Empty plaintext | | Allowed. Encrypting zero bytes produces just a 16-byte tag (AEAD) or zero bytes (raw ChaCha20). |
|
|
597
663
|
|
|
598
664
|
> ## Cross-References
|
|
599
665
|
>
|
|
600
666
|
> - [index](./README.md) — Project Documentation index
|
|
667
|
+
> - [lexicon](./lexicon.md) — Glossary of cryptographic terms
|
|
601
668
|
> - [asm_chacha](./asm_chacha.md) — WASM (AssemblyScript) implementation details for the chacha20 module
|
|
602
|
-
> - [
|
|
603
|
-
> - [serpent](./serpent.md) — alternative
|
|
604
|
-
> - [sha2](./sha2.md) — SHA-2 hashes and HMAC
|
|
669
|
+
> - [authenticated encryption](./aead.md) — `Seal`, `SealStream`, `OpenStream`: use `XChaCha20Cipher` as the suite argument
|
|
670
|
+
> - [serpent](./serpent.md) — `SerpentCipher`: alternative `CipherSuite` for `Seal` and streaming
|
|
671
|
+
> - [sha2](./sha2.md) — SHA-2 hashes and HMAC. Needed for Encrypt-then-MAC if using raw ChaCha20
|
|
605
672
|
> - [types](./types.md) — `AEAD` and `Streamcipher` interfaces implemented by ChaCha20 classes
|
|
606
|
-
> - [
|
|
673
|
+
> - [architecture](./architecture.md) — architecture overview, module relationships, buffer layouts, and build pipeline
|
|
674
|
+
> - [chacha_audit](./chacha_audit.md) — XChaCha20-Poly1305 implementation audit
|