leviathan-crypto 2.0.1 → 2.1.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 +171 -7
- package/LICENSE +4 -0
- package/README.md +109 -54
- package/SECURITY.md +125 -238
- package/dist/chacha20/cipher-suite.d.ts +10 -0
- package/dist/chacha20/cipher-suite.js +65 -2
- package/dist/chacha20/generator.d.ts +12 -0
- package/dist/chacha20/generator.js +91 -0
- package/dist/chacha20/index.d.ts +97 -1
- package/dist/chacha20/index.js +139 -11
- package/dist/chacha20/ops.d.ts +57 -6
- package/dist/chacha20/ops.js +93 -13
- package/dist/chacha20/pool-worker.js +12 -0
- package/dist/chacha20/types.d.ts +1 -32
- package/dist/ct-wasm.js +1 -1
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +66 -26
- package/dist/docs/architecture.md +600 -521
- package/dist/docs/argon2id.md +17 -14
- package/dist/docs/chacha20.md +146 -39
- package/dist/docs/exports.md +46 -10
- package/dist/docs/fortuna.md +339 -122
- package/dist/docs/init.md +24 -25
- package/dist/docs/loader.md +142 -47
- package/dist/docs/serpent.md +139 -41
- package/dist/docs/sha2.md +77 -19
- package/dist/docs/sha3.md +81 -15
- package/dist/docs/types.md +155 -15
- package/dist/docs/utils.md +171 -81
- package/dist/embedded/chacha20-pool-worker.d.ts +1 -0
- package/dist/embedded/chacha20-pool-worker.js +5 -0
- package/dist/embedded/kyber.d.ts +1 -1
- package/dist/embedded/kyber.js +1 -1
- package/dist/embedded/serpent-pool-worker.d.ts +1 -0
- package/dist/embedded/serpent-pool-worker.js +5 -0
- package/dist/fortuna.d.ts +14 -8
- package/dist/fortuna.js +144 -50
- package/dist/index.d.ts +8 -6
- package/dist/index.js +6 -5
- package/dist/init.d.ts +0 -2
- package/dist/init.js +83 -3
- package/dist/kyber/indcpa.js +4 -4
- package/dist/kyber/index.js +25 -5
- package/dist/kyber/kem.js +56 -1
- package/dist/kyber/suite.d.ts +1 -2
- package/dist/kyber/types.d.ts +1 -0
- package/dist/kyber/validate.d.ts +8 -4
- package/dist/kyber/validate.js +18 -14
- package/dist/kyber.wasm +0 -0
- package/dist/loader.d.ts +7 -2
- package/dist/loader.js +25 -28
- package/dist/ratchet/index.d.ts +6 -0
- package/dist/ratchet/index.js +37 -0
- package/dist/ratchet/kdf-chain.d.ts +13 -0
- package/dist/ratchet/kdf-chain.js +85 -0
- package/dist/ratchet/ratchet-keypair.d.ts +9 -0
- package/dist/ratchet/ratchet-keypair.js +61 -0
- package/dist/ratchet/root-kdf.d.ts +4 -0
- package/dist/ratchet/root-kdf.js +124 -0
- package/dist/ratchet/skipped-key-store.d.ts +14 -0
- package/dist/ratchet/skipped-key-store.js +154 -0
- package/dist/ratchet/types.d.ts +36 -0
- package/dist/ratchet/types.js +26 -0
- package/dist/serpent/cipher-suite.d.ts +10 -0
- package/dist/serpent/cipher-suite.js +135 -50
- package/dist/serpent/generator.d.ts +12 -0
- package/dist/serpent/generator.js +97 -0
- package/dist/serpent/index.d.ts +61 -1
- package/dist/serpent/index.js +92 -7
- package/dist/serpent/pool-worker.js +25 -101
- package/dist/serpent/serpent-cbc.d.ts +14 -4
- package/dist/serpent/serpent-cbc.js +50 -32
- package/dist/serpent/shared-ops.d.ts +83 -0
- package/dist/serpent/shared-ops.js +213 -0
- package/dist/serpent/types.d.ts +1 -5
- package/dist/sha2/hash.d.ts +2 -0
- package/dist/sha2/hash.js +53 -0
- package/dist/sha2/index.d.ts +1 -0
- package/dist/sha2/index.js +15 -1
- package/dist/sha3/hash.d.ts +2 -0
- package/dist/sha3/hash.js +53 -0
- package/dist/sha3/index.d.ts +17 -2
- package/dist/sha3/index.js +79 -7
- package/dist/stream/header.js +5 -5
- package/dist/stream/open-stream.js +36 -14
- package/dist/stream/seal-stream-pool.d.ts +1 -0
- package/dist/stream/seal-stream-pool.js +38 -8
- package/dist/stream/seal-stream.js +29 -11
- package/dist/types.d.ts +21 -0
- package/dist/utils.d.ts +7 -8
- package/dist/utils.js +73 -40
- package/dist/wasm-source.d.ts +9 -8
- package/package.json +79 -64
package/dist/chacha20/index.d.ts
CHANGED
|
@@ -1,21 +1,72 @@
|
|
|
1
1
|
import type { WasmSource } from '../wasm-source.js';
|
|
2
2
|
import { AuthenticationError } from '../errors.js';
|
|
3
3
|
export { AuthenticationError };
|
|
4
|
+
/**
|
|
5
|
+
* Load and initialise the ChaCha20 WASM module from `source`.
|
|
6
|
+
* Must be called before constructing any ChaCha20 class.
|
|
7
|
+
* @param source WASM binary — gzip+base64 string, URL, ArrayBuffer, Uint8Array,
|
|
8
|
+
* pre-compiled WebAssembly.Module, Response, or Promise<Response>
|
|
9
|
+
*/
|
|
4
10
|
export declare function chacha20Init(source: WasmSource): Promise<void>;
|
|
5
11
|
export type { WasmSource };
|
|
12
|
+
/**
|
|
13
|
+
* Raw ChaCha20 stream cipher (RFC 8439 §2.4).
|
|
14
|
+
*
|
|
15
|
+
* Holds exclusive access to the `chacha20` WASM module from construction
|
|
16
|
+
* until `dispose()`. Constructing a second ChaCha20 or any other chacha20
|
|
17
|
+
* user while this instance is live throws. Call `dispose()` when done.
|
|
18
|
+
*/
|
|
6
19
|
export declare class ChaCha20 {
|
|
7
20
|
private readonly x;
|
|
21
|
+
private _tok;
|
|
8
22
|
constructor();
|
|
23
|
+
/**
|
|
24
|
+
* Load key and nonce into WASM state and set the block counter to 1.
|
|
25
|
+
* Must be called before each message (RFC 8439 §2.4).
|
|
26
|
+
* @param key 32 bytes
|
|
27
|
+
* @param nonce 12 bytes — must be unique per (key, message)
|
|
28
|
+
*/
|
|
9
29
|
beginEncrypt(key: Uint8Array, nonce: Uint8Array): void;
|
|
30
|
+
/**
|
|
31
|
+
* XOR `chunk` with the next keystream block(s). Counter advances automatically.
|
|
32
|
+
* @param chunk Plaintext chunk — must not exceed WASM CHUNK_SIZE
|
|
33
|
+
* @returns Ciphertext of the same length
|
|
34
|
+
*/
|
|
10
35
|
encryptChunk(chunk: Uint8Array): Uint8Array;
|
|
36
|
+
/**
|
|
37
|
+
* Alias for `beginEncrypt` — ChaCha20 is a stream cipher (symmetric).
|
|
38
|
+
* @param key 32 bytes
|
|
39
|
+
* @param nonce 12 bytes — must match the value used to encrypt
|
|
40
|
+
*/
|
|
11
41
|
beginDecrypt(key: Uint8Array, nonce: Uint8Array): void;
|
|
42
|
+
/**
|
|
43
|
+
* Alias for `encryptChunk` — ChaCha20 is a stream cipher (symmetric).
|
|
44
|
+
* @param chunk Ciphertext chunk
|
|
45
|
+
* @returns Plaintext of the same length
|
|
46
|
+
*/
|
|
12
47
|
decryptChunk(chunk: Uint8Array): Uint8Array;
|
|
48
|
+
/** Wipe WASM state and release exclusive module access. Idempotent. */
|
|
13
49
|
dispose(): void;
|
|
14
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Poly1305 one-time MAC (RFC 8439 §2.5).
|
|
53
|
+
*
|
|
54
|
+
* Atomic (stateless) class: each `mac()` call is independent.
|
|
55
|
+
* The key must never be reused across messages. For most use cases, prefer
|
|
56
|
+
* `ChaCha20Poly1305` or `XChaCha20Poly1305` which manage the one-time key
|
|
57
|
+
* automatically.
|
|
58
|
+
*/
|
|
15
59
|
export declare class Poly1305 {
|
|
16
60
|
private readonly x;
|
|
17
61
|
constructor();
|
|
62
|
+
/**
|
|
63
|
+
* Compute a 16-byte Poly1305 MAC for `msg` using `key`.
|
|
64
|
+
* @param key 32-byte one-time key — must not be reused across messages
|
|
65
|
+
* @param msg Message to authenticate
|
|
66
|
+
* @returns 16-byte Poly1305 tag
|
|
67
|
+
*/
|
|
18
68
|
mac(key: Uint8Array, msg: Uint8Array): Uint8Array;
|
|
69
|
+
/** Wipe WASM MAC state. */
|
|
19
70
|
dispose(): void;
|
|
20
71
|
}
|
|
21
72
|
/**
|
|
@@ -34,9 +85,31 @@ export declare class ChaCha20Poly1305 {
|
|
|
34
85
|
private readonly x;
|
|
35
86
|
private _used;
|
|
36
87
|
constructor();
|
|
88
|
+
/**
|
|
89
|
+
* Encrypt and authenticate `plaintext` with ChaCha20-Poly1305 (RFC 8439 §2.8).
|
|
90
|
+
*
|
|
91
|
+
* **Single-use guard:** `encrypt()` may only be called once per instance.
|
|
92
|
+
* Any throw — including validation errors — permanently locks this instance.
|
|
93
|
+
* Always create a new `ChaCha20Poly1305` per message.
|
|
94
|
+
* @param key 32 bytes
|
|
95
|
+
* @param nonce 12 bytes — must be unique per (key, message)
|
|
96
|
+
* @param plaintext Data to encrypt
|
|
97
|
+
* @param aad Additional authenticated data (optional)
|
|
98
|
+
* @returns Ciphertext || 16-byte Poly1305 tag
|
|
99
|
+
*/
|
|
37
100
|
encrypt(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, aad?: Uint8Array): Uint8Array;
|
|
101
|
+
/**
|
|
102
|
+
* Verify and decrypt a ChaCha20-Poly1305 ciphertext (RFC 8439 §2.8).
|
|
103
|
+
* Throws `AuthenticationError` if the tag does not match.
|
|
104
|
+
* @param key 32 bytes
|
|
105
|
+
* @param nonce 12 bytes — must match the value used to encrypt
|
|
106
|
+
* @param ciphertext Ciphertext || 16-byte tag (combined format from `encrypt`)
|
|
107
|
+
* @param aad Additional authenticated data (optional)
|
|
108
|
+
* @returns Plaintext
|
|
109
|
+
*/
|
|
38
110
|
decrypt(key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, // ciphertext || tag(16) combined
|
|
39
111
|
aad?: Uint8Array): Uint8Array;
|
|
112
|
+
/** Wipe WASM cipher and MAC state. */
|
|
40
113
|
dispose(): void;
|
|
41
114
|
}
|
|
42
115
|
/**
|
|
@@ -54,9 +127,32 @@ export declare class XChaCha20Poly1305 {
|
|
|
54
127
|
private readonly x;
|
|
55
128
|
private _used;
|
|
56
129
|
constructor();
|
|
130
|
+
/**
|
|
131
|
+
* Encrypt and authenticate `plaintext` with XChaCha20-Poly1305
|
|
132
|
+
* (draft-irtf-cfrg-xchacha). Recommended for general-purpose AEAD.
|
|
133
|
+
*
|
|
134
|
+
* **Single-use guard:** `encrypt()` may only be called once per instance.
|
|
135
|
+
* Any throw — including validation errors — permanently locks this instance.
|
|
136
|
+
* Always create a new `XChaCha20Poly1305` per message to prevent nonce reuse.
|
|
137
|
+
* @param key 32 bytes
|
|
138
|
+
* @param nonce 24 bytes — safe to generate randomly via `randomBytes(24)`
|
|
139
|
+
* @param plaintext Data to encrypt
|
|
140
|
+
* @param aad Additional authenticated data (optional)
|
|
141
|
+
* @returns Ciphertext || 16-byte Poly1305 tag
|
|
142
|
+
*/
|
|
57
143
|
encrypt(key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, aad?: Uint8Array): Uint8Array;
|
|
144
|
+
/**
|
|
145
|
+
* Verify and decrypt an XChaCha20-Poly1305 ciphertext.
|
|
146
|
+
* Throws `AuthenticationError` if the tag does not match.
|
|
147
|
+
* @param key 32 bytes
|
|
148
|
+
* @param nonce 24 bytes — must match the value used to encrypt
|
|
149
|
+
* @param ciphertext Ciphertext || 16-byte tag (combined format from `encrypt`)
|
|
150
|
+
* @param aad Additional authenticated data (optional)
|
|
151
|
+
* @returns Plaintext
|
|
152
|
+
*/
|
|
58
153
|
decrypt(key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, aad?: Uint8Array): Uint8Array;
|
|
154
|
+
/** Wipe WASM cipher and MAC state. */
|
|
59
155
|
dispose(): void;
|
|
60
156
|
}
|
|
61
157
|
export { XChaCha20Cipher } from './cipher-suite.js';
|
|
62
|
-
export
|
|
158
|
+
export { ChaCha20Generator } from './generator.js';
|
package/dist/chacha20/index.js
CHANGED
|
@@ -23,23 +23,47 @@
|
|
|
23
23
|
//
|
|
24
24
|
// Public API classes for the ChaCha20 WASM module.
|
|
25
25
|
// Uses the init() module cache — call chacha20Init(source) before constructing.
|
|
26
|
-
import { getInstance, initModule } from '../init.js';
|
|
26
|
+
import { getInstance, initModule, _acquireModule, _releaseModule, _assertNotOwned } from '../init.js';
|
|
27
27
|
import { aeadEncrypt, aeadDecrypt, xcEncrypt, xcDecrypt } from './ops.js';
|
|
28
28
|
import { AuthenticationError } from '../errors.js';
|
|
29
29
|
export { AuthenticationError };
|
|
30
|
+
/**
|
|
31
|
+
* Load and initialise the ChaCha20 WASM module from `source`.
|
|
32
|
+
* Must be called before constructing any ChaCha20 class.
|
|
33
|
+
* @param source WASM binary — gzip+base64 string, URL, ArrayBuffer, Uint8Array,
|
|
34
|
+
* pre-compiled WebAssembly.Module, Response, or Promise<Response>
|
|
35
|
+
*/
|
|
30
36
|
export async function chacha20Init(source) {
|
|
31
37
|
return initModule('chacha20', source);
|
|
32
38
|
}
|
|
39
|
+
/** Returns the raw chacha20 WASM export object. @internal */
|
|
33
40
|
function getExports() {
|
|
34
41
|
return getInstance('chacha20').exports;
|
|
35
42
|
}
|
|
36
|
-
// ── ChaCha20
|
|
43
|
+
// ── ChaCha20 ────────────────────────────────────────────────────────────────
|
|
44
|
+
/**
|
|
45
|
+
* Raw ChaCha20 stream cipher (RFC 8439 §2.4).
|
|
46
|
+
*
|
|
47
|
+
* Holds exclusive access to the `chacha20` WASM module from construction
|
|
48
|
+
* until `dispose()`. Constructing a second ChaCha20 or any other chacha20
|
|
49
|
+
* user while this instance is live throws. Call `dispose()` when done.
|
|
50
|
+
*/
|
|
37
51
|
export class ChaCha20 {
|
|
38
52
|
x;
|
|
53
|
+
_tok;
|
|
39
54
|
constructor() {
|
|
40
55
|
this.x = getExports();
|
|
56
|
+
this._tok = _acquireModule('chacha20');
|
|
41
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Load key and nonce into WASM state and set the block counter to 1.
|
|
60
|
+
* Must be called before each message (RFC 8439 §2.4).
|
|
61
|
+
* @param key 32 bytes
|
|
62
|
+
* @param nonce 12 bytes — must be unique per (key, message)
|
|
63
|
+
*/
|
|
42
64
|
beginEncrypt(key, nonce) {
|
|
65
|
+
if (this._tok === undefined)
|
|
66
|
+
throw new Error('ChaCha20: instance has been disposed');
|
|
43
67
|
if (key.length !== 32)
|
|
44
68
|
throw new RangeError(`ChaCha20 key must be 32 bytes (got ${key.length})`);
|
|
45
69
|
if (nonce.length !== 12)
|
|
@@ -50,7 +74,14 @@ export class ChaCha20 {
|
|
|
50
74
|
this.x.chachaSetCounter(1);
|
|
51
75
|
this.x.chachaLoadKey();
|
|
52
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* XOR `chunk` with the next keystream block(s). Counter advances automatically.
|
|
79
|
+
* @param chunk Plaintext chunk — must not exceed WASM CHUNK_SIZE
|
|
80
|
+
* @returns Ciphertext of the same length
|
|
81
|
+
*/
|
|
53
82
|
encryptChunk(chunk) {
|
|
83
|
+
if (this._tok === undefined)
|
|
84
|
+
throw new Error('ChaCha20: instance has been disposed');
|
|
54
85
|
const maxChunk = this.x.getChunkSize();
|
|
55
86
|
if (chunk.length > maxChunk)
|
|
56
87
|
throw new RangeError(`chunk exceeds maximum size of ${maxChunk} bytes — split into smaller chunks`);
|
|
@@ -61,23 +92,57 @@ export class ChaCha20 {
|
|
|
61
92
|
this.x.chachaEncryptChunk_simd(chunk.length);
|
|
62
93
|
return mem.slice(ctOff, ctOff + chunk.length);
|
|
63
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Alias for `beginEncrypt` — ChaCha20 is a stream cipher (symmetric).
|
|
97
|
+
* @param key 32 bytes
|
|
98
|
+
* @param nonce 12 bytes — must match the value used to encrypt
|
|
99
|
+
*/
|
|
64
100
|
beginDecrypt(key, nonce) {
|
|
65
101
|
this.beginEncrypt(key, nonce);
|
|
66
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Alias for `encryptChunk` — ChaCha20 is a stream cipher (symmetric).
|
|
105
|
+
* @param chunk Ciphertext chunk
|
|
106
|
+
* @returns Plaintext of the same length
|
|
107
|
+
*/
|
|
67
108
|
decryptChunk(chunk) {
|
|
68
109
|
return this.encryptChunk(chunk);
|
|
69
110
|
}
|
|
111
|
+
/** Wipe WASM state and release exclusive module access. Idempotent. */
|
|
70
112
|
dispose() {
|
|
71
|
-
this.
|
|
113
|
+
if (this._tok === undefined)
|
|
114
|
+
return;
|
|
115
|
+
try {
|
|
116
|
+
this.x.wipeBuffers();
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
_releaseModule('chacha20', this._tok);
|
|
120
|
+
this._tok = undefined;
|
|
121
|
+
}
|
|
72
122
|
}
|
|
73
123
|
}
|
|
74
|
-
// ── Poly1305
|
|
124
|
+
// ── Poly1305 ────────────────────────────────────────────────────────────────
|
|
125
|
+
/**
|
|
126
|
+
* Poly1305 one-time MAC (RFC 8439 §2.5).
|
|
127
|
+
*
|
|
128
|
+
* Atomic (stateless) class: each `mac()` call is independent.
|
|
129
|
+
* The key must never be reused across messages. For most use cases, prefer
|
|
130
|
+
* `ChaCha20Poly1305` or `XChaCha20Poly1305` which manage the one-time key
|
|
131
|
+
* automatically.
|
|
132
|
+
*/
|
|
75
133
|
export class Poly1305 {
|
|
76
134
|
x;
|
|
77
135
|
constructor() {
|
|
78
136
|
this.x = getExports();
|
|
79
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Compute a 16-byte Poly1305 MAC for `msg` using `key`.
|
|
140
|
+
* @param key 32-byte one-time key — must not be reused across messages
|
|
141
|
+
* @param msg Message to authenticate
|
|
142
|
+
* @returns 16-byte Poly1305 tag
|
|
143
|
+
*/
|
|
80
144
|
mac(key, msg) {
|
|
145
|
+
_assertNotOwned('chacha20');
|
|
81
146
|
if (key.length !== 32)
|
|
82
147
|
throw new RangeError(`Poly1305 key must be 32 bytes (got ${key.length})`);
|
|
83
148
|
const mem = new Uint8Array(this.x.memory.buffer);
|
|
@@ -96,11 +161,13 @@ export class Poly1305 {
|
|
|
96
161
|
this.x.polyFinal();
|
|
97
162
|
return new Uint8Array(this.x.memory.buffer).slice(tagOff, tagOff + 16);
|
|
98
163
|
}
|
|
164
|
+
/** Wipe WASM MAC state. */
|
|
99
165
|
dispose() {
|
|
166
|
+
_assertNotOwned('chacha20');
|
|
100
167
|
this.x.wipeBuffers();
|
|
101
168
|
}
|
|
102
169
|
}
|
|
103
|
-
// ── ChaCha20Poly1305
|
|
170
|
+
// ── ChaCha20Poly1305 ────────────────────────────────────────────────────────
|
|
104
171
|
/**
|
|
105
172
|
* ChaCha20-Poly1305 AEAD (RFC 8439 §2.8).
|
|
106
173
|
*
|
|
@@ -119,23 +186,48 @@ export class ChaCha20Poly1305 {
|
|
|
119
186
|
constructor() {
|
|
120
187
|
this.x = getExports();
|
|
121
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Encrypt and authenticate `plaintext` with ChaCha20-Poly1305 (RFC 8439 §2.8).
|
|
191
|
+
*
|
|
192
|
+
* **Single-use guard:** `encrypt()` may only be called once per instance.
|
|
193
|
+
* Any throw — including validation errors — permanently locks this instance.
|
|
194
|
+
* Always create a new `ChaCha20Poly1305` per message.
|
|
195
|
+
* @param key 32 bytes
|
|
196
|
+
* @param nonce 12 bytes — must be unique per (key, message)
|
|
197
|
+
* @param plaintext Data to encrypt
|
|
198
|
+
* @param aad Additional authenticated data (optional)
|
|
199
|
+
* @returns Ciphertext || 16-byte Poly1305 tag
|
|
200
|
+
*/
|
|
122
201
|
encrypt(key, nonce, plaintext, aad = new Uint8Array(0)) {
|
|
123
202
|
if (this._used)
|
|
124
203
|
throw new Error('leviathan-crypto: encrypt() already called on this instance. '
|
|
125
204
|
+ 'Create a new instance for each encryption to prevent nonce reuse.');
|
|
205
|
+
// Strict single-use: lock FIRST, before anything else. Any subsequent
|
|
206
|
+
// throw — including validation errors — terminates the instance.
|
|
207
|
+
this._used = true;
|
|
208
|
+
_assertNotOwned('chacha20');
|
|
126
209
|
if (key.length !== 32)
|
|
127
210
|
throw new RangeError(`key must be 32 bytes (got ${key.length})`);
|
|
128
211
|
if (nonce.length !== 12)
|
|
129
212
|
throw new RangeError(`nonce must be 12 bytes (got ${nonce.length})`);
|
|
130
213
|
const { ciphertext, tag } = aeadEncrypt(this.x, key, nonce, plaintext, aad);
|
|
131
|
-
this._used = true;
|
|
132
214
|
const out = new Uint8Array(ciphertext.length + 16);
|
|
133
215
|
out.set(ciphertext);
|
|
134
216
|
out.set(tag, ciphertext.length);
|
|
135
217
|
return out;
|
|
136
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Verify and decrypt a ChaCha20-Poly1305 ciphertext (RFC 8439 §2.8).
|
|
221
|
+
* Throws `AuthenticationError` if the tag does not match.
|
|
222
|
+
* @param key 32 bytes
|
|
223
|
+
* @param nonce 12 bytes — must match the value used to encrypt
|
|
224
|
+
* @param ciphertext Ciphertext || 16-byte tag (combined format from `encrypt`)
|
|
225
|
+
* @param aad Additional authenticated data (optional)
|
|
226
|
+
* @returns Plaintext
|
|
227
|
+
*/
|
|
137
228
|
decrypt(key, nonce, ciphertext, // ciphertext || tag(16) combined
|
|
138
229
|
aad = new Uint8Array(0)) {
|
|
230
|
+
_assertNotOwned('chacha20');
|
|
139
231
|
if (key.length !== 32)
|
|
140
232
|
throw new RangeError(`key must be 32 bytes (got ${key.length})`);
|
|
141
233
|
if (nonce.length !== 12)
|
|
@@ -146,11 +238,13 @@ export class ChaCha20Poly1305 {
|
|
|
146
238
|
const tag = ciphertext.subarray(ciphertext.length - 16);
|
|
147
239
|
return aeadDecrypt(this.x, key, nonce, ct, tag, aad);
|
|
148
240
|
}
|
|
241
|
+
/** Wipe WASM cipher and MAC state. */
|
|
149
242
|
dispose() {
|
|
243
|
+
_assertNotOwned('chacha20');
|
|
150
244
|
this.x.wipeBuffers();
|
|
151
245
|
}
|
|
152
246
|
}
|
|
153
|
-
// ── XChaCha20Poly1305
|
|
247
|
+
// ── XChaCha20Poly1305 ───────────────────────────────────────────────────────
|
|
154
248
|
/**
|
|
155
249
|
* XChaCha20-Poly1305 AEAD (IETF draft-irtf-cfrg-xchacha).
|
|
156
250
|
*
|
|
@@ -168,19 +262,44 @@ export class XChaCha20Poly1305 {
|
|
|
168
262
|
constructor() {
|
|
169
263
|
this.x = getExports();
|
|
170
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* Encrypt and authenticate `plaintext` with XChaCha20-Poly1305
|
|
267
|
+
* (draft-irtf-cfrg-xchacha). Recommended for general-purpose AEAD.
|
|
268
|
+
*
|
|
269
|
+
* **Single-use guard:** `encrypt()` may only be called once per instance.
|
|
270
|
+
* Any throw — including validation errors — permanently locks this instance.
|
|
271
|
+
* Always create a new `XChaCha20Poly1305` per message to prevent nonce reuse.
|
|
272
|
+
* @param key 32 bytes
|
|
273
|
+
* @param nonce 24 bytes — safe to generate randomly via `randomBytes(24)`
|
|
274
|
+
* @param plaintext Data to encrypt
|
|
275
|
+
* @param aad Additional authenticated data (optional)
|
|
276
|
+
* @returns Ciphertext || 16-byte Poly1305 tag
|
|
277
|
+
*/
|
|
171
278
|
encrypt(key, nonce, plaintext, aad = new Uint8Array(0)) {
|
|
172
279
|
if (this._used)
|
|
173
280
|
throw new Error('leviathan-crypto: encrypt() already called on this instance. '
|
|
174
281
|
+ 'Create a new instance for each encryption to prevent nonce reuse.');
|
|
282
|
+
// Strict single-use: lock FIRST, before anything else. Any subsequent
|
|
283
|
+
// throw — including validation errors — terminates the instance.
|
|
284
|
+
this._used = true;
|
|
285
|
+
_assertNotOwned('chacha20');
|
|
175
286
|
if (key.length !== 32)
|
|
176
287
|
throw new RangeError(`key must be 32 bytes (got ${key.length})`);
|
|
177
288
|
if (nonce.length !== 24)
|
|
178
289
|
throw new RangeError(`XChaCha20 nonce must be 24 bytes (got ${nonce.length})`);
|
|
179
|
-
|
|
180
|
-
this._used = true;
|
|
181
|
-
return result;
|
|
290
|
+
return xcEncrypt(this.x, key, nonce, plaintext, aad);
|
|
182
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Verify and decrypt an XChaCha20-Poly1305 ciphertext.
|
|
294
|
+
* Throws `AuthenticationError` if the tag does not match.
|
|
295
|
+
* @param key 32 bytes
|
|
296
|
+
* @param nonce 24 bytes — must match the value used to encrypt
|
|
297
|
+
* @param ciphertext Ciphertext || 16-byte tag (combined format from `encrypt`)
|
|
298
|
+
* @param aad Additional authenticated data (optional)
|
|
299
|
+
* @returns Plaintext
|
|
300
|
+
*/
|
|
183
301
|
decrypt(key, nonce, ciphertext, aad = new Uint8Array(0)) {
|
|
302
|
+
_assertNotOwned('chacha20');
|
|
184
303
|
if (key.length !== 32)
|
|
185
304
|
throw new RangeError(`key must be 32 bytes (got ${key.length})`);
|
|
186
305
|
if (nonce.length !== 24)
|
|
@@ -189,12 +308,21 @@ export class XChaCha20Poly1305 {
|
|
|
189
308
|
throw new RangeError(`ciphertext too short — must include 16-byte tag (got ${ciphertext.length})`);
|
|
190
309
|
return xcDecrypt(this.x, key, nonce, ciphertext, aad);
|
|
191
310
|
}
|
|
311
|
+
/** Wipe WASM cipher and MAC state. */
|
|
192
312
|
dispose() {
|
|
313
|
+
_assertNotOwned('chacha20');
|
|
193
314
|
this.x.wipeBuffers();
|
|
194
315
|
}
|
|
195
316
|
}
|
|
196
317
|
export { XChaCha20Cipher } from './cipher-suite.js';
|
|
197
|
-
// ──
|
|
318
|
+
// ── ChaCha20Generator ───────────────────────────────────────────────────────
|
|
319
|
+
export { ChaCha20Generator } from './generator.js';
|
|
320
|
+
// ── Ready check ─────────────────────────────────────────────────────────────
|
|
321
|
+
/**
|
|
322
|
+
* Returns `true` if the chacha20 WASM module has been initialised.
|
|
323
|
+
* Used by tests and internal guards; not part of the public API.
|
|
324
|
+
* @internal
|
|
325
|
+
*/
|
|
198
326
|
export function _chachaReady() {
|
|
199
327
|
try {
|
|
200
328
|
getInstance('chacha20');
|
package/dist/chacha20/ops.d.ts
CHANGED
|
@@ -1,16 +1,67 @@
|
|
|
1
1
|
import type { ChaChaExports } from './types.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* ChaCha20-Poly1305 AEAD encrypt (RFC 8439 §2.8).
|
|
4
|
+
* @param x ChaCha20 WASM exports
|
|
5
|
+
* @param key 32-byte key
|
|
6
|
+
* @param nonce 12-byte nonce — must be unique per (key, message)
|
|
7
|
+
* @param plaintext Data to encrypt (must be ≤ WASM CHUNK_SIZE)
|
|
8
|
+
* @param aad Additional authenticated data
|
|
9
|
+
* @returns `{ ciphertext, tag }` — tag is 16 bytes
|
|
10
|
+
*/
|
|
3
11
|
export declare function aeadEncrypt(x: ChaChaExports, key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, aad: Uint8Array): {
|
|
4
12
|
ciphertext: Uint8Array;
|
|
5
13
|
tag: Uint8Array;
|
|
6
14
|
};
|
|
7
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* ChaCha20-Poly1305 AEAD decrypt with constant-time tag comparison (RFC 8439 §2.8).
|
|
17
|
+
* Throws `AuthenticationError` on tag mismatch; never returns plaintext on failure.
|
|
18
|
+
* @param x ChaCha20 WASM exports
|
|
19
|
+
* @param key 32-byte key
|
|
20
|
+
* @param nonce 12-byte nonce — must match the value used to encrypt
|
|
21
|
+
* @param ciphertext Ciphertext bytes (must be ≤ WASM CHUNK_SIZE)
|
|
22
|
+
* @param tag 16-byte Poly1305 tag
|
|
23
|
+
* @param aad Additional authenticated data
|
|
24
|
+
* @param cipherName Error label for `AuthenticationError` (default 'chacha20-poly1305')
|
|
25
|
+
* @returns Plaintext
|
|
26
|
+
*/
|
|
8
27
|
export declare function aeadDecrypt(x: ChaChaExports, key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, tag: Uint8Array, aad: Uint8Array, cipherName?: string): Uint8Array;
|
|
9
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Derive a 32-byte HChaCha20 subkey from `key` and the first 16 bytes of `nonce`.
|
|
30
|
+
* Used as the inner key for XChaCha20-Poly1305 (draft-irtf-cfrg-xchacha §2.3).
|
|
31
|
+
* @param x ChaCha20 WASM exports
|
|
32
|
+
* @param key 32-byte master key
|
|
33
|
+
* @param nonce 24-byte XChaCha20 nonce (only bytes 0–15 are used)
|
|
34
|
+
* @returns 32-byte HChaCha20 subkey
|
|
35
|
+
*/
|
|
10
36
|
export declare function deriveSubkey(x: ChaChaExports, key: Uint8Array, nonce: Uint8Array): Uint8Array;
|
|
11
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Build the inner 12-byte ChaCha20 nonce for XChaCha20 from bytes 16–23 of the
|
|
39
|
+
* 24-byte XChaCha nonce (draft-irtf-cfrg-xchacha §2.3).
|
|
40
|
+
* @param nonce 24-byte XChaCha20 nonce
|
|
41
|
+
* @returns 12-byte inner nonce (bytes 0–3 are zero, bytes 4–11 are nonce[16:24])
|
|
42
|
+
*/
|
|
12
43
|
export declare function innerNonce(nonce: Uint8Array): Uint8Array;
|
|
13
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* XChaCha20-Poly1305 encrypt (draft-irtf-cfrg-xchacha).
|
|
46
|
+
* Derives HChaCha20 subkey from `key` + nonce[0:16], then runs
|
|
47
|
+
* ChaCha20-Poly1305 with a 12-byte inner nonce (nonce[16:24]).
|
|
48
|
+
* @param x ChaCha20 WASM exports
|
|
49
|
+
* @param key 32-byte key
|
|
50
|
+
* @param nonce 24-byte nonce
|
|
51
|
+
* @param plaintext Data to encrypt
|
|
52
|
+
* @param aad Additional authenticated data
|
|
53
|
+
* @returns Ciphertext || 16-byte Poly1305 tag
|
|
54
|
+
*/
|
|
14
55
|
export declare function xcEncrypt(x: ChaChaExports, key: Uint8Array, nonce: Uint8Array, plaintext: Uint8Array, aad: Uint8Array): Uint8Array;
|
|
15
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* XChaCha20-Poly1305 decrypt (draft-irtf-cfrg-xchacha).
|
|
58
|
+
* Derives HChaCha20 subkey, verifies the Poly1305 tag, then decrypts.
|
|
59
|
+
* Throws `AuthenticationError` on tag mismatch.
|
|
60
|
+
* @param x ChaCha20 WASM exports
|
|
61
|
+
* @param key 32-byte key
|
|
62
|
+
* @param nonce 24-byte nonce — must match the value used to encrypt
|
|
63
|
+
* @param ciphertext Ciphertext || 16-byte tag (combined format from `xcEncrypt`)
|
|
64
|
+
* @param aad Additional authenticated data
|
|
65
|
+
* @returns Plaintext
|
|
66
|
+
*/
|
|
16
67
|
export declare function xcDecrypt(x: ChaChaExports, key: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, aad: Uint8Array): Uint8Array;
|
package/dist/chacha20/ops.js
CHANGED
|
@@ -5,7 +5,14 @@
|
|
|
5
5
|
// and the pool worker (pool.worker.ts), eliminating duplication.
|
|
6
6
|
import { constantTimeEqual } from '../utils.js';
|
|
7
7
|
import { AuthenticationError } from '../errors.js';
|
|
8
|
-
// ── Module-private helpers
|
|
8
|
+
// ── Module-private helpers ──────────────────────────────────────────────────
|
|
9
|
+
/**
|
|
10
|
+
* Feed `data` into the active Poly1305 accumulator via the WASM message buffer.
|
|
11
|
+
* No-op when `data` is empty.
|
|
12
|
+
* @param x ChaCha20 WASM exports
|
|
13
|
+
* @param data Bytes to absorb
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
9
16
|
function polyFeed(x, data) {
|
|
10
17
|
if (data.length === 0)
|
|
11
18
|
return;
|
|
@@ -19,6 +26,14 @@ function polyFeed(x, data) {
|
|
|
19
26
|
pos += chunk;
|
|
20
27
|
}
|
|
21
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Build the 16-byte Poly1305 length footer from AAD and ciphertext lengths.
|
|
31
|
+
* Both lengths are encoded as 64-bit little-endian integers (RFC 8439 §2.8).
|
|
32
|
+
* @param aadLen AAD byte length
|
|
33
|
+
* @param ctLen Ciphertext byte length
|
|
34
|
+
* @returns 16-byte length block
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
22
37
|
function lenBlock(aadLen, ctLen) {
|
|
23
38
|
const b = new Uint8Array(16);
|
|
24
39
|
const dv = new DataView(b.buffer);
|
|
@@ -31,8 +46,16 @@ function lenBlock(aadLen, ctLen) {
|
|
|
31
46
|
dv.setUint32(12, Math.floor(ctLen / 0x100000000) >>> 0, true);
|
|
32
47
|
return b;
|
|
33
48
|
}
|
|
34
|
-
// ── Inner AEAD (12-byte nonce)
|
|
35
|
-
/**
|
|
49
|
+
// ── Inner AEAD (12-byte nonce) ──────────────────────────────────────────────
|
|
50
|
+
/**
|
|
51
|
+
* ChaCha20-Poly1305 AEAD encrypt (RFC 8439 §2.8).
|
|
52
|
+
* @param x ChaCha20 WASM exports
|
|
53
|
+
* @param key 32-byte key
|
|
54
|
+
* @param nonce 12-byte nonce — must be unique per (key, message)
|
|
55
|
+
* @param plaintext Data to encrypt (must be ≤ WASM CHUNK_SIZE)
|
|
56
|
+
* @param aad Additional authenticated data
|
|
57
|
+
* @returns `{ ciphertext, tag }` — tag is 16 bytes
|
|
58
|
+
*/
|
|
36
59
|
export function aeadEncrypt(x, key, nonce, plaintext, aad) {
|
|
37
60
|
const maxChunk = x.getChunkSize();
|
|
38
61
|
if (plaintext.length > maxChunk)
|
|
@@ -50,9 +73,13 @@ export function aeadEncrypt(x, key, nonce, plaintext, aad) {
|
|
|
50
73
|
const aadPad = (16 - aad.length % 16) % 16;
|
|
51
74
|
if (aadPad > 0)
|
|
52
75
|
polyFeed(x, new Uint8Array(aadPad));
|
|
53
|
-
// Step 4: Re-init ChaCha20 at counter=1
|
|
76
|
+
// Step 4: Re-init ChaCha20 at counter=1.
|
|
77
|
+
// `chachaGenPolyKey` mutated CHACHA_STATE + 48 (the counter word) to 0 but
|
|
78
|
+
// left every other state word intact (constants, key, nonce). Writing
|
|
79
|
+
// counter=1 via `chachaSetCounter` restores the state for encryption —
|
|
80
|
+
// no second `chachaLoadKey()` is needed (the key/nonce buffers and the
|
|
81
|
+
// non-counter state words are already correct).
|
|
54
82
|
x.chachaSetCounter(1);
|
|
55
|
-
x.chachaLoadKey();
|
|
56
83
|
// Step 5: Encrypt
|
|
57
84
|
mem.set(plaintext, x.getChunkPtOffset());
|
|
58
85
|
x.chachaEncryptChunk_simd(plaintext.length);
|
|
@@ -71,7 +98,18 @@ export function aeadEncrypt(x, key, nonce, plaintext, aad) {
|
|
|
71
98
|
const tag = new Uint8Array(x.memory.buffer).slice(tagOff, tagOff + 16);
|
|
72
99
|
return { ciphertext, tag };
|
|
73
100
|
}
|
|
74
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* ChaCha20-Poly1305 AEAD decrypt with constant-time tag comparison (RFC 8439 §2.8).
|
|
103
|
+
* Throws `AuthenticationError` on tag mismatch; never returns plaintext on failure.
|
|
104
|
+
* @param x ChaCha20 WASM exports
|
|
105
|
+
* @param key 32-byte key
|
|
106
|
+
* @param nonce 12-byte nonce — must match the value used to encrypt
|
|
107
|
+
* @param ciphertext Ciphertext bytes (must be ≤ WASM CHUNK_SIZE)
|
|
108
|
+
* @param tag 16-byte Poly1305 tag
|
|
109
|
+
* @param aad Additional authenticated data
|
|
110
|
+
* @param cipherName Error label for `AuthenticationError` (default 'chacha20-poly1305')
|
|
111
|
+
* @returns Plaintext
|
|
112
|
+
*/
|
|
75
113
|
export function aeadDecrypt(x, key, nonce, ciphertext, tag, aad, cipherName = 'chacha20-poly1305') {
|
|
76
114
|
const maxChunk = x.getChunkSize();
|
|
77
115
|
if (ciphertext.length > maxChunk)
|
|
@@ -97,9 +135,19 @@ export function aeadDecrypt(x, key, nonce, ciphertext, tag, aad, cipherName = 'c
|
|
|
97
135
|
const tagOff = x.getPolyTagOffset();
|
|
98
136
|
const expectedTag = new Uint8Array(x.memory.buffer).slice(tagOff, tagOff + 16);
|
|
99
137
|
if (!constantTimeEqual(expectedTag, tag)) {
|
|
100
|
-
// Wipe the full chunk output buffer — defense-in-depth before throwing
|
|
138
|
+
// Wipe the full chunk output buffer — defense-in-depth before throwing.
|
|
101
139
|
const ctOff = x.getChunkCtOffset();
|
|
102
140
|
mem.fill(0, ctOff, ctOff + maxChunk);
|
|
141
|
+
// Also zero the 64-byte chacha block buffer — it holds keystream bytes
|
|
142
|
+
// generated by chachaGenPolyKey() that would otherwise persist until
|
|
143
|
+
// the next op or dispose().
|
|
144
|
+
const blockOff = x.getChachaBlockOffset();
|
|
145
|
+
mem.fill(0, blockOff, blockOff + 64);
|
|
146
|
+
// And the 32-byte Poly1305 one-time subkey copy at POLY_KEY_OFFSET.
|
|
147
|
+
// chachaGenPolyKey copies keystream[0..32] here; wiping CHACHA_BLOCK
|
|
148
|
+
// zeroes the source but not this copy.
|
|
149
|
+
const polyKeyOff = x.getPolyKeyOffset();
|
|
150
|
+
mem.fill(0, polyKeyOff, polyKeyOff + 32);
|
|
103
151
|
throw new AuthenticationError(cipherName);
|
|
104
152
|
}
|
|
105
153
|
// Decrypt only after authentication succeeds
|
|
@@ -110,8 +158,15 @@ export function aeadDecrypt(x, key, nonce, ciphertext, tag, aad, cipherName = 'c
|
|
|
110
158
|
const ptOff = x.getChunkCtOffset();
|
|
111
159
|
return new Uint8Array(x.memory.buffer).slice(ptOff, ptOff + ciphertext.length);
|
|
112
160
|
}
|
|
113
|
-
// ── XChaCha20 helpers
|
|
114
|
-
/**
|
|
161
|
+
// ── XChaCha20 helpers ───────────────────────────────────────────────────────
|
|
162
|
+
/**
|
|
163
|
+
* Derive a 32-byte HChaCha20 subkey from `key` and the first 16 bytes of `nonce`.
|
|
164
|
+
* Used as the inner key for XChaCha20-Poly1305 (draft-irtf-cfrg-xchacha §2.3).
|
|
165
|
+
* @param x ChaCha20 WASM exports
|
|
166
|
+
* @param key 32-byte master key
|
|
167
|
+
* @param nonce 24-byte XChaCha20 nonce (only bytes 0–15 are used)
|
|
168
|
+
* @returns 32-byte HChaCha20 subkey
|
|
169
|
+
*/
|
|
115
170
|
export function deriveSubkey(x, key, nonce) {
|
|
116
171
|
const mem = new Uint8Array(x.memory.buffer);
|
|
117
172
|
mem.set(key, x.getKeyOffset());
|
|
@@ -120,14 +175,29 @@ export function deriveSubkey(x, key, nonce) {
|
|
|
120
175
|
const off = x.getXChaChaSubkeyOffset();
|
|
121
176
|
return new Uint8Array(x.memory.buffer).slice(off, off + 32);
|
|
122
177
|
}
|
|
123
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* Build the inner 12-byte ChaCha20 nonce for XChaCha20 from bytes 16–23 of the
|
|
180
|
+
* 24-byte XChaCha nonce (draft-irtf-cfrg-xchacha §2.3).
|
|
181
|
+
* @param nonce 24-byte XChaCha20 nonce
|
|
182
|
+
* @returns 12-byte inner nonce (bytes 0–3 are zero, bytes 4–11 are nonce[16:24])
|
|
183
|
+
*/
|
|
124
184
|
export function innerNonce(nonce) {
|
|
125
185
|
const n = new Uint8Array(12);
|
|
126
186
|
n.set(nonce.subarray(16, 24), 4);
|
|
127
187
|
return n;
|
|
128
188
|
}
|
|
129
|
-
// ── Full XChaCha20-Poly1305
|
|
130
|
-
/**
|
|
189
|
+
// ── Full XChaCha20-Poly1305 ─────────────────────────────────────────────────
|
|
190
|
+
/**
|
|
191
|
+
* XChaCha20-Poly1305 encrypt (draft-irtf-cfrg-xchacha).
|
|
192
|
+
* Derives HChaCha20 subkey from `key` + nonce[0:16], then runs
|
|
193
|
+
* ChaCha20-Poly1305 with a 12-byte inner nonce (nonce[16:24]).
|
|
194
|
+
* @param x ChaCha20 WASM exports
|
|
195
|
+
* @param key 32-byte key
|
|
196
|
+
* @param nonce 24-byte nonce
|
|
197
|
+
* @param plaintext Data to encrypt
|
|
198
|
+
* @param aad Additional authenticated data
|
|
199
|
+
* @returns Ciphertext || 16-byte Poly1305 tag
|
|
200
|
+
*/
|
|
131
201
|
export function xcEncrypt(x, key, nonce, plaintext, aad) {
|
|
132
202
|
const subkey = deriveSubkey(x, key, nonce);
|
|
133
203
|
const inner = innerNonce(nonce);
|
|
@@ -137,7 +207,17 @@ export function xcEncrypt(x, key, nonce, plaintext, aad) {
|
|
|
137
207
|
result.set(tag, ciphertext.length);
|
|
138
208
|
return result;
|
|
139
209
|
}
|
|
140
|
-
/**
|
|
210
|
+
/**
|
|
211
|
+
* XChaCha20-Poly1305 decrypt (draft-irtf-cfrg-xchacha).
|
|
212
|
+
* Derives HChaCha20 subkey, verifies the Poly1305 tag, then decrypts.
|
|
213
|
+
* Throws `AuthenticationError` on tag mismatch.
|
|
214
|
+
* @param x ChaCha20 WASM exports
|
|
215
|
+
* @param key 32-byte key
|
|
216
|
+
* @param nonce 24-byte nonce — must match the value used to encrypt
|
|
217
|
+
* @param ciphertext Ciphertext || 16-byte tag (combined format from `xcEncrypt`)
|
|
218
|
+
* @param aad Additional authenticated data
|
|
219
|
+
* @returns Plaintext
|
|
220
|
+
*/
|
|
141
221
|
export function xcDecrypt(x, key, nonce, ciphertext, aad) {
|
|
142
222
|
const ct = ciphertext.subarray(0, ciphertext.length - 16);
|
|
143
223
|
const tag = ciphertext.subarray(ciphertext.length - 16);
|