leviathan-crypto 1.3.1 → 1.4.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 +22 -4
- package/README.md +20 -18
- package/SECURITY.md +21 -9
- package/dist/chacha20/index.d.ts +20 -0
- package/dist/chacha20/index.js +48 -2
- package/dist/chacha20/pool.js +3 -13
- package/dist/chacha20/stream-sealer.d.ts +49 -0
- package/dist/chacha20/stream-sealer.js +327 -0
- package/dist/docs/chacha20.md +195 -53
- package/dist/docs/fortuna.md +6 -6
- package/dist/docs/loader.md +23 -13
- package/dist/docs/serpent.md +27 -9
- package/dist/docs/types.md +1 -1
- package/dist/docs/utils.md +8 -12
- package/dist/embedded/chacha20.d.ts +1 -1
- package/dist/embedded/chacha20.js +2 -1
- 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/fortuna.d.ts +2 -2
- package/dist/fortuna.js +6 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/loader.d.ts +6 -0
- package/dist/loader.js +31 -9
- package/dist/serpent/index.js +1 -1
- package/dist/serpent/seal.d.ts +2 -2
- package/dist/serpent/seal.js +7 -5
- package/dist/serpent/stream-pool.js +5 -15
- package/dist/serpent/stream-sealer.d.ts +5 -0
- package/dist/serpent/stream-sealer.js +25 -24
- package/dist/sha2/index.js +1 -1
- package/dist/sha3/index.js +1 -1
- package/dist/utils.d.ts +5 -5
- package/dist/utils.js +26 -16
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -88,7 +88,7 @@ await serpentInit()
|
|
|
88
88
|
| Classes | `init()` call |
|
|
89
89
|
|---------|--------------|
|
|
90
90
|
| `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, `Serpent`, `SerpentCtr`, `SerpentCbc` | `init(['serpent', 'sha2'])` |
|
|
91
|
-
| `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Poly1305Pool` | `init(['chacha20'])` |
|
|
91
|
+
| `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Seal`, `XChaCha20Poly1305Pool` | `init(['chacha20'])` |
|
|
92
92
|
| `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512`, `HKDF_SHA256`, `HKDF_SHA512` | `init(['sha2'])` |
|
|
93
93
|
| `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` | `init(['sha3'])` |
|
|
94
94
|
| `Fortuna` | `init(['serpent', 'sha2'])` |
|
|
@@ -106,8 +106,9 @@ await init(['serpent', 'sha2'])
|
|
|
106
106
|
|
|
107
107
|
const key = randomBytes(64) // 64-byte key (encKey + macKey)
|
|
108
108
|
const seal = new SerpentSeal()
|
|
109
|
-
const ciphertext = seal.encrypt(key, plaintext)
|
|
110
|
-
const decrypted = seal.decrypt(key, ciphertext)
|
|
109
|
+
const ciphertext = seal.encrypt(key, plaintext) // Serpent-CBC + HMAC-SHA256
|
|
110
|
+
const decrypted = seal.decrypt(key, ciphertext) // throws on tamper
|
|
111
|
+
// Optional AAD: seal.encrypt(key, plaintext, aad) / seal.decrypt(key, ciphertext, aad)
|
|
111
112
|
seal.dispose()
|
|
112
113
|
```
|
|
113
114
|
|
|
@@ -154,6 +155,23 @@ const opener = new SerpentStreamOpener(key, header, { framed: true })
|
|
|
154
155
|
const chunks = opener.feed(frame0) // Uint8Array[] — throws on auth failure
|
|
155
156
|
```
|
|
156
157
|
|
|
158
|
+
### XChaCha20Seal (recommended)
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { init, XChaCha20Seal, randomBytes } from 'leviathan-crypto'
|
|
162
|
+
|
|
163
|
+
await init(['chacha20'])
|
|
164
|
+
|
|
165
|
+
const seal = new XChaCha20Seal(randomBytes(32)) // 32-byte key
|
|
166
|
+
const ct = seal.encrypt(plaintext) // nonce(24) || ct || tag(16)
|
|
167
|
+
const pt = seal.decrypt(ct) // throws on tamper
|
|
168
|
+
seal.dispose()
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Binds key at construction, generates a fresh nonce per `encrypt()` call. No nonce
|
|
172
|
+
management needed. For protocol interop requiring explicit nonces, use
|
|
173
|
+
`XChaCha20Poly1305` directly.
|
|
174
|
+
|
|
157
175
|
### XChaCha20-Poly1305
|
|
158
176
|
|
|
159
177
|
```typescript
|
|
@@ -257,7 +275,7 @@ The complete API reference ships in `docs/` alongside this file:
|
|
|
257
275
|
| File | Contents |
|
|
258
276
|
|------|----------|
|
|
259
277
|
| `docs/serpent.md` | `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, `Serpent`, `SerpentCtr`, `SerpentCbc` |
|
|
260
|
-
| `docs/chacha20.md` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Poly1305Pool` |
|
|
278
|
+
| `docs/chacha20.md` | `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Seal`, `XChaCha20Poly1305Pool` |
|
|
261
279
|
| `docs/sha2.md` | `SHA256`, `SHA384`, `SHA512`, `HMAC_SHA256`, `HMAC_SHA384`, `HMAC_SHA512`, `HKDF_SHA256`, `HKDF_SHA512` |
|
|
262
280
|
| `docs/sha3.md` | `SHA3_224`, `SHA3_256`, `SHA3_384`, `SHA3_512`, `SHAKE128`, `SHAKE256` |
|
|
263
281
|
| `docs/fortuna.md` | `Fortuna` CSPRNG |
|
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ npm install leviathan-crypto
|
|
|
85
85
|
|
|
86
86
|
## Demos
|
|
87
87
|
|
|
88
|
-
**`lvthn-web`** [ [demo](https://leviathan.3xi.club/web) · [source](https://github.com/xero/leviathan-demos/tree/main/
|
|
88
|
+
**`lvthn-web`** [ [demo](https://leviathan.3xi.club/web) · [source](https://github.com/xero/leviathan-demos/tree/main/web) · [readme](https://github.com/xero/leviathan-demos/blob/main/web/README.md) ]
|
|
89
89
|
|
|
90
90
|
A browser encryption tool in a single, self-contained HTML file. Encrypt text
|
|
91
91
|
or files using Serpent-256-CBC and Argon2id key derivation, then share the
|
|
@@ -94,7 +94,7 @@ initial load. The code is written to be read. The Encrypt-then-MAC
|
|
|
94
94
|
construction, HMAC input (header with HMAC field zeroed + ciphertext), and
|
|
95
95
|
Argon2id parameters are all intentional examples worth reading.
|
|
96
96
|
|
|
97
|
-
**`lvthn-chat`** [ [demo](https://leviathan.3xi.club/chat) · [source](https://github.com/xero/leviathan-demos/tree/main/
|
|
97
|
+
**`lvthn-chat`** [ [demo](https://leviathan.3xi.club/chat) · [source](https://github.com/xero/leviathan-demos/tree/main/chat) · [readme](https://github.com/xero/leviathan-demos/blob/main/chat/README.md) ]
|
|
98
98
|
|
|
99
99
|
End-to-end encrypted chat featuring two-party messaging over X25519 key
|
|
100
100
|
exchange and XChaCha20-Poly1305 message encryption. The relay server functions
|
|
@@ -126,17 +126,20 @@ cat secret.txt | lvthn encrypt -k my.key --armor > secret.enc
|
|
|
126
126
|
| Class | Module | Auth | Notes |
|
|
127
127
|
| ----------------------------------------------------------- | ----------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
128
128
|
| **Authenticated encryption** | | | |
|
|
129
|
-
| `SerpentSeal` | `serpent`, `sha2` | **Yes** | Serpent-CBC + HMAC-SHA256. Recommended default
|
|
129
|
+
| `SerpentSeal` | `serpent`, `sha2` | **Yes** | Serpent-CBC + HMAC-SHA256. Recommended Serpent default. 64-byte key. |
|
|
130
|
+
| `XChaCha20Seal` | `chacha20` | **Yes** | XChaCha20-Poly1305 with internal nonce management. Recommended ChaCha20 default. 32-byte key. |
|
|
130
131
|
| `SerpentStream`, `SerpentStreamPool` | `serpent`, `sha2` | **Yes** | Chunked one-shot AEAD for large payloads. Pool variant parallelises across workers. 32-byte key. |
|
|
131
132
|
| `SerpentStreamSealer`, `SerpentStreamOpener` | `serpent`, `sha2` | **Yes** | Incremental streaming AEAD: seal/open one chunk at a time. Pass `{ framed: true }` for self-delimiting `u32be` length-prefix framing. 64-byte key. |
|
|
132
|
-
| `
|
|
133
|
+
| `XChaCha20StreamSealer`, `XChaCha20StreamOpener` | `chacha20` | **Yes** | Incremental streaming AEAD using XChaCha20-Poly1305. Per-chunk random nonces, position-bound AAD. `{ framed: true }` for length-prefixed framing. 32-byte key. |
|
|
133
134
|
| `XChaCha20Poly1305Pool` | `chacha20` | **Yes** | Worker-pool wrapper for `XChaCha20Poly1305`. Parallelises encryption across isolated WASM instances. |
|
|
134
|
-
|
|
|
135
|
+
| **Stateless primitives** _caller manages nonces_ | | | |
|
|
136
|
+
| `XChaCha20Poly1305` | `chacha20` | **Yes** | RFC-faithful stateless AEAD. 24-byte nonce, caller-managed. Use `XChaCha20Seal` unless you need explicit nonce control. |
|
|
137
|
+
| `ChaCha20Poly1305` | `chacha20` | **Yes** | RFC 8439 stateless AEAD. 12-byte nonce, caller-managed. Prefer `XChaCha20Seal` unless you need RFC 8439 exact compliance. |
|
|
135
138
|
| **Unauthenticated primitives** _pair with HMAC or use AEAD_ | | | |
|
|
136
139
|
| `Serpent` | `serpent` | **No** | Serpent-256 ECB block cipher. Single-block encrypt/decrypt. |
|
|
137
140
|
| `SerpentCtr` | `serpent` | **No** | Serpent-256 CTR mode stream cipher. Requires `{ dangerUnauthenticated: true }`. |
|
|
138
141
|
| `SerpentCbc` | `serpent` | **No** | Serpent-256 CBC mode with PKCS7 padding. Requires `{ dangerUnauthenticated: true }`. |
|
|
139
|
-
| `ChaCha20` | `chacha20` | **No** | ChaCha20 stream cipher — RFC 8439. Unauthenticated; use `
|
|
142
|
+
| `ChaCha20` | `chacha20` | **No** | ChaCha20 stream cipher — RFC 8439. Unauthenticated; use `XChaCha20Seal` unless you need raw keystream. |
|
|
140
143
|
| `Poly1305` | `chacha20` | **No** | Poly1305 one-time MAC — RFC 8439. Use via the AEAD classes unless you have a specific reason not to. |
|
|
141
144
|
| **Hashing and key derivation** | | | |
|
|
142
145
|
| `SHA256`, `SHA384`, `SHA512` | `sha2` | — | SHA-2 family — FIPS 180-4. |
|
|
@@ -174,25 +177,24 @@ const decrypted = seal.decrypt(key, ciphertext)
|
|
|
174
177
|
seal.dispose()
|
|
175
178
|
```
|
|
176
179
|
|
|
177
|
-
### Authenticated encryption with XChaCha20
|
|
180
|
+
### Authenticated encryption with XChaCha20
|
|
178
181
|
|
|
179
182
|
```typescript
|
|
180
|
-
import { init,
|
|
183
|
+
import { init, XChaCha20Seal, randomBytes } from 'leviathan-crypto'
|
|
181
184
|
|
|
182
185
|
await init(['chacha20'])
|
|
183
186
|
|
|
184
|
-
const key
|
|
185
|
-
const nonce = randomBytes(24) // 24-byte nonce (XChaCha20 extended nonce)
|
|
187
|
+
const key = randomBytes(32) // 32-byte key
|
|
186
188
|
|
|
187
|
-
const
|
|
189
|
+
const seal = new XChaCha20Seal(key)
|
|
188
190
|
|
|
189
|
-
// Encrypt and authenticate
|
|
190
|
-
const ciphertext =
|
|
191
|
+
// Encrypt and authenticate (nonce generated internally)
|
|
192
|
+
const ciphertext = seal.encrypt(plaintext)
|
|
191
193
|
|
|
192
194
|
// Decrypt and verify (throws on tamper)
|
|
193
|
-
const decrypted =
|
|
195
|
+
const decrypted = seal.decrypt(ciphertext)
|
|
194
196
|
|
|
195
|
-
|
|
197
|
+
seal.dispose()
|
|
196
198
|
```
|
|
197
199
|
|
|
198
200
|
For more examples, including streaming, chunking, hashing, and key derivation,
|
|
@@ -224,7 +226,7 @@ import { serpentInit, SerpentSeal } from 'leviathan-crypto/serpent'
|
|
|
224
226
|
await serpentInit()
|
|
225
227
|
|
|
226
228
|
// Only chacha20.wasm ends up in your bundle
|
|
227
|
-
import { chacha20Init,
|
|
229
|
+
import { chacha20Init, XChaCha20Seal } from 'leviathan-crypto/chacha20'
|
|
228
230
|
await chacha20Init()
|
|
229
231
|
```
|
|
230
232
|
|
|
@@ -252,7 +254,7 @@ await chacha20Init()
|
|
|
252
254
|
| ----------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
253
255
|
| serpent | [▼](./docs/serpent.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/serpent) | Serpent-256 TypeScript API (`SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `SerpentStreamSealer`, `SerpentStreamOpener`, `Serpent`, `SerpentCtr`, `SerpentCbc`) |
|
|
254
256
|
| asm_serpent | [▼](./docs/asm_serpent.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/asm_serpent) | Serpent-256 WASM implementation (bitslice S-boxes, key schedule, CTR/CBC) |
|
|
255
|
-
| chacha20 | [▼](./docs/chacha20.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/chacha20) | ChaCha20/Poly1305 TypeScript API (`ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Poly1305Pool`)
|
|
257
|
+
| chacha20 | [▼](./docs/chacha20.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/chacha20) | ChaCha20/Poly1305 TypeScript API (`XChaCha20Seal`, `XChaCha20StreamSealer`, `XChaCha20StreamOpener`, `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `XChaCha20Poly1305Pool`) |
|
|
256
258
|
| asm_chacha | [▼](./docs/asm_chacha.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/asm_chacha) | ChaCha20/Poly1305 WASM implementation (quarter-round, HChaCha20) |
|
|
257
259
|
| sha2 | [▼](./docs/sha2.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/sha2) | SHA-2 TypeScript API (`SHA256`, `SHA512`, `SHA384`, `HMAC_SHA256`, `HMAC_SHA512`, `HMAC_SHA384`) |
|
|
258
260
|
| asm_sha2 | [▼](./docs/asm_sha2.md) · [¶](https://github.com/xero/leviathan-crypto/wiki/asm_sha2) | SHA-2 WASM implementation (compression functions, HMAC) |
|
|
@@ -278,7 +280,7 @@ These helpers are available immediately on import with no `init()` required.
|
|
|
278
280
|
| `constantTimeEqual(a, b)` | [▼](./docs/utils.md#constanttimeequal) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#constanttimeequal) | Constant-time byte comparison (XOR-accumulate) |
|
|
279
281
|
| `wipe(data)` | [▼](./docs/utils.md#wipe) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#wipe) | Zero a typed array in place |
|
|
280
282
|
| `xor(a, b)` | [▼](./docs/utils.md#xor) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#xor) | XOR two equal-length `Uint8Array`s |
|
|
281
|
-
| `concat(
|
|
283
|
+
| `concat(...arrays)` | [▼](./docs/utils.md#concat) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#concat) | Concatenate `Uint8Array`s (variadic) |
|
|
282
284
|
| `hasSIMD()` | [▼](./docs/utils.md#hassimd) · [¶](https://github.com/xero/leviathan-crypto/wiki/utils#hassimd) | Detects WebAssembly SIMD support. Cached after first call. Used internally for CTR/CBC/ChaCha20 dispatch. |
|
|
283
285
|
|
|
284
286
|
### Algorithm correctness and verifications
|
package/SECURITY.md
CHANGED
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
| Version | Supported |
|
|
15
15
|
|---------|-----------|
|
|
16
|
-
| v1.
|
|
17
|
-
| v1.
|
|
16
|
+
| v1.4.x | ✓ |
|
|
17
|
+
| v1.3.x | ✓ |
|
|
18
|
+
| v1.2.x | ✗ |
|
|
18
19
|
| v1.1.x | ✗ |
|
|
19
20
|
| v1.0.x | ✗ |
|
|
20
21
|
|
|
@@ -92,18 +93,29 @@ See: [`xero/BicliqueFinder/biclique_research.md`][biclique]
|
|
|
92
93
|
|
|
93
94
|
### Authenticated Encryption by Default
|
|
94
95
|
|
|
95
|
-
Raw unauthenticated cipher modes (`SerpentCbc`, `SerpentCtr`)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
Raw unauthenticated cipher modes (`SerpentCbc`, `SerpentCtr`, `ChaCha20`) and
|
|
97
|
+
stateless caller-managed-nonce primitives (`ChaCha20Poly1305`,
|
|
98
|
+
`XChaCha20Poly1305`) are exposed for power users but are not the recommended
|
|
99
|
+
entry point. The primary API surfaces — `SerpentSeal`, `SerpentStream`,
|
|
100
|
+
`SerpentStreamSealer`, `XChaCha20Seal`, and `XChaCha20StreamSealer` — are
|
|
101
|
+
authenticated by construction with internally managed nonces.
|
|
99
102
|
|
|
100
|
-
|
|
103
|
+
**Both streaming constructions satisfy the _Cryptographic Doom Principle_:**
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
`SerpentStreamSealer` uses encrypt-then-MAC (SerpentCbc + HMAC-SHA256).
|
|
106
|
+
MAC verification is the unconditional gate on the open path.
|
|
107
|
+
Decryption is unreachable until that gate clears. Per-chunk
|
|
104
108
|
HKDF key derivation with position-bound info extends this
|
|
105
109
|
guarantee to full stream integrity.
|
|
106
110
|
|
|
111
|
+
`XChaCha20StreamSealer` uses XChaCha20-Poly1305 AEAD per chunk.
|
|
112
|
+
The Poly1305 tag is verified inside the WASM `xcDecrypt` call
|
|
113
|
+
before any plaintext is produced. On authentication failure,
|
|
114
|
+
plaintext bytes are never generated and never returned. Stream-level
|
|
115
|
+
binding via per-chunk AAD (`stream_id || index || isLast || user AAD`)
|
|
116
|
+
ensures reorder, splice, truncation, and cross-stream substitution
|
|
117
|
+
all fail AEAD verification before decryption.
|
|
118
|
+
|
|
107
119
|
### Dependency Management
|
|
108
120
|
|
|
109
121
|
The library has **zero** runtime dependencies by design.
|
package/dist/chacha20/index.d.ts
CHANGED
|
@@ -46,4 +46,24 @@ export declare class XChaCha20Poly1305 {
|
|
|
46
46
|
decrypt(key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, aad?: Uint8Array): Uint8Array;
|
|
47
47
|
dispose(): void;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* XChaCha20-Poly1305 AEAD with bound key and automatic nonce management.
|
|
51
|
+
* Implements the AEAD interface — encrypt()/decrypt() require only plaintext
|
|
52
|
+
* and optional AAD. Each encrypt() call generates a fresh 24-byte random nonce.
|
|
53
|
+
*
|
|
54
|
+
* Wire format: nonce(24) || ciphertext || tag(16)
|
|
55
|
+
*
|
|
56
|
+
* Use this when you want the simplest correct API and do not need to manage
|
|
57
|
+
* nonces yourself. For protocol interop requiring explicit nonce control,
|
|
58
|
+
* use XChaCha20Poly1305 directly.
|
|
59
|
+
*/
|
|
60
|
+
export declare class XChaCha20Seal {
|
|
61
|
+
private readonly _x;
|
|
62
|
+
private readonly _key;
|
|
63
|
+
constructor(key: Uint8Array);
|
|
64
|
+
encrypt(plaintext: Uint8Array, aad?: Uint8Array, _nonce?: Uint8Array): Uint8Array;
|
|
65
|
+
decrypt(ciphertext: Uint8Array, aad?: Uint8Array): Uint8Array;
|
|
66
|
+
dispose(): void;
|
|
67
|
+
}
|
|
68
|
+
export { XChaCha20StreamSealer, XChaCha20StreamOpener } from './stream-sealer.js';
|
|
49
69
|
export declare function _chachaReady(): boolean;
|
package/dist/chacha20/index.js
CHANGED
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
// Uses the init() module cache — call init('chacha20') before constructing.
|
|
26
26
|
import { getInstance, initModule } from '../init.js';
|
|
27
27
|
import { aeadEncrypt, aeadDecrypt, xcEncrypt, xcDecrypt } from './ops.js';
|
|
28
|
-
import { hasSIMD } from '../utils.js';
|
|
29
|
-
const _embedded = () => import('../embedded/chacha20.js').then(m => m.
|
|
28
|
+
import { hasSIMD, randomBytes, wipe } from '../utils.js';
|
|
29
|
+
const _embedded = () => import('../embedded/chacha20.js').then(m => m.WASM_GZ_BASE64);
|
|
30
30
|
export async function chacha20Init(mode = 'embedded', opts) {
|
|
31
31
|
return initModule('chacha20', _embedded, mode, opts);
|
|
32
32
|
}
|
|
@@ -167,6 +167,52 @@ export class XChaCha20Poly1305 {
|
|
|
167
167
|
this.x.wipeBuffers();
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
+
// ── XChaCha20Seal ────────────────────────────────────────────────────────────
|
|
171
|
+
/**
|
|
172
|
+
* XChaCha20-Poly1305 AEAD with bound key and automatic nonce management.
|
|
173
|
+
* Implements the AEAD interface — encrypt()/decrypt() require only plaintext
|
|
174
|
+
* and optional AAD. Each encrypt() call generates a fresh 24-byte random nonce.
|
|
175
|
+
*
|
|
176
|
+
* Wire format: nonce(24) || ciphertext || tag(16)
|
|
177
|
+
*
|
|
178
|
+
* Use this when you want the simplest correct API and do not need to manage
|
|
179
|
+
* nonces yourself. For protocol interop requiring explicit nonce control,
|
|
180
|
+
* use XChaCha20Poly1305 directly.
|
|
181
|
+
*/
|
|
182
|
+
export class XChaCha20Seal {
|
|
183
|
+
_x;
|
|
184
|
+
_key;
|
|
185
|
+
constructor(key) {
|
|
186
|
+
if (!_chachaReady())
|
|
187
|
+
throw new Error('leviathan-crypto: call init([\'chacha20\']) before using XChaCha20Seal');
|
|
188
|
+
if (key.length !== 32)
|
|
189
|
+
throw new RangeError(`XChaCha20Seal key must be 32 bytes (got ${key.length})`);
|
|
190
|
+
this._x = getExports();
|
|
191
|
+
this._key = key.slice();
|
|
192
|
+
}
|
|
193
|
+
// _nonce: test seam only — inject a fixed nonce for deterministic KAT vectors
|
|
194
|
+
encrypt(plaintext, aad = new Uint8Array(0), _nonce) {
|
|
195
|
+
const nonce = (_nonce && _nonce.length === 24) ? _nonce : randomBytes(24);
|
|
196
|
+
const sealed = xcEncrypt(this._x, this._key, nonce, plaintext, aad);
|
|
197
|
+
// Prepend nonce to sealed output (ciphertext || tag)
|
|
198
|
+
const out = new Uint8Array(24 + sealed.length);
|
|
199
|
+
out.set(nonce, 0);
|
|
200
|
+
out.set(sealed, 24);
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
decrypt(ciphertext, aad = new Uint8Array(0)) {
|
|
204
|
+
if (ciphertext.length < 40)
|
|
205
|
+
throw new RangeError(`XChaCha20Seal ciphertext too short — need nonce(24)+tag(16)=40 bytes minimum (got ${ciphertext.length})`);
|
|
206
|
+
const nonce = ciphertext.subarray(0, 24);
|
|
207
|
+
const payload = ciphertext.subarray(24);
|
|
208
|
+
return xcDecrypt(this._x, this._key, nonce, payload, aad);
|
|
209
|
+
}
|
|
210
|
+
dispose() {
|
|
211
|
+
wipe(this._key);
|
|
212
|
+
this._x.wipeBuffers();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export { XChaCha20StreamSealer, XChaCha20StreamOpener } from './stream-sealer.js';
|
|
170
216
|
// ── Ready check ──────────────────────────────────────────────────────────────
|
|
171
217
|
export function _chachaReady() {
|
|
172
218
|
try {
|
package/dist/chacha20/pool.js
CHANGED
|
@@ -4,24 +4,14 @@
|
|
|
4
4
|
// Dispatches independent encrypt/decrypt jobs across Web Workers, each with
|
|
5
5
|
// its own WebAssembly.Instance and isolated linear memory.
|
|
6
6
|
import { isInitialized } from '../init.js';
|
|
7
|
-
|
|
8
|
-
function base64ToBytes(b64) {
|
|
9
|
-
if (typeof atob === 'function') {
|
|
10
|
-
const raw = atob(b64);
|
|
11
|
-
const out = new Uint8Array(raw.length);
|
|
12
|
-
for (let i = 0; i < raw.length; i++)
|
|
13
|
-
out[i] = raw.charCodeAt(i);
|
|
14
|
-
return out;
|
|
15
|
-
}
|
|
16
|
-
return new Uint8Array(Buffer.from(b64, 'base64'));
|
|
17
|
-
}
|
|
7
|
+
import { decodeWasm } from '../loader.js';
|
|
18
8
|
// ── WASM module singleton ────────────────────────────────────────────────────
|
|
19
9
|
let _wasmModule;
|
|
20
10
|
async function getWasmModule() {
|
|
21
11
|
if (_wasmModule)
|
|
22
12
|
return _wasmModule;
|
|
23
|
-
const {
|
|
24
|
-
const bytes =
|
|
13
|
+
const { WASM_GZ_BASE64 } = await import('../embedded/chacha20.js');
|
|
14
|
+
const bytes = await decodeWasm(WASM_GZ_BASE64);
|
|
25
15
|
_wasmModule = await WebAssembly.compile(bytes.buffer);
|
|
26
16
|
return _wasmModule;
|
|
27
17
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare class XChaCha20StreamSealer {
|
|
2
|
+
private readonly _x;
|
|
3
|
+
private readonly _key;
|
|
4
|
+
private readonly _cs;
|
|
5
|
+
private readonly _id;
|
|
6
|
+
private readonly _framed;
|
|
7
|
+
private readonly _aad;
|
|
8
|
+
private _index;
|
|
9
|
+
private _state;
|
|
10
|
+
/** Public: consumers use this 3-param form. */
|
|
11
|
+
constructor(key: Uint8Array, chunkSize?: number, opts?: {
|
|
12
|
+
framed?: boolean;
|
|
13
|
+
aad?: Uint8Array;
|
|
14
|
+
});
|
|
15
|
+
/** @internal Test-only overload to inject fixed stream_id for deterministic output. */
|
|
16
|
+
constructor(key: Uint8Array, chunkSize: number | undefined, opts: {
|
|
17
|
+
framed?: boolean;
|
|
18
|
+
aad?: Uint8Array;
|
|
19
|
+
} | undefined, _id: Uint8Array);
|
|
20
|
+
header(): Uint8Array;
|
|
21
|
+
seal(plaintext: Uint8Array): Uint8Array;
|
|
22
|
+
final(plaintext: Uint8Array): Uint8Array;
|
|
23
|
+
private _sealChunk;
|
|
24
|
+
private _wipe;
|
|
25
|
+
dispose(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare class XChaCha20StreamOpener {
|
|
28
|
+
private readonly _x;
|
|
29
|
+
private readonly _key;
|
|
30
|
+
private readonly _cs;
|
|
31
|
+
private readonly _id;
|
|
32
|
+
private readonly _framed;
|
|
33
|
+
private readonly _aad;
|
|
34
|
+
private readonly _buf;
|
|
35
|
+
private readonly _maxFrame;
|
|
36
|
+
private _bufLen;
|
|
37
|
+
private _index;
|
|
38
|
+
private _dead;
|
|
39
|
+
constructor(key: Uint8Array, header: Uint8Array, opts?: {
|
|
40
|
+
framed?: boolean;
|
|
41
|
+
aad?: Uint8Array;
|
|
42
|
+
});
|
|
43
|
+
get closed(): boolean;
|
|
44
|
+
open(chunk: Uint8Array): Uint8Array;
|
|
45
|
+
private _openRaw;
|
|
46
|
+
feed(bytes: Uint8Array): Uint8Array[];
|
|
47
|
+
private _wipe;
|
|
48
|
+
dispose(): void;
|
|
49
|
+
}
|