aegis-aead 0.2.1 → 0.2.2

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.
@@ -10,12 +10,16 @@ import {
10
10
  aesRound,
11
11
  blockFromBytes,
12
12
  blocksPut,
13
+ blocksRotr6,
14
+ blocksXor,
13
15
  blockToBytes,
14
16
  blockXor,
15
17
  createAesBlock,
16
18
  createAesBlocks,
17
19
  pack,
20
+ pack04_6,
18
21
  unpack,
22
+ unpack04_6,
19
23
  wordIdx,
20
24
  } from "./aes-bs.js";
21
25
  import { randomBytes } from "./random.js";
@@ -31,58 +35,68 @@ const C1: AesBlock = new Uint32Array([
31
35
 
32
36
  /**
33
37
  * Bitsliced AEGIS-256 cipher state.
34
- * Uses 6 AES blocks (96 bytes) stored in bitsliced form.
38
+ * Uses 6 AES blocks stored in packed bitsliced form throughout the lifetime
39
+ * of the state.
35
40
  */
36
41
  export class Aegis256BsState {
37
42
  private st: AesBlocks;
38
43
  private st1: AesBlocks;
44
+ private constantInput: AesBlocks;
39
45
  private tmp: AesBlock;
46
+ private z: AesBlock;
40
47
 
41
48
  constructor() {
42
49
  this.st = createAesBlocks();
43
50
  this.st1 = createAesBlocks();
51
+ this.constantInput = createAesBlocks();
44
52
  this.tmp = createAesBlock();
53
+ this.z = createAesBlock();
45
54
  }
46
55
 
47
- /**
48
- * AEGIS round function: applies AES round to all blocks and rotates.
49
- * st[i] = AES(st[i]) ^ st[(i+5) mod 6]
50
- */
51
- private aegisRound(): void {
56
+ private aegisRoundPacked(): void {
52
57
  const st = this.st;
53
58
  const st1 = this.st1;
54
59
 
55
60
  st1.set(st);
56
- pack(st1);
57
61
  aesRound(st1);
58
- unpack(st1);
59
-
60
- for (let i = 0; i < 6; i++) {
61
- const prev = (i + 5) % 6;
62
- st[wordIdx(i, 0)] = (st[wordIdx(i, 0)]! ^ st1[wordIdx(prev, 0)]!) >>> 0;
63
- st[wordIdx(i, 1)] = (st[wordIdx(i, 1)]! ^ st1[wordIdx(prev, 1)]!) >>> 0;
64
- st[wordIdx(i, 2)] = (st[wordIdx(i, 2)]! ^ st1[wordIdx(prev, 2)]!) >>> 0;
65
- st[wordIdx(i, 3)] = (st[wordIdx(i, 3)]! ^ st1[wordIdx(prev, 3)]!) >>> 0;
66
- }
62
+ blocksRotr6(st1);
63
+ blocksXor(st, st1);
64
+ blocksXor(st, this.constantInput);
67
65
  }
68
66
 
69
- /**
70
- * Absorb rate: XOR message block into state position 0.
71
- */
72
- private absorbRate(m: AesBlock): void {
73
- const st = this.st;
74
- st[wordIdx(0, 0)] = (st[wordIdx(0, 0)]! ^ m[0]!) >>> 0;
75
- st[wordIdx(0, 1)] = (st[wordIdx(0, 1)]! ^ m[1]!) >>> 0;
76
- st[wordIdx(0, 2)] = (st[wordIdx(0, 2)]! ^ m[2]!) >>> 0;
77
- st[wordIdx(0, 3)] = (st[wordIdx(0, 3)]! ^ m[3]!) >>> 0;
67
+ private packConstantInput(m: AesBlock): void {
68
+ const out = this.constantInput;
69
+ out.fill(0);
70
+ blocksPut(out, m, 0);
71
+ pack04_6(out);
78
72
  }
79
73
 
80
74
  /**
81
- * Update state with a message block.
75
+ * Extract the keystream block z directly from the packed state.
76
+ *
77
+ * z = S1 ^ S4 ^ S5 ^ (S2 & S3)
78
+ *
79
+ * In the packed layout, logical block j lives at bit position (7 - j) of
80
+ * every byte, so S1 = bit 6, S4 = bit 3, S5 = bit 2, S2 = bit 5,
81
+ * S3 = bit 4. The combination is computed lane-wise into bit position 7
82
+ * and unpack04_6 then extracts the AES block at logical index 0.
82
83
  */
83
- private update(m: AesBlock): void {
84
- this.aegisRound();
85
- this.absorbRate(m);
84
+ private keystreamPacked(): void {
85
+ const st = this.st;
86
+ const zPacked = this.st1;
87
+ for (let i = 0; i < 32; i++) {
88
+ const x = st[i]!;
89
+ zPacked[i] =
90
+ ((x & 0x40404040) << 1) ^
91
+ ((x & 0x08080808) << 4) ^
92
+ ((x & 0x04040404) << 5) ^
93
+ ((x & (x >>> 1) & 0x10101010) << 3);
94
+ }
95
+ unpack04_6(zPacked);
96
+ const z = this.z;
97
+ for (let i = 0; i < 4; i++) {
98
+ z[i] = zPacked[wordIdx(0, i)]!;
99
+ }
86
100
  }
87
101
 
88
102
  /**
@@ -117,57 +131,45 @@ export class Aegis256BsState {
117
131
  blocksPut(this.st, k0c0, 4);
118
132
  blocksPut(this.st, k1c1, 5);
119
133
 
134
+ pack(this.st);
120
135
  for (let i = 0; i < 4; i++) {
121
- this.update(k0);
122
- this.update(k1);
123
- this.update(k0n0);
124
- this.update(k1n1);
136
+ this.packConstantInput(k0);
137
+ this.aegisRoundPacked();
138
+ this.packConstantInput(k1);
139
+ this.aegisRoundPacked();
140
+ this.packConstantInput(k0n0);
141
+ this.aegisRoundPacked();
142
+ this.packConstantInput(k1n1);
143
+ this.aegisRoundPacked();
125
144
  }
126
145
  }
127
146
 
128
147
  /**
129
148
  * Absorbs a 16-byte associated data block into the state.
130
- * @param ai - 16-byte associated data block
131
149
  */
132
150
  absorb(ai: Uint8Array): void {
133
151
  const msg = this.tmp;
134
152
  blockFromBytes(msg, ai);
135
- this.update(msg);
153
+ this.packConstantInput(msg);
154
+ this.aegisRoundPacked();
136
155
  }
137
156
 
138
157
  /**
139
158
  * Encrypts a 16-byte plaintext block and writes to output buffer.
140
- * @param xi - 16-byte plaintext block
141
- * @param out - 16-byte output buffer
142
159
  */
143
160
  encTo(xi: Uint8Array, out: Uint8Array): void {
144
- const st = this.st;
145
- const z = this.tmp;
146
- const t = createAesBlock();
161
+ const t = this.tmp;
147
162
 
148
- for (let i = 0; i < 4; i++) {
149
- z[i] =
150
- (st[wordIdx(1, i)]! ^
151
- st[wordIdx(4, i)]! ^
152
- st[wordIdx(5, i)]! ^
153
- (st[wordIdx(2, i)]! & st[wordIdx(3, i)]!)) >>>
154
- 0;
155
- }
163
+ this.keystreamPacked();
156
164
 
157
165
  blockFromBytes(t, xi);
166
+ this.packConstantInput(t);
167
+ this.aegisRoundPacked();
158
168
 
159
- const outBlock = createAesBlock();
160
- blockXor(outBlock, t, z);
161
- blockToBytes(out, outBlock);
162
-
163
- this.update(t);
169
+ blockXor(t, t, this.z);
170
+ blockToBytes(out, t);
164
171
  }
165
172
 
166
- /**
167
- * Encrypts a 16-byte plaintext block.
168
- * @param xi - 16-byte plaintext block
169
- * @returns 16-byte ciphertext block
170
- */
171
173
  enc(xi: Uint8Array): Uint8Array {
172
174
  const out = new Uint8Array(16);
173
175
  this.encTo(xi, out);
@@ -176,77 +178,42 @@ export class Aegis256BsState {
176
178
 
177
179
  /**
178
180
  * Decrypts a 16-byte ciphertext block and writes to output buffer.
179
- * @param ci - 16-byte ciphertext block
180
- * @param out - 16-byte output buffer
181
181
  */
182
182
  decTo(ci: Uint8Array, out: Uint8Array): void {
183
- const st = this.st;
184
183
  const msg = this.tmp;
185
184
 
186
185
  blockFromBytes(msg, ci);
186
+ this.keystreamPacked();
187
+ blockXor(msg, msg, this.z);
187
188
 
188
- for (let i = 0; i < 4; i++) {
189
- msg[i] =
190
- (msg[i]! ^
191
- st[wordIdx(1, i)]! ^
192
- st[wordIdx(4, i)]! ^
193
- st[wordIdx(5, i)]! ^
194
- (st[wordIdx(2, i)]! & st[wordIdx(3, i)]!)) >>>
195
- 0;
196
- }
189
+ this.packConstantInput(msg);
190
+ this.aegisRoundPacked();
197
191
 
198
- this.update(msg);
199
192
  blockToBytes(out, msg);
200
193
  }
201
194
 
202
- /**
203
- * Decrypts a 16-byte ciphertext block.
204
- * @param ci - 16-byte ciphertext block
205
- * @returns 16-byte plaintext block
206
- */
207
195
  dec(ci: Uint8Array): Uint8Array {
208
196
  const out = new Uint8Array(16);
209
197
  this.decTo(ci, out);
210
198
  return out;
211
199
  }
212
200
 
213
- /**
214
- * Encrypts a 16-byte plaintext block in-place.
215
- * @param block - 16-byte buffer (plaintext in, ciphertext out)
216
- */
217
201
  encInPlace(block: Uint8Array): void {
218
202
  this.encTo(block, block);
219
203
  }
220
204
 
221
- /**
222
- * Decrypts a 16-byte ciphertext block in-place.
223
- * @param block - 16-byte buffer (ciphertext in, plaintext out)
224
- */
225
205
  decInPlace(block: Uint8Array): void {
226
206
  this.decTo(block, block);
227
207
  }
228
208
 
229
- /**
230
- * Decrypts a partial (final) ciphertext block smaller than 16 bytes.
231
- * @param cn - Partial ciphertext block (1-15 bytes)
232
- * @returns Decrypted plaintext of the same length
233
- */
234
209
  decPartial(cn: Uint8Array): Uint8Array {
235
- const st = this.st;
236
210
  const msg = this.tmp;
237
211
 
238
212
  const padded = zeroPad(cn, RATE);
239
213
  blockFromBytes(msg, padded);
240
214
 
241
- for (let i = 0; i < 4; i++) {
242
- msg[i] =
243
- (msg[i]! ^
244
- st[wordIdx(1, i)]! ^
245
- st[wordIdx(4, i)]! ^
246
- st[wordIdx(5, i)]! ^
247
- (st[wordIdx(2, i)]! & st[wordIdx(3, i)]!)) >>>
248
- 0;
249
- }
215
+ this.keystreamPacked();
216
+ blockXor(msg, msg, this.z);
250
217
 
251
218
  const pad = new Uint8Array(RATE);
252
219
  blockToBytes(pad, msg);
@@ -256,18 +223,14 @@ export class Aegis256BsState {
256
223
  pad.fill(0, cn.length);
257
224
  blockFromBytes(msg, pad);
258
225
 
259
- this.aegisRound();
260
- this.absorbRate(msg);
226
+ this.packConstantInput(msg);
227
+ this.aegisRoundPacked();
261
228
 
262
229
  return xn;
263
230
  }
264
231
 
265
232
  /**
266
233
  * Finalizes encryption/decryption and produces an authentication tag.
267
- * @param adLen - Associated data length in bytes
268
- * @param msgLen - Message length in bytes
269
- * @param tagLen - Tag length (16 or 32 bytes)
270
- * @returns Authentication tag
271
234
  */
272
235
  finalize(adLen: number, msgLen: number, tagLen: 16 | 32 = 16): Uint8Array {
273
236
  const st = this.st;
@@ -278,14 +241,20 @@ export class Aegis256BsState {
278
241
  tmp[2] = ((msgLen * 8) & 0xffffffff) >>> 0;
279
242
  tmp[3] = Math.floor((msgLen * 8) / 0x100000000) >>> 0;
280
243
 
281
- tmp[0] = (tmp[0]! ^ st[wordIdx(3, 0)]!) >>> 0;
282
- tmp[1] = (tmp[1]! ^ st[wordIdx(3, 1)]!) >>> 0;
283
- tmp[2] = (tmp[2]! ^ st[wordIdx(3, 2)]!) >>> 0;
284
- tmp[3] = (tmp[3]! ^ st[wordIdx(3, 3)]!) >>> 0;
244
+ const unpacked = this.st1;
245
+ unpacked.set(st);
246
+ unpack(unpacked);
285
247
 
248
+ tmp[0] = (tmp[0]! ^ unpacked[wordIdx(3, 0)]!) >>> 0;
249
+ tmp[1] = (tmp[1]! ^ unpacked[wordIdx(3, 1)]!) >>> 0;
250
+ tmp[2] = (tmp[2]! ^ unpacked[wordIdx(3, 2)]!) >>> 0;
251
+ tmp[3] = (tmp[3]! ^ unpacked[wordIdx(3, 3)]!) >>> 0;
252
+
253
+ this.packConstantInput(tmp);
286
254
  for (let i = 0; i < 7; i++) {
287
- this.update(tmp);
255
+ this.aegisRoundPacked();
288
256
  }
257
+ unpack(st);
289
258
 
290
259
  if (tagLen === 16) {
291
260
  const tag = new Uint8Array(16);
@@ -323,12 +292,6 @@ export class Aegis256BsState {
323
292
 
324
293
  /**
325
294
  * Encrypts a message using bitsliced AEGIS-256 (detached mode).
326
- * @param msg - Plaintext message
327
- * @param ad - Associated data (authenticated but not encrypted)
328
- * @param key - 32-byte encryption key
329
- * @param nonce - 32-byte nonce (must be unique per message with the same key)
330
- * @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
331
- * @returns Object containing ciphertext and authentication tag separately
332
295
  */
333
296
  export function aegis256BsEncryptDetached(
334
297
  msg: Uint8Array,
@@ -365,12 +328,6 @@ export function aegis256BsEncryptDetached(
365
328
 
366
329
  /**
367
330
  * Decrypts a message using bitsliced AEGIS-256 (detached mode).
368
- * @param ct - Ciphertext
369
- * @param tag - Authentication tag (16 or 32 bytes)
370
- * @param ad - Associated data (must match what was used during encryption)
371
- * @param key - 32-byte encryption key
372
- * @param nonce - 32-byte nonce (must match what was used during encryption)
373
- * @returns Decrypted plaintext, or null if authentication fails
374
331
  */
375
332
  export function aegis256BsDecryptDetached(
376
333
  ct: Uint8Array,
@@ -409,16 +366,6 @@ export function aegis256BsDecryptDetached(
409
366
  return msg;
410
367
  }
411
368
 
412
- /**
413
- * Encrypts a message in-place using bitsliced AEGIS-256 (detached mode).
414
- * The input buffer is modified to contain the ciphertext.
415
- * @param data - Buffer containing plaintext (will be overwritten with ciphertext)
416
- * @param ad - Associated data (authenticated but not encrypted)
417
- * @param key - 32-byte encryption key
418
- * @param nonce - 32-byte nonce (must be unique per message with the same key)
419
- * @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
420
- * @returns Authentication tag
421
- */
422
369
  export function aegis256BsEncryptDetachedInPlace(
423
370
  data: Uint8Array,
424
371
  ad: Uint8Array,
@@ -451,16 +398,6 @@ export function aegis256BsEncryptDetachedInPlace(
451
398
  return state.finalize(ad.length, msgLen, tagLen);
452
399
  }
453
400
 
454
- /**
455
- * Decrypts a message in-place using bitsliced AEGIS-256 (detached mode).
456
- * The input buffer is modified to contain the plaintext (or zeroed on failure).
457
- * @param data - Buffer containing ciphertext (will be overwritten with plaintext)
458
- * @param tag - Authentication tag (16 or 32 bytes)
459
- * @param ad - Associated data (must match what was used during encryption)
460
- * @param key - 32-byte encryption key
461
- * @param nonce - 32-byte nonce (must match what was used during encryption)
462
- * @returns True if authentication succeeds, false otherwise
463
- */
464
401
  export function aegis256BsDecryptDetachedInPlace(
465
402
  data: Uint8Array,
466
403
  tag: Uint8Array,
@@ -503,16 +440,6 @@ export function aegis256BsDecryptDetachedInPlace(
503
440
  export const AEGIS_256_BS_NONCE_SIZE = 32;
504
441
  export const AEGIS_256_BS_KEY_SIZE = 32;
505
442
 
506
- /**
507
- * Encrypts a message using bitsliced AEGIS-256.
508
- * Returns a single buffer containing nonce || ciphertext || tag.
509
- * @param msg - Plaintext message
510
- * @param ad - Associated data (authenticated but not encrypted)
511
- * @param key - 32-byte encryption key
512
- * @param nonce - 32-byte nonce (optional, generates random nonce if not provided)
513
- * @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
514
- * @returns Concatenated nonce || ciphertext || tag
515
- */
516
443
  export function aegis256BsEncrypt(
517
444
  msg: Uint8Array,
518
445
  ad: Uint8Array,
@@ -539,15 +466,6 @@ export function aegis256BsEncrypt(
539
466
  return result;
540
467
  }
541
468
 
542
- /**
543
- * Decrypts a message using bitsliced AEGIS-256.
544
- * Expects input as nonce || ciphertext || tag.
545
- * @param sealed - Concatenated nonce || ciphertext || tag
546
- * @param ad - Associated data (must match what was used during encryption)
547
- * @param key - 32-byte encryption key
548
- * @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
549
- * @returns Decrypted plaintext, or null if authentication fails
550
- */
551
469
  export function aegis256BsDecrypt(
552
470
  sealed: Uint8Array,
553
471
  ad: Uint8Array,
@@ -564,14 +482,6 @@ export function aegis256BsDecrypt(
564
482
  return aegis256BsDecryptDetached(ct, tag, ad, key, nonce);
565
483
  }
566
484
 
567
- /**
568
- * Computes a MAC (Message Authentication Code) using bitsliced AEGIS-256.
569
- * @param data - Data to authenticate
570
- * @param key - 32-byte key
571
- * @param nonce - 32-byte nonce (optional, uses zero nonce if null)
572
- * @param tagLen - Tag length: 16 or 32 bytes (default: 16)
573
- * @returns Authentication tag
574
- */
575
485
  export function aegis256BsMac(
576
486
  data: Uint8Array,
577
487
  key: Uint8Array,
@@ -589,14 +499,6 @@ export function aegis256BsMac(
589
499
  return state.finalize(data.length, tagLen, tagLen);
590
500
  }
591
501
 
592
- /**
593
- * Verifies a MAC computed using bitsliced AEGIS-256.
594
- * @param data - Data to verify
595
- * @param tag - Expected authentication tag (16 or 32 bytes)
596
- * @param key - 32-byte key
597
- * @param nonce - 32-byte nonce (optional, uses zero nonce if null)
598
- * @returns True if the tag is valid, false otherwise
599
- */
600
502
  export function aegis256BsMacVerify(
601
503
  data: Uint8Array,
602
504
  tag: Uint8Array,
@@ -608,18 +510,10 @@ export function aegis256BsMacVerify(
608
510
  return constantTimeEqual(tag, expectedTag);
609
511
  }
610
512
 
611
- /**
612
- * Generates a random 32-byte key for bitsliced AEGIS-256.
613
- * @returns 32-byte encryption key
614
- */
615
513
  export function aegis256BsCreateKey(): Uint8Array {
616
514
  return randomBytes(AEGIS_256_BS_KEY_SIZE);
617
515
  }
618
516
 
619
- /**
620
- * Generates a random 32-byte nonce for bitsliced AEGIS-256.
621
- * @returns 32-byte nonce
622
- */
623
517
  export function aegis256BsCreateNonce(): Uint8Array {
624
518
  return randomBytes(AEGIS_256_BS_NONCE_SIZE);
625
519
  }