aegis-aead 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -7,6 +7,30 @@ A compact, zero-dependency JavaScript/TypeScript implementation of [AEGIS](https
7
7
 
8
8
  AEGIS provides both encryption with authentication and standalone MAC functionality, with a simple API that makes it hard to misuse.
9
9
 
10
+ ## Table of Contents
11
+
12
+ - [aegis-aead](#aegis-aead)
13
+ - [Table of Contents](#table-of-contents)
14
+ - [Installation](#installation)
15
+ - [Quick Start](#quick-start)
16
+ - [Choosing an Algorithm](#choosing-an-algorithm)
17
+ - [Usage Examples](#usage-examples)
18
+ - [Combined Mode](#combined-mode)
19
+ - [Detached Mode](#detached-mode)
20
+ - [In-Place Mode](#in-place-mode)
21
+ - [MAC (Message Authentication Code)](#mac-message-authentication-code)
22
+ - [API Overview](#api-overview)
23
+ - [Functions](#functions)
24
+ - [Parameters](#parameters)
25
+ - [Constants](#constants)
26
+ - [Parallel Variants (AEGIS-128X / AEGIS-256X)](#parallel-variants-aegis-128x--aegis-256x)
27
+ - [Security Considerations](#security-considerations)
28
+ - [Nonce Safety](#nonce-safety)
29
+ - [Tag Lengths](#tag-lengths)
30
+ - [Bitsliced Variants](#bitsliced-variants)
31
+ - [Compatibility](#compatibility)
32
+ - [Browser Example](#browser-example)
33
+
10
34
  ## Installation
11
35
 
12
36
  ```bash
@@ -15,32 +39,62 @@ bun add aegis-aead
15
39
  npm install aegis-aead
16
40
  ```
17
41
 
18
- ## Usage
19
-
20
- ### Encryption and Decryption
42
+ ## Quick Start
21
43
 
22
44
  ```typescript
23
- import {
24
- aegis128LCreateKey,
25
- aegis128LEncrypt,
26
- aegis128LDecrypt
27
- } from "aegis-aead";
45
+ import { aegis128LCreateKey, aegis128LEncrypt, aegis128LDecrypt } from "aegis-aead";
28
46
 
29
- const key = aegis128LCreateKey(); // 16 random bytes
47
+ const key = aegis128LCreateKey();
30
48
  const message = new TextEncoder().encode("Hello, world!");
31
49
  const associatedData = new TextEncoder().encode("metadata");
32
50
 
33
- // Encrypt - returns nonce || ciphertext || tag
34
- // A random nonce is generated automatically
51
+ // Encrypt (nonce is generated automatically)
35
52
  const sealed = aegis128LEncrypt(message, associatedData, key);
36
53
 
37
54
  // Decrypt (returns null if authentication fails)
38
55
  const decrypted = aegis128LDecrypt(sealed, associatedData, key);
39
56
  ```
40
57
 
58
+ ## Choosing an Algorithm
59
+
60
+ | Algorithm | Key | Nonce | Best For |
61
+ | ------------- | -------- | -------- | ---------------------------------------- |
62
+ | AEGIS-128L | 16 bytes | 16 bytes | General use, high throughput |
63
+ | AEGIS-256 | 32 bytes | 32 bytes | Large nonce, unlimited messages |
64
+ | AEGIS-128X | 16 bytes | 16 bytes | Interop with native SIMD implementations |
65
+ | AEGIS-256X | 32 bytes | 32 bytes | Interop + large nonce |
66
+ | AEGIS-128L-BS | 16 bytes | 16 bytes | Side-channel protection |
67
+ | AEGIS-256-BS | 32 bytes | 32 bytes | Side-channel + large nonce |
68
+
69
+ Recommendations:
70
+
71
+ - Default choice: AEGIS-128L offers excellent performance with safe random nonces up to 2^48 messages
72
+ - Unlimited messages: AEGIS-256 when you need unlimited random nonces (32-byte nonce eliminates collision risk)
73
+ - Interoperability: AEGIS-128X/256X when exchanging data with native implementations using these variants
74
+ - Hostile environments: Bitsliced variants (-BS) when attackers may observe timing
75
+
76
+ Note: The X variants are designed for SIMD parallelism in native code. In JavaScript they offer no speed benefit but are provided for interoperability.
77
+
78
+ ## Usage Examples
79
+
80
+ ### Combined Mode
81
+
82
+ The simplest API: returns `nonce || ciphertext || tag` in one buffer.
83
+
84
+ ```typescript
85
+ import { aegis128LCreateKey, aegis128LEncrypt, aegis128LDecrypt } from "aegis-aead";
86
+
87
+ const key = aegis128LCreateKey();
88
+ const message = new TextEncoder().encode("Hello, world!");
89
+ const ad = new TextEncoder().encode("metadata");
90
+
91
+ const sealed = aegis128LEncrypt(message, ad, key);
92
+ const decrypted = aegis128LDecrypt(sealed, ad, key);
93
+ ```
94
+
41
95
  ### Detached Mode
42
96
 
43
- For applications that need separate access to the ciphertext and tag:
97
+ When you need separate access to ciphertext and tag:
44
98
 
45
99
  ```typescript
46
100
  import {
@@ -53,278 +107,149 @@ import {
53
107
  const key = aegis128LCreateKey();
54
108
  const nonce = aegis128LCreateNonce();
55
109
  const message = new TextEncoder().encode("Hello, world!");
56
- const associatedData = new TextEncoder().encode("metadata");
110
+ const ad = new TextEncoder().encode("metadata");
57
111
 
58
- // Encrypt - returns ciphertext and tag separately
59
- const { ciphertext, tag } = aegis128LEncryptDetached(message, associatedData, key, nonce);
60
-
61
- // Decrypt
62
- const decrypted = aegis128LDecryptDetached(ciphertext, tag, associatedData, key, nonce);
112
+ const { ciphertext, tag } = aegis128LEncryptDetached(message, ad, key, nonce);
113
+ const decrypted = aegis128LDecryptDetached(ciphertext, tag, ad, key, nonce);
63
114
  ```
64
115
 
65
- ### MAC (Message Authentication Code)
116
+ ### In-Place Mode
117
+
118
+ Zero-copy encryption that modifies the buffer directly:
66
119
 
67
120
  ```typescript
68
121
  import {
69
122
  aegis128LCreateKey,
70
- aegis128LMac,
71
- aegis128LMacVerify
123
+ aegis128LCreateNonce,
124
+ aegis128LEncryptDetachedInPlace,
125
+ aegis128LDecryptDetachedInPlace
72
126
  } from "aegis-aead";
73
127
 
74
128
  const key = aegis128LCreateKey();
75
- const data = new TextEncoder().encode("data to authenticate");
129
+ const nonce = aegis128LCreateNonce();
130
+ const data = new TextEncoder().encode("Hello, world!");
131
+ const ad = new TextEncoder().encode("metadata");
76
132
 
77
- // Generate MAC (nonce defaults to zero if not provided)
78
- const tag = aegis128LMac(data, key);
133
+ // Encrypt: data is modified, tag is returned
134
+ const tag = aegis128LEncryptDetachedInPlace(data, ad, key, nonce);
79
135
 
80
- // Verify MAC
81
- const valid = aegis128LMacVerify(data, tag, key);
136
+ // Decrypt: returns true if authentication succeeds
137
+ const success = aegis128LDecryptDetachedInPlace(data, tag, ad, key, nonce);
82
138
  ```
83
139
 
84
- ## Algorithms
140
+ ### MAC (Message Authentication Code)
85
141
 
86
- | Algorithm | Key Size | Nonce Size | Block Size | Use Case |
87
- | ------------- | -------- | ---------- | ---------- | ---------------------------------- |
88
- | AEGIS-128L | 16 bytes | 16 bytes | 32 bytes | High throughput on 64-bit CPUs |
89
- | AEGIS-256 | 32 bytes | 32 bytes | 16 bytes | 256-bit security level |
90
- | AEGIS-128X | 16 bytes | 16 bytes | 32×D bytes | Multi-lane AEGIS-128L (D = degree) |
91
- | AEGIS-256X | 32 bytes | 32 bytes | 16×D bytes | Multi-lane AEGIS-256 (D = degree) |
92
- | AEGIS-128L-BS | 16 bytes | 16 bytes | 32 bytes | Bitsliced |
93
- | AEGIS-256-BS | 32 bytes | 32 bytes | 16 bytes | Bitsliced |
142
+ Authenticate data without encrypting:
94
143
 
95
- ### Bitsliced Variants
96
-
97
- The `-BS` (bitsliced) variants provide protection against cache-timing side-channel attacks at the cost of ~20% performance overhead. They process AES operations without lookup tables, making execution time independent of the key and data values.
144
+ ```typescript
145
+ import { aegis128LCreateKey, aegis128LMac, aegis128LMacVerify } from "aegis-aead";
98
146
 
99
- Use the bitsliced variants when:
100
- - Adversaries may be able to observe timing information (shared hosting, cloud VMs)
101
- - The key holder may encrypt or decrypt attacker-controlled data
102
- - Maximum side-channel resistance is required
147
+ const key = aegis128LCreateKey();
148
+ const data = new TextEncoder().encode("data to authenticate");
103
149
 
104
- For most use cases, the standard implementations are safe since AEGIS's continuous state mixing makes practical timing attacks extremely difficult.
150
+ const tag = aegis128LMac(data, key);
151
+ const valid = aegis128LMacVerify(data, tag, key);
152
+ ```
105
153
 
106
- ### Random Nonces
154
+ ## API Overview
107
155
 
108
- When using random nonces (the default for combined-mode functions):
156
+ All AEGIS variants follow the same API pattern. Replace `aegis128L` with your chosen algorithm (`aegis256`, `aegis128X2`, `aegis128X4`, `aegis256X2`, `aegis256X4`, `aegis128LBs`, `aegis256Bs`).
109
157
 
110
- - AEGIS-128L/128X: Safe for up to 2^48 messages per key, regardless of their size
111
- - AEGIS-256/256X: No practical limits on the number of messages per key
158
+ ### Functions
112
159
 
113
- ### Tag Lengths
160
+ | Function | Description |
161
+ | ------------------------------------------------------- | -------------------------------------------------- |
162
+ | `createKey()` | Generate a random key |
163
+ | `createNonce()` | Generate a random nonce |
164
+ | `encrypt(msg, ad, key, nonce?, tagLen?)` | Encrypt, returns `nonce \|\| ciphertext \|\| tag` |
165
+ | `decrypt(sealed, ad, key, tagLen?)` | Decrypt combined output, returns `null` on failure |
166
+ | `encryptDetached(msg, ad, key, nonce, tagLen?)` | Encrypt, returns `{ ciphertext, tag }` |
167
+ | `decryptDetached(ct, tag, ad, key, nonce)` | Decrypt detached, returns `null` on failure |
168
+ | `encryptDetachedInPlace(data, ad, key, nonce, tagLen?)` | Encrypt in-place, returns tag |
169
+ | `decryptDetachedInPlace(data, tag, ad, key, nonce)` | Decrypt in-place, returns `boolean` |
170
+ | `mac(data, key, nonce?, tagLen?)` | Generate MAC tag |
171
+ | `macVerify(data, tag, key, nonce?)` | Verify MAC tag |
114
172
 
115
- All algorithms support two tag lengths:
173
+ ### Parameters
116
174
 
117
- - 16 bytes (128-bit) - default
118
- - 32 bytes (256-bit) - pass `32` as the last parameter to encrypt/MAC functions
175
+ - msg/data: `Uint8Array` - Data to encrypt/authenticate
176
+ - ad: `Uint8Array` - Associated data (authenticated but not encrypted)
177
+ - key: `Uint8Array` - Encryption key (16 or 32 bytes depending on algorithm)
178
+ - nonce: `Uint8Array` - Number used once (auto-generated if omitted in combined mode)
179
+ - tagLen: `number` - Authentication tag length: `16` (default) or `32`
119
180
 
120
- ## API Reference
181
+ ### Constants
121
182
 
122
- ### AEGIS-128L
183
+ Each algorithm exports size constants:
123
184
 
124
185
  ```typescript
125
- // Key/Nonce generation
126
- aegis128LCreateKey(): Uint8Array // 16 random bytes
127
- aegis128LCreateNonce(): Uint8Array // 16 random bytes
128
-
129
- // Combined (nonce || ciphertext || tag)
130
- aegis128LEncrypt(msg, ad, key, nonce?, tagLen?): Uint8Array
131
- aegis128LDecrypt(sealed, ad, key, tagLen?): Uint8Array | null
132
-
133
- // Detached (separate ciphertext and tag)
134
- aegis128LEncryptDetached(msg, ad, key, nonce, tagLen?): { ciphertext, tag }
135
- aegis128LDecryptDetached(ciphertext, tag, ad, key, nonce): Uint8Array | null
136
-
137
- // MAC (nonce is optional, defaults to zero)
138
- aegis128LMac(data, key, nonce?, tagLen?): Uint8Array
139
- aegis128LMacVerify(data, tag, key, nonce?): boolean
140
-
141
- // Constants
142
- AEGIS_128L_KEY_SIZE // 16
143
- AEGIS_128L_NONCE_SIZE // 16
186
+ import { AEGIS_128L_KEY_SIZE, AEGIS_128L_NONCE_SIZE } from "aegis-aead";
187
+ // AEGIS_128L_KEY_SIZE = 16
188
+ // AEGIS_128L_NONCE_SIZE = 16
144
189
  ```
145
190
 
146
- ### AEGIS-256
147
-
148
- ```typescript
149
- // Key/Nonce generation
150
- aegis256CreateKey(): Uint8Array // 32 random bytes
151
- aegis256CreateNonce(): Uint8Array // 32 random bytes
191
+ ### Parallel Variants (AEGIS-128X / AEGIS-256X)
152
192
 
153
- // Combined (nonce || ciphertext || tag)
154
- aegis256Encrypt(msg, ad, key, nonce?, tagLen?): Uint8Array
155
- aegis256Decrypt(sealed, ad, key, tagLen?): Uint8Array | null
193
+ The X variants support a configurable degree of parallelism:
156
194
 
157
- // Detached (separate ciphertext and tag)
158
- aegis256EncryptDetached(msg, ad, key, nonce, tagLen?): { ciphertext, tag }
159
- aegis256DecryptDetached(ciphertext, tag, ad, key, nonce): Uint8Array | null
160
-
161
- // MAC (nonce is optional, defaults to zero)
162
- aegis256Mac(data, key, nonce?, tagLen?): Uint8Array
163
- aegis256MacVerify(data, tag, key, nonce?): boolean
195
+ ```typescript
196
+ // Pre-configured for degree 2 and 4
197
+ import { aegis128X2Encrypt, aegis128X4Encrypt } from "aegis-aead";
164
198
 
165
- // Constants
166
- AEGIS_256_KEY_SIZE // 32
167
- AEGIS_256_NONCE_SIZE // 32
199
+ // Or use custom degree (typically 2 or 4)
200
+ import { aegis128XEncrypt } from "aegis-aead";
201
+ const sealed = aegis128XEncrypt(msg, ad, key, nonce, 16, 4); // degree=4
168
202
  ```
169
203
 
170
- ### AEGIS-128X
204
+ ## Security Considerations
171
205
 
172
- Pre-configured variants for degree 2 and 4:
206
+ ### Nonce Safety
173
207
 
174
- ```typescript
175
- // Key/Nonce generation
176
- aegis128XCreateKey(): Uint8Array // 16 random bytes
177
- aegis128XCreateNonce(): Uint8Array // 16 random bytes
178
- aegis128X2CreateKey(): Uint8Array // alias
179
- aegis128X2CreateNonce(): Uint8Array
180
- aegis128X4CreateKey(): Uint8Array // alias
181
- aegis128X4CreateNonce(): Uint8Array
182
-
183
- // Combined (nonce || ciphertext || tag)
184
- aegis128X2Encrypt(msg, ad, key, nonce?, tagLen?): Uint8Array
185
- aegis128X2Decrypt(sealed, ad, key, tagLen?): Uint8Array | null
186
- aegis128X4Encrypt(msg, ad, key, nonce?, tagLen?): Uint8Array
187
- aegis128X4Decrypt(sealed, ad, key, tagLen?): Uint8Array | null
188
-
189
- // Detached (separate ciphertext and tag)
190
- aegis128X2EncryptDetached(msg, ad, key, nonce, tagLen?): { ciphertext, tag }
191
- aegis128X2DecryptDetached(ciphertext, tag, ad, key, nonce): Uint8Array | null
192
- aegis128X4EncryptDetached(msg, ad, key, nonce, tagLen?): { ciphertext, tag }
193
- aegis128X4DecryptDetached(ciphertext, tag, ad, key, nonce): Uint8Array | null
194
-
195
- // MAC (nonce is optional, defaults to zero)
196
- aegis128X2Mac(data, key, nonce?, tagLen?): Uint8Array
197
- aegis128X2MacVerify(data, tag, key, nonce?): boolean
198
- aegis128X4Mac(data, key, nonce?, tagLen?): Uint8Array
199
- aegis128X4MacVerify(data, tag, key, nonce?): boolean
200
-
201
- // Custom degree
202
- aegis128XEncrypt(msg, ad, key, nonce?, tagLen?, degree?): Uint8Array
203
- aegis128XDecrypt(sealed, ad, key, tagLen?, degree?): Uint8Array | null
204
- aegis128XEncryptDetached(msg, ad, key, nonce, tagLen?, degree?): { ciphertext, tag }
205
- aegis128XDecryptDetached(ciphertext, tag, ad, key, nonce, degree?): Uint8Array | null
206
- aegis128XMac(data, key, nonce?, tagLen?, degree?): Uint8Array
207
- aegis128XMacVerify(data, tag, key, nonce?, degree?): boolean
208
-
209
- // Constants
210
- AEGIS_128X_KEY_SIZE // 16
211
- AEGIS_128X_NONCE_SIZE // 16
212
- ```
208
+ - Combined mode generates random nonces automatically
209
+ - Detached mode requires you to provide nonces - never reuse a nonce with the same key
210
+ - AEGIS-128L/128X: Safe for up to 2^48 messages per key with random nonces
211
+ - AEGIS-256/256X: No practical limits on message count
213
212
 
214
- ### AEGIS-256X
213
+ ### Tag Lengths
215
214
 
216
- Pre-configured variants for degree 2 and 4:
215
+ All algorithms support 16-byte (128-bit) and 32-byte (256-bit) tags:
217
216
 
218
217
  ```typescript
219
- // Key/Nonce generation
220
- aegis256XCreateKey(): Uint8Array // 32 random bytes
221
- aegis256XCreateNonce(): Uint8Array // 32 random bytes
222
- aegis256X2CreateKey(): Uint8Array // alias
223
- aegis256X2CreateNonce(): Uint8Array
224
- aegis256X4CreateKey(): Uint8Array // alias
225
- aegis256X4CreateNonce(): Uint8Array
226
-
227
- // Combined (nonce || ciphertext || tag)
228
- aegis256X2Encrypt(msg, ad, key, nonce?, tagLen?): Uint8Array
229
- aegis256X2Decrypt(sealed, ad, key, tagLen?): Uint8Array | null
230
- aegis256X4Encrypt(msg, ad, key, nonce?, tagLen?): Uint8Array
231
- aegis256X4Decrypt(sealed, ad, key, tagLen?): Uint8Array | null
232
-
233
- // Detached (separate ciphertext and tag)
234
- aegis256X2EncryptDetached(msg, ad, key, nonce, tagLen?): { ciphertext, tag }
235
- aegis256X2DecryptDetached(ciphertext, tag, ad, key, nonce): Uint8Array | null
236
- aegis256X4EncryptDetached(msg, ad, key, nonce, tagLen?): { ciphertext, tag }
237
- aegis256X4DecryptDetached(ciphertext, tag, ad, key, nonce): Uint8Array | null
238
-
239
- // MAC (nonce is optional, defaults to zero)
240
- aegis256X2Mac(data, key, nonce?, tagLen?): Uint8Array
241
- aegis256X2MacVerify(data, tag, key, nonce?): boolean
242
- aegis256X4Mac(data, key, nonce?, tagLen?): Uint8Array
243
- aegis256X4MacVerify(data, tag, key, nonce?): boolean
244
-
245
- // Custom degree
246
- aegis256XEncrypt(msg, ad, key, nonce?, tagLen?, degree?): Uint8Array
247
- aegis256XDecrypt(sealed, ad, key, tagLen?, degree?): Uint8Array | null
248
- aegis256XEncryptDetached(msg, ad, key, nonce, tagLen?, degree?): { ciphertext, tag }
249
- aegis256XDecryptDetached(ciphertext, tag, ad, key, nonce, degree?): Uint8Array | null
250
- aegis256XMac(data, key, nonce?, tagLen?, degree?): Uint8Array
251
- aegis256XMacVerify(data, tag, key, nonce?, degree?): boolean
252
-
253
- // Constants
254
- AEGIS_256X_KEY_SIZE // 32
255
- AEGIS_256X_NONCE_SIZE // 32
218
+ // 32-byte tag
219
+ const sealed = aegis128LEncrypt(msg, ad, key, undefined, 32);
256
220
  ```
257
221
 
258
- ### AEGIS-128L-BS (Bitsliced)
259
-
260
- ```typescript
261
- // Key/Nonce generation
262
- aegis128LBsCreateKey(): Uint8Array // 16 random bytes
263
- aegis128LBsCreateNonce(): Uint8Array // 16 random bytes
222
+ ### Bitsliced Variants
264
223
 
265
- // Combined (nonce || ciphertext || tag)
266
- aegis128LBsEncrypt(msg, ad, key, nonce?, tagLen?): Uint8Array
267
- aegis128LBsDecrypt(sealed, ad, key, tagLen?): Uint8Array | null
224
+ The `-BS` variants use a constant-time bitsliced AES implementation that doesn't use lookup tables. This prevents cache-timing attacks at the cost of ~20% performance.
268
225
 
269
- // Detached (separate ciphertext and tag)
270
- aegis128LBsEncryptDetached(msg, ad, key, nonce, tagLen?): { ciphertext, tag }
271
- aegis128LBsDecryptDetached(ciphertext, tag, ad, key, nonce): Uint8Array | null
226
+ Use bitsliced variants when:
227
+ - Running on shared infrastructure (cloud VMs, containers)
228
+ - Attackers may observe timing information
229
+ - Processing attacker-controlled data with secret keys
272
230
 
273
- // MAC (nonce is optional, defaults to zero)
274
- aegis128LBsMac(data, key, nonce?, tagLen?): Uint8Array
275
- aegis128LBsMacVerify(data, tag, key, nonce?): boolean
231
+ For most applications, standard variants are safe since AEGIS's continuous state mixing makes timing attacks impractical.
276
232
 
277
- // Constants
278
- AEGIS_128L_BS_KEY_SIZE // 16
279
- AEGIS_128L_BS_NONCE_SIZE // 16
280
- ```
233
+ ## Compatibility
281
234
 
282
- ### AEGIS-256-BS (Bitsliced)
235
+ Runtime Requirements:
283
236
 
284
- ```typescript
285
- // Key/Nonce generation
286
- aegis256BsCreateKey(): Uint8Array // 32 random bytes
287
- aegis256BsCreateNonce(): Uint8Array // 32 random bytes
237
+ The library uses the Web Crypto API (`crypto.getRandomValues`) for key/nonce generation:
288
238
 
289
- // Combined (nonce || ciphertext || tag)
290
- aegis256BsEncrypt(msg, ad, key, nonce?, tagLen?): Uint8Array
291
- aegis256BsDecrypt(sealed, ad, key, tagLen?): Uint8Array | null
239
+ - All modern browsers
240
+ - Node.js 18+
241
+ - Deno
242
+ - Bun
292
243
 
293
- // Detached (separate ciphertext and tag)
294
- aegis256BsEncryptDetached(msg, ad, key, nonce, tagLen?): { ciphertext, tag }
295
- aegis256BsDecryptDetached(ciphertext, tag, ad, key, nonce): Uint8Array | null
244
+ Interoperability:
296
245
 
297
- // MAC (nonce is optional, defaults to zero)
298
- aegis256BsMac(data, key, nonce?, tagLen?): Uint8Array
299
- aegis256BsMacVerify(data, tag, key, nonce?): boolean
300
-
301
- // Constants
302
- AEGIS_256_BS_KEY_SIZE // 32
303
- AEGIS_256_BS_NONCE_SIZE // 32
304
- ```
246
+ This library implements the [AEGIS IETF draft specification](https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/) and interoperates with any compliant implementation. See the [full list of AEGIS implementations](https://github.com/cfrg/draft-irtf-cfrg-aegis-aead?tab=readme-ov-file#known-implementations).
305
247
 
306
248
  ## Browser Example
307
249
 
308
- A browser example is included in `examples/`. To build and run it:
250
+ A browser demo is included:
309
251
 
310
252
  ```bash
311
253
  bun run build:example
312
254
  open examples/index.html
313
255
  ```
314
-
315
- The example demonstrates encryption/decryption with a simple UI where you can enter a message, encrypt it, and decrypt it back.
316
-
317
- ## Compatibility
318
-
319
- The key/nonce generation functions use the Web Crypto API (`globalThis.crypto.getRandomValues`) which is available in:
320
-
321
- - All modern browsers
322
- - Node.js 18+
323
- - Deno
324
- - Bun
325
-
326
- ## Interoperability
327
-
328
- This library follows the [AEGIS IETF draft specification](https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/) and can exchange encrypted messages with any compliant implementation, including native libraries in C, Rust, Go, Zig, and more.
329
-
330
- See the [full list of AEGIS implementations](https://github.com/cfrg/draft-irtf-cfrg-aegis-aead?tab=readme-ov-file#known-implementations).
@@ -70,6 +70,16 @@ export declare class Aegis128LBsState {
70
70
  * @returns 32-byte plaintext block
71
71
  */
72
72
  dec(ci: Uint8Array): Uint8Array;
73
+ /**
74
+ * Encrypts a 32-byte plaintext block in-place.
75
+ * @param block - 32-byte buffer (plaintext in, ciphertext out)
76
+ */
77
+ encInPlace(block: Uint8Array): void;
78
+ /**
79
+ * Decrypts a 32-byte ciphertext block in-place.
80
+ * @param block - 32-byte buffer (ciphertext in, plaintext out)
81
+ */
82
+ decInPlace(block: Uint8Array): void;
73
83
  /**
74
84
  * Decrypts a partial (final) ciphertext block smaller than 32 bytes.
75
85
  * @param cn - Partial ciphertext block (1-31 bytes)
@@ -108,6 +118,28 @@ export declare function aegis128LBsEncryptDetached(msg: Uint8Array, ad: Uint8Arr
108
118
  * @returns Decrypted plaintext, or null if authentication fails
109
119
  */
110
120
  export declare function aegis128LBsDecryptDetached(ct: Uint8Array, tag: Uint8Array, ad: Uint8Array, key: Uint8Array, nonce: Uint8Array): Uint8Array | null;
121
+ /**
122
+ * Encrypts a message in-place using bitsliced AEGIS-128L (detached mode).
123
+ * The input buffer is modified to contain the ciphertext.
124
+ * @param data - Buffer containing plaintext (will be overwritten with ciphertext)
125
+ * @param ad - Associated data (authenticated but not encrypted)
126
+ * @param key - 16-byte encryption key
127
+ * @param nonce - 16-byte nonce (must be unique per message with the same key)
128
+ * @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
129
+ * @returns Authentication tag
130
+ */
131
+ export declare function aegis128LBsEncryptDetachedInPlace(data: Uint8Array, ad: Uint8Array, key: Uint8Array, nonce: Uint8Array, tagLen?: 16 | 32): Uint8Array;
132
+ /**
133
+ * Decrypts a message in-place using bitsliced AEGIS-128L (detached mode).
134
+ * The input buffer is modified to contain the plaintext (or zeroed on failure).
135
+ * @param data - Buffer containing ciphertext (will be overwritten with plaintext)
136
+ * @param tag - Authentication tag (16 or 32 bytes)
137
+ * @param ad - Associated data (must match what was used during encryption)
138
+ * @param key - 16-byte encryption key
139
+ * @param nonce - 16-byte nonce (must match what was used during encryption)
140
+ * @returns True if authentication succeeds, false otherwise
141
+ */
142
+ export declare function aegis128LBsDecryptDetachedInPlace(data: Uint8Array, tag: Uint8Array, ad: Uint8Array, key: Uint8Array, nonce: Uint8Array): boolean;
111
143
  export declare const AEGIS_128L_BS_NONCE_SIZE = 16;
112
144
  export declare const AEGIS_128L_BS_KEY_SIZE = 16;
113
145
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"aegis128l-bs.d.ts","sourceRoot":"","sources":["../src/aegis128l-bs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aegis128l-bs.d.ts","sourceRoot":"","sources":["../src/aegis128l-bs.ts"],"names":[],"mappings":"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"}
@@ -215,6 +215,20 @@ export class Aegis128LBsState {
215
215
  this.decTo(ci, out);
216
216
  return out;
217
217
  }
218
+ /**
219
+ * Encrypts a 32-byte plaintext block in-place.
220
+ * @param block - 32-byte buffer (plaintext in, ciphertext out)
221
+ */
222
+ encInPlace(block) {
223
+ this.encTo(block, block);
224
+ }
225
+ /**
226
+ * Decrypts a 32-byte ciphertext block in-place.
227
+ * @param block - 32-byte buffer (ciphertext in, plaintext out)
228
+ */
229
+ decInPlace(block) {
230
+ this.decTo(block, block);
231
+ }
218
232
  /**
219
233
  * Decrypts a partial (final) ciphertext block smaller than 32 bytes.
220
234
  * @param cn - Partial ciphertext block (1-31 bytes)
@@ -383,6 +397,71 @@ export function aegis128LBsDecryptDetached(ct, tag, ad, key, nonce) {
383
397
  }
384
398
  return msg;
385
399
  }
400
+ /**
401
+ * Encrypts a message in-place using bitsliced AEGIS-128L (detached mode).
402
+ * The input buffer is modified to contain the ciphertext.
403
+ * @param data - Buffer containing plaintext (will be overwritten with ciphertext)
404
+ * @param ad - Associated data (authenticated but not encrypted)
405
+ * @param key - 16-byte encryption key
406
+ * @param nonce - 16-byte nonce (must be unique per message with the same key)
407
+ * @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
408
+ * @returns Authentication tag
409
+ */
410
+ export function aegis128LBsEncryptDetachedInPlace(data, ad, key, nonce, tagLen = 16) {
411
+ const state = new Aegis128LBsState();
412
+ state.init(key, nonce);
413
+ const adPadded = zeroPad(ad, RATE);
414
+ for (let i = 0; i + RATE <= adPadded.length; i += RATE) {
415
+ state.absorb(adPadded.subarray(i, i + RATE));
416
+ }
417
+ const msgLen = data.length;
418
+ const fullBlocksLen = Math.floor(msgLen / RATE) * RATE;
419
+ for (let i = 0; i < fullBlocksLen; i += RATE) {
420
+ state.encInPlace(data.subarray(i, i + RATE));
421
+ }
422
+ if (msgLen > fullBlocksLen) {
423
+ const lastPartial = data.subarray(fullBlocksLen);
424
+ const lastBlock = zeroPad(lastPartial, RATE);
425
+ const encBlock = state.enc(lastBlock);
426
+ lastPartial.set(encBlock.subarray(0, lastPartial.length));
427
+ }
428
+ return state.finalize(ad.length, msgLen, tagLen);
429
+ }
430
+ /**
431
+ * Decrypts a message in-place using bitsliced AEGIS-128L (detached mode).
432
+ * The input buffer is modified to contain the plaintext (or zeroed on failure).
433
+ * @param data - Buffer containing ciphertext (will be overwritten with plaintext)
434
+ * @param tag - Authentication tag (16 or 32 bytes)
435
+ * @param ad - Associated data (must match what was used during encryption)
436
+ * @param key - 16-byte encryption key
437
+ * @param nonce - 16-byte nonce (must match what was used during encryption)
438
+ * @returns True if authentication succeeds, false otherwise
439
+ */
440
+ export function aegis128LBsDecryptDetachedInPlace(data, tag, ad, key, nonce) {
441
+ const tagLen = tag.length;
442
+ const state = new Aegis128LBsState();
443
+ state.init(key, nonce);
444
+ const adPadded = zeroPad(ad, RATE);
445
+ for (let i = 0; i + RATE <= adPadded.length; i += RATE) {
446
+ state.absorb(adPadded.subarray(i, i + RATE));
447
+ }
448
+ const msgLen = data.length;
449
+ const fullBlocksLen = Math.floor(msgLen / RATE) * RATE;
450
+ for (let i = 0; i < fullBlocksLen; i += RATE) {
451
+ state.decInPlace(data.subarray(i, i + RATE));
452
+ }
453
+ if (msgLen > fullBlocksLen) {
454
+ const lastPartial = data.subarray(fullBlocksLen);
455
+ const decrypted = state.decPartial(lastPartial);
456
+ lastPartial.set(decrypted);
457
+ }
458
+ const expectedTag = state.finalize(ad.length, msgLen, tagLen);
459
+ if (!constantTimeEqual(tag, expectedTag)) {
460
+ data.fill(0);
461
+ return false;
462
+ }
463
+ return true;
464
+ }
386
465
  export const AEGIS_128L_BS_NONCE_SIZE = 16;
387
466
  export const AEGIS_128L_BS_KEY_SIZE = 16;
388
467
  /**