aegis-aead 0.2.0 → 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.
- package/README.md +155 -228
- package/dist/aegis128l-bs.d.ts +23 -48
- package/dist/aegis128l-bs.d.ts.map +1 -1
- package/dist/aegis128l-bs.js +124 -152
- package/dist/aegis128l-bs.js.map +1 -1
- package/dist/aegis128l.d.ts +32 -0
- package/dist/aegis128l.d.ts.map +1 -1
- package/dist/aegis128l.js +94 -0
- package/dist/aegis128l.js.map +1 -1
- package/dist/aegis128x.d.ts +42 -0
- package/dist/aegis128x.d.ts.map +1 -1
- package/dist/aegis128x.js +100 -0
- package/dist/aegis128x.js.map +1 -1
- package/dist/aegis256-bs.d.ts +19 -91
- package/dist/aegis256-bs.d.ts.map +1 -1
- package/dist/aegis256-bs.js +124 -156
- package/dist/aegis256-bs.js.map +1 -1
- package/dist/aegis256.d.ts +32 -0
- package/dist/aegis256.d.ts.map +1 -1
- package/dist/aegis256.js +79 -0
- package/dist/aegis256.js.map +1 -1
- package/dist/aegis256x.d.ts +42 -0
- package/dist/aegis256x.d.ts.map +1 -1
- package/dist/aegis256x.js +91 -0
- package/dist/aegis256x.js.map +1 -1
- package/dist/aes-bs.d.ts +16 -0
- package/dist/aes-bs.d.ts.map +1 -1
- package/dist/aes-bs.js +173 -99
- package/dist/aes-bs.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/aegis128l-bs.ts +154 -156
- package/src/aegis128l.ts +126 -0
- package/src/aegis128x.ts +168 -0
- package/src/aegis256-bs.ts +158 -157
- package/src/aegis256.ts +111 -0
- package/src/aegis256x.ts +155 -0
- package/src/aes-bs.ts +187 -106
- package/src/index.ts +20 -0
package/src/aegis128l-bs.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
pack,
|
|
20
20
|
pack04,
|
|
21
21
|
unpack,
|
|
22
|
+
unpack04,
|
|
22
23
|
wordIdx,
|
|
23
24
|
} from "./aes-bs.js";
|
|
24
25
|
import { randomBytes } from "./random.js";
|
|
@@ -34,11 +35,13 @@ const C1: AesBlock = new Uint32Array([
|
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* Bitsliced AEGIS-128L cipher state.
|
|
37
|
-
*
|
|
38
|
+
* The state is kept in packed bitsliced form between operations so that
|
|
39
|
+
* pack/unpack does not need to be applied at every round.
|
|
38
40
|
*/
|
|
39
41
|
export class Aegis128LBsState {
|
|
40
42
|
private st: AesBlocks;
|
|
41
43
|
private st1: AesBlocks;
|
|
44
|
+
private constantInput: AesBlocks;
|
|
42
45
|
private tmp0: AesBlock;
|
|
43
46
|
private tmp1: AesBlock;
|
|
44
47
|
private z0: AesBlock;
|
|
@@ -47,38 +50,14 @@ export class Aegis128LBsState {
|
|
|
47
50
|
constructor() {
|
|
48
51
|
this.st = createAesBlocks();
|
|
49
52
|
this.st1 = createAesBlocks();
|
|
53
|
+
this.constantInput = createAesBlocks();
|
|
50
54
|
this.tmp0 = createAesBlock();
|
|
51
55
|
this.tmp1 = createAesBlock();
|
|
52
56
|
this.z0 = createAesBlock();
|
|
53
57
|
this.z1 = createAesBlock();
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
* AEGIS round function: applies AES round to all blocks and rotates.
|
|
58
|
-
* st[i] = AES(st[i]) ^ st[(i-1) mod 8]
|
|
59
|
-
*/
|
|
60
|
-
private aegisRound(): void {
|
|
61
|
-
const st = this.st;
|
|
62
|
-
const st1 = this.st1;
|
|
63
|
-
|
|
64
|
-
st1.set(st);
|
|
65
|
-
pack(st1);
|
|
66
|
-
aesRound(st1);
|
|
67
|
-
unpack(st1);
|
|
68
|
-
|
|
69
|
-
for (let i = 0; i < 8; i++) {
|
|
70
|
-
const prev = (i + 7) % 8;
|
|
71
|
-
st[wordIdx(i, 0)] = (st[wordIdx(i, 0)]! ^ st1[wordIdx(prev, 0)]!) >>> 0;
|
|
72
|
-
st[wordIdx(i, 1)] = (st[wordIdx(i, 1)]! ^ st1[wordIdx(prev, 1)]!) >>> 0;
|
|
73
|
-
st[wordIdx(i, 2)] = (st[wordIdx(i, 2)]! ^ st1[wordIdx(prev, 2)]!) >>> 0;
|
|
74
|
-
st[wordIdx(i, 3)] = (st[wordIdx(i, 3)]! ^ st1[wordIdx(prev, 3)]!) >>> 0;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* AEGIS round function with constant input (used in packed mode).
|
|
80
|
-
*/
|
|
81
|
-
private aegisRoundPacked(constantInput: AesBlocks): void {
|
|
60
|
+
private aegisRoundPacked(): void {
|
|
82
61
|
const st = this.st;
|
|
83
62
|
const st1 = this.st1;
|
|
84
63
|
|
|
@@ -86,13 +65,11 @@ export class Aegis128LBsState {
|
|
|
86
65
|
aesRound(st1);
|
|
87
66
|
blocksRotr(st1);
|
|
88
67
|
blocksXor(st, st1);
|
|
89
|
-
blocksXor(st, constantInput);
|
|
68
|
+
blocksXor(st, this.constantInput);
|
|
90
69
|
}
|
|
91
70
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
*/
|
|
95
|
-
private packConstantInput(out: AesBlocks, m0: AesBlock, m1: AesBlock): void {
|
|
71
|
+
private packConstantInput(m0: AesBlock, m1: AesBlock): void {
|
|
72
|
+
const out = this.constantInput;
|
|
96
73
|
out.fill(0);
|
|
97
74
|
blocksPut(out, m0, 0);
|
|
98
75
|
blocksPut(out, m1, 4);
|
|
@@ -100,27 +77,35 @@ export class Aegis128LBsState {
|
|
|
100
77
|
}
|
|
101
78
|
|
|
102
79
|
/**
|
|
103
|
-
*
|
|
80
|
+
* Extract the keystream blocks z0 and z1 directly from the packed state.
|
|
81
|
+
*
|
|
82
|
+
* z0 = S6 ^ S1 ^ (S2 & S3)
|
|
83
|
+
* z1 = S2 ^ S5 ^ (S6 & S7)
|
|
84
|
+
*
|
|
85
|
+
* The formula is evaluated lane-wise on the packed state, the result is
|
|
86
|
+
* placed in positions 0 (z0) and 4 (z1), then unpack04 extracts the two
|
|
87
|
+
* AES blocks.
|
|
104
88
|
*/
|
|
105
|
-
private
|
|
89
|
+
private keystreamPacked(): void {
|
|
106
90
|
const st = this.st;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
91
|
+
const z = this.st1;
|
|
92
|
+
for (let i = 0; i < 32; i++) {
|
|
93
|
+
const x = st[i]!;
|
|
94
|
+
z[i] =
|
|
95
|
+
((x & 0x02020202) << 6) ^
|
|
96
|
+
((x & 0x40404040) << 1) ^
|
|
97
|
+
((x & (x >>> 1) & 0x10101010) << 3) ^
|
|
98
|
+
((x & 0x20202020) >>> 2) ^
|
|
99
|
+
((x & 0x04040404) << 1) ^
|
|
100
|
+
((x & (x >>> 1) & 0x01010101) << 3);
|
|
101
|
+
}
|
|
102
|
+
unpack04(z);
|
|
103
|
+
const z0 = this.z0;
|
|
104
|
+
const z1 = this.z1;
|
|
105
|
+
for (let i = 0; i < 4; i++) {
|
|
106
|
+
z0[i] = z[wordIdx(0, i)]!;
|
|
107
|
+
z1[i] = z[wordIdx(4, i)]!;
|
|
108
|
+
}
|
|
124
109
|
}
|
|
125
110
|
|
|
126
111
|
/**
|
|
@@ -150,13 +135,11 @@ export class Aegis128LBsState {
|
|
|
150
135
|
blocksPut(this.st, kc1, 6);
|
|
151
136
|
blocksPut(this.st, kc0, 7);
|
|
152
137
|
|
|
153
|
-
|
|
154
|
-
this.packConstantInput(constantInput, n, k);
|
|
138
|
+
this.packConstantInput(n, k);
|
|
155
139
|
pack(this.st);
|
|
156
140
|
for (let i = 0; i < 10; i++) {
|
|
157
|
-
this.aegisRoundPacked(
|
|
141
|
+
this.aegisRoundPacked();
|
|
158
142
|
}
|
|
159
|
-
unpack(this.st);
|
|
160
143
|
}
|
|
161
144
|
|
|
162
145
|
/**
|
|
@@ -168,7 +151,8 @@ export class Aegis128LBsState {
|
|
|
168
151
|
const msg1 = this.tmp1;
|
|
169
152
|
blockFromBytes(msg0, ai.subarray(0, 16));
|
|
170
153
|
blockFromBytes(msg1, ai.subarray(16, 32));
|
|
171
|
-
this.
|
|
154
|
+
this.packConstantInput(msg0, msg1);
|
|
155
|
+
this.aegisRoundPacked();
|
|
172
156
|
}
|
|
173
157
|
|
|
174
158
|
/**
|
|
@@ -177,39 +161,22 @@ export class Aegis128LBsState {
|
|
|
177
161
|
* @param out - 32-byte output buffer
|
|
178
162
|
*/
|
|
179
163
|
encTo(xi: Uint8Array, out: Uint8Array): void {
|
|
180
|
-
const st = this.st;
|
|
181
|
-
const z0 = this.z0;
|
|
182
|
-
const z1 = this.z1;
|
|
183
164
|
const t0 = this.tmp0;
|
|
184
165
|
const t1 = this.tmp1;
|
|
185
166
|
|
|
186
|
-
|
|
187
|
-
z0[i] =
|
|
188
|
-
(st[wordIdx(6, i)]! ^
|
|
189
|
-
st[wordIdx(1, i)]! ^
|
|
190
|
-
(st[wordIdx(2, i)]! & st[wordIdx(3, i)]!)) >>>
|
|
191
|
-
0;
|
|
192
|
-
}
|
|
193
|
-
for (let i = 0; i < 4; i++) {
|
|
194
|
-
z1[i] =
|
|
195
|
-
(st[wordIdx(2, i)]! ^
|
|
196
|
-
st[wordIdx(5, i)]! ^
|
|
197
|
-
(st[wordIdx(6, i)]! & st[wordIdx(7, i)]!)) >>>
|
|
198
|
-
0;
|
|
199
|
-
}
|
|
167
|
+
this.keystreamPacked();
|
|
200
168
|
|
|
201
169
|
blockFromBytes(t0, xi.subarray(0, 16));
|
|
202
170
|
blockFromBytes(t1, xi.subarray(16, 32));
|
|
203
171
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
blockXor(out0, t0, z0);
|
|
207
|
-
blockXor(out1, t1, z1);
|
|
172
|
+
this.packConstantInput(t0, t1);
|
|
173
|
+
this.aegisRoundPacked();
|
|
208
174
|
|
|
209
|
-
|
|
210
|
-
|
|
175
|
+
blockXor(t0, t0, this.z0);
|
|
176
|
+
blockXor(t1, t1, this.z1);
|
|
211
177
|
|
|
212
|
-
|
|
178
|
+
blockToBytes(out.subarray(0, 16), t0);
|
|
179
|
+
blockToBytes(out.subarray(16, 32), t1);
|
|
213
180
|
}
|
|
214
181
|
|
|
215
182
|
/**
|
|
@@ -229,31 +196,18 @@ export class Aegis128LBsState {
|
|
|
229
196
|
* @param out - 32-byte output buffer
|
|
230
197
|
*/
|
|
231
198
|
decTo(ci: Uint8Array, out: Uint8Array): void {
|
|
232
|
-
const st = this.st;
|
|
233
199
|
const msg0 = this.tmp0;
|
|
234
200
|
const msg1 = this.tmp1;
|
|
235
201
|
|
|
236
202
|
blockFromBytes(msg0, ci.subarray(0, 16));
|
|
237
203
|
blockFromBytes(msg1, ci.subarray(16, 32));
|
|
238
204
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
st[wordIdx(6, i)]! ^
|
|
243
|
-
st[wordIdx(1, i)]! ^
|
|
244
|
-
(st[wordIdx(2, i)]! & st[wordIdx(3, i)]!)) >>>
|
|
245
|
-
0;
|
|
246
|
-
}
|
|
247
|
-
for (let i = 0; i < 4; i++) {
|
|
248
|
-
msg1[i] =
|
|
249
|
-
(msg1[i]! ^
|
|
250
|
-
st[wordIdx(2, i)]! ^
|
|
251
|
-
st[wordIdx(5, i)]! ^
|
|
252
|
-
(st[wordIdx(6, i)]! & st[wordIdx(7, i)]!)) >>>
|
|
253
|
-
0;
|
|
254
|
-
}
|
|
205
|
+
this.keystreamPacked();
|
|
206
|
+
blockXor(msg0, msg0, this.z0);
|
|
207
|
+
blockXor(msg1, msg1, this.z1);
|
|
255
208
|
|
|
256
|
-
this.
|
|
209
|
+
this.packConstantInput(msg0, msg1);
|
|
210
|
+
this.aegisRoundPacked();
|
|
257
211
|
|
|
258
212
|
blockToBytes(out.subarray(0, 16), msg0);
|
|
259
213
|
blockToBytes(out.subarray(16, 32), msg1);
|
|
@@ -270,13 +224,20 @@ export class Aegis128LBsState {
|
|
|
270
224
|
return out;
|
|
271
225
|
}
|
|
272
226
|
|
|
227
|
+
encInPlace(block: Uint8Array): void {
|
|
228
|
+
this.encTo(block, block);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
decInPlace(block: Uint8Array): void {
|
|
232
|
+
this.decTo(block, block);
|
|
233
|
+
}
|
|
234
|
+
|
|
273
235
|
/**
|
|
274
236
|
* Decrypts a partial (final) ciphertext block smaller than 32 bytes.
|
|
275
237
|
* @param cn - Partial ciphertext block (1-31 bytes)
|
|
276
238
|
* @returns Decrypted plaintext of the same length
|
|
277
239
|
*/
|
|
278
240
|
decPartial(cn: Uint8Array): Uint8Array {
|
|
279
|
-
const st = this.st;
|
|
280
241
|
const msg0 = this.tmp0;
|
|
281
242
|
const msg1 = this.tmp1;
|
|
282
243
|
|
|
@@ -284,22 +245,9 @@ export class Aegis128LBsState {
|
|
|
284
245
|
blockFromBytes(msg0, padded.subarray(0, 16));
|
|
285
246
|
blockFromBytes(msg1, padded.subarray(16, 32));
|
|
286
247
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
st[wordIdx(6, i)]! ^
|
|
291
|
-
st[wordIdx(1, i)]! ^
|
|
292
|
-
(st[wordIdx(2, i)]! & st[wordIdx(3, i)]!)) >>>
|
|
293
|
-
0;
|
|
294
|
-
}
|
|
295
|
-
for (let i = 0; i < 4; i++) {
|
|
296
|
-
msg1[i] =
|
|
297
|
-
(msg1[i]! ^
|
|
298
|
-
st[wordIdx(2, i)]! ^
|
|
299
|
-
st[wordIdx(5, i)]! ^
|
|
300
|
-
(st[wordIdx(6, i)]! & st[wordIdx(7, i)]!)) >>>
|
|
301
|
-
0;
|
|
302
|
-
}
|
|
248
|
+
this.keystreamPacked();
|
|
249
|
+
blockXor(msg0, msg0, this.z0);
|
|
250
|
+
blockXor(msg1, msg1, this.z1);
|
|
303
251
|
|
|
304
252
|
const pad = new Uint8Array(RATE);
|
|
305
253
|
blockToBytes(pad.subarray(0, 16), msg0);
|
|
@@ -311,8 +259,8 @@ export class Aegis128LBsState {
|
|
|
311
259
|
blockFromBytes(msg0, pad.subarray(0, 16));
|
|
312
260
|
blockFromBytes(msg1, pad.subarray(16, 32));
|
|
313
261
|
|
|
314
|
-
this.
|
|
315
|
-
this.
|
|
262
|
+
this.packConstantInput(msg0, msg1);
|
|
263
|
+
this.aegisRoundPacked();
|
|
316
264
|
|
|
317
265
|
return xn;
|
|
318
266
|
}
|
|
@@ -333,18 +281,20 @@ export class Aegis128LBsState {
|
|
|
333
281
|
tmp[2] = ((msgLen * 8) & 0xffffffff) >>> 0;
|
|
334
282
|
tmp[3] = Math.floor((msgLen * 8) / 0x100000000) >>> 0;
|
|
335
283
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
tmp[3] = (tmp[3]! ^ st[wordIdx(2, 3)]!) >>> 0;
|
|
284
|
+
const unpacked = this.st1;
|
|
285
|
+
unpacked.set(st);
|
|
286
|
+
unpack(unpacked);
|
|
340
287
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
288
|
+
tmp[0] = (tmp[0]! ^ unpacked[wordIdx(2, 0)]!) >>> 0;
|
|
289
|
+
tmp[1] = (tmp[1]! ^ unpacked[wordIdx(2, 1)]!) >>> 0;
|
|
290
|
+
tmp[2] = (tmp[2]! ^ unpacked[wordIdx(2, 2)]!) >>> 0;
|
|
291
|
+
tmp[3] = (tmp[3]! ^ unpacked[wordIdx(2, 3)]!) >>> 0;
|
|
292
|
+
|
|
293
|
+
this.packConstantInput(tmp, tmp);
|
|
344
294
|
for (let i = 0; i < 7; i++) {
|
|
345
|
-
this.aegisRoundPacked(
|
|
295
|
+
this.aegisRoundPacked();
|
|
346
296
|
}
|
|
347
|
-
unpack(
|
|
297
|
+
unpack(st);
|
|
348
298
|
|
|
349
299
|
if (tagLen === 16) {
|
|
350
300
|
const tag = new Uint8Array(16);
|
|
@@ -477,18 +427,89 @@ export function aegis128LBsDecryptDetached(
|
|
|
477
427
|
return msg;
|
|
478
428
|
}
|
|
479
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Encrypts a message in-place using bitsliced AEGIS-128L (detached mode).
|
|
432
|
+
*/
|
|
433
|
+
export function aegis128LBsEncryptDetachedInPlace(
|
|
434
|
+
data: Uint8Array,
|
|
435
|
+
ad: Uint8Array,
|
|
436
|
+
key: Uint8Array,
|
|
437
|
+
nonce: Uint8Array,
|
|
438
|
+
tagLen: 16 | 32 = 16,
|
|
439
|
+
): Uint8Array {
|
|
440
|
+
const state = new Aegis128LBsState();
|
|
441
|
+
state.init(key, nonce);
|
|
442
|
+
|
|
443
|
+
const adPadded = zeroPad(ad, RATE);
|
|
444
|
+
for (let i = 0; i + RATE <= adPadded.length; i += RATE) {
|
|
445
|
+
state.absorb(adPadded.subarray(i, i + RATE));
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const msgLen = data.length;
|
|
449
|
+
const fullBlocksLen = Math.floor(msgLen / RATE) * RATE;
|
|
450
|
+
|
|
451
|
+
for (let i = 0; i < fullBlocksLen; i += RATE) {
|
|
452
|
+
state.encInPlace(data.subarray(i, i + RATE));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (msgLen > fullBlocksLen) {
|
|
456
|
+
const lastPartial = data.subarray(fullBlocksLen);
|
|
457
|
+
const lastBlock = zeroPad(lastPartial, RATE);
|
|
458
|
+
const encBlock = state.enc(lastBlock);
|
|
459
|
+
lastPartial.set(encBlock.subarray(0, lastPartial.length));
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return state.finalize(ad.length, msgLen, tagLen);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Decrypts a message in-place using bitsliced AEGIS-128L (detached mode).
|
|
467
|
+
*/
|
|
468
|
+
export function aegis128LBsDecryptDetachedInPlace(
|
|
469
|
+
data: Uint8Array,
|
|
470
|
+
tag: Uint8Array,
|
|
471
|
+
ad: Uint8Array,
|
|
472
|
+
key: Uint8Array,
|
|
473
|
+
nonce: Uint8Array,
|
|
474
|
+
): boolean {
|
|
475
|
+
const tagLen = tag.length as 16 | 32;
|
|
476
|
+
const state = new Aegis128LBsState();
|
|
477
|
+
state.init(key, nonce);
|
|
478
|
+
|
|
479
|
+
const adPadded = zeroPad(ad, RATE);
|
|
480
|
+
for (let i = 0; i + RATE <= adPadded.length; i += RATE) {
|
|
481
|
+
state.absorb(adPadded.subarray(i, i + RATE));
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const msgLen = data.length;
|
|
485
|
+
const fullBlocksLen = Math.floor(msgLen / RATE) * RATE;
|
|
486
|
+
|
|
487
|
+
for (let i = 0; i < fullBlocksLen; i += RATE) {
|
|
488
|
+
state.decInPlace(data.subarray(i, i + RATE));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (msgLen > fullBlocksLen) {
|
|
492
|
+
const lastPartial = data.subarray(fullBlocksLen);
|
|
493
|
+
const decrypted = state.decPartial(lastPartial);
|
|
494
|
+
lastPartial.set(decrypted);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const expectedTag = state.finalize(ad.length, msgLen, tagLen);
|
|
498
|
+
|
|
499
|
+
if (!constantTimeEqual(tag, expectedTag)) {
|
|
500
|
+
data.fill(0);
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
|
|
480
507
|
export const AEGIS_128L_BS_NONCE_SIZE = 16;
|
|
481
508
|
export const AEGIS_128L_BS_KEY_SIZE = 16;
|
|
482
509
|
|
|
483
510
|
/**
|
|
484
511
|
* Encrypts a message using bitsliced AEGIS-128L.
|
|
485
512
|
* Returns a single buffer containing nonce || ciphertext || tag.
|
|
486
|
-
* @param msg - Plaintext message
|
|
487
|
-
* @param ad - Associated data (authenticated but not encrypted)
|
|
488
|
-
* @param key - 16-byte encryption key
|
|
489
|
-
* @param nonce - 16-byte nonce (optional, generates random nonce if not provided)
|
|
490
|
-
* @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
|
|
491
|
-
* @returns Concatenated nonce || ciphertext || tag
|
|
492
513
|
*/
|
|
493
514
|
export function aegis128LBsEncrypt(
|
|
494
515
|
msg: Uint8Array,
|
|
@@ -519,11 +540,6 @@ export function aegis128LBsEncrypt(
|
|
|
519
540
|
/**
|
|
520
541
|
* Decrypts a message using bitsliced AEGIS-128L.
|
|
521
542
|
* Expects input as nonce || ciphertext || tag.
|
|
522
|
-
* @param sealed - Concatenated nonce || ciphertext || tag
|
|
523
|
-
* @param ad - Associated data (must match what was used during encryption)
|
|
524
|
-
* @param key - 16-byte encryption key
|
|
525
|
-
* @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
|
|
526
|
-
* @returns Decrypted plaintext, or null if authentication fails
|
|
527
543
|
*/
|
|
528
544
|
export function aegis128LBsDecrypt(
|
|
529
545
|
sealed: Uint8Array,
|
|
@@ -542,12 +558,7 @@ export function aegis128LBsDecrypt(
|
|
|
542
558
|
}
|
|
543
559
|
|
|
544
560
|
/**
|
|
545
|
-
* Computes a MAC
|
|
546
|
-
* @param data - Data to authenticate
|
|
547
|
-
* @param key - 16-byte key
|
|
548
|
-
* @param nonce - 16-byte nonce (optional, uses zero nonce if null)
|
|
549
|
-
* @param tagLen - Tag length: 16 or 32 bytes (default: 16)
|
|
550
|
-
* @returns Authentication tag
|
|
561
|
+
* Computes a MAC using bitsliced AEGIS-128L.
|
|
551
562
|
*/
|
|
552
563
|
export function aegis128LBsMac(
|
|
553
564
|
data: Uint8Array,
|
|
@@ -568,11 +579,6 @@ export function aegis128LBsMac(
|
|
|
568
579
|
|
|
569
580
|
/**
|
|
570
581
|
* Verifies a MAC computed using bitsliced AEGIS-128L.
|
|
571
|
-
* @param data - Data to verify
|
|
572
|
-
* @param tag - Expected authentication tag (16 or 32 bytes)
|
|
573
|
-
* @param key - 16-byte key
|
|
574
|
-
* @param nonce - 16-byte nonce (optional, uses zero nonce if null)
|
|
575
|
-
* @returns True if the tag is valid, false otherwise
|
|
576
582
|
*/
|
|
577
583
|
export function aegis128LBsMacVerify(
|
|
578
584
|
data: Uint8Array,
|
|
@@ -585,18 +591,10 @@ export function aegis128LBsMacVerify(
|
|
|
585
591
|
return constantTimeEqual(tag, expectedTag);
|
|
586
592
|
}
|
|
587
593
|
|
|
588
|
-
/**
|
|
589
|
-
* Generates a random 16-byte key for bitsliced AEGIS-128L.
|
|
590
|
-
* @returns 16-byte encryption key
|
|
591
|
-
*/
|
|
592
594
|
export function aegis128LBsCreateKey(): Uint8Array {
|
|
593
595
|
return randomBytes(AEGIS_128L_BS_KEY_SIZE);
|
|
594
596
|
}
|
|
595
597
|
|
|
596
|
-
/**
|
|
597
|
-
* Generates a random 16-byte nonce for bitsliced AEGIS-128L.
|
|
598
|
-
* @returns 16-byte nonce
|
|
599
|
-
*/
|
|
600
598
|
export function aegis128LBsCreateNonce(): Uint8Array {
|
|
601
599
|
return randomBytes(AEGIS_128L_BS_NONCE_SIZE);
|
|
602
600
|
}
|
package/src/aegis128l.ts
CHANGED
|
@@ -204,6 +204,37 @@ export class Aegis128LState {
|
|
|
204
204
|
return out;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Encrypts a 32-byte plaintext block in-place.
|
|
209
|
+
* @param block - 32-byte buffer (plaintext in, ciphertext out)
|
|
210
|
+
*/
|
|
211
|
+
encInPlace(block: Uint8Array): void {
|
|
212
|
+
const z0 = this.z0;
|
|
213
|
+
const z1 = this.z1;
|
|
214
|
+
const tmp = this.tmp0;
|
|
215
|
+
|
|
216
|
+
xorBlocksTo(this.s1, this.s6, z0);
|
|
217
|
+
andBlocksTo(this.s2, this.s3, tmp);
|
|
218
|
+
for (let i = 0; i < 16; i++) z0[i] ^= tmp[i]!;
|
|
219
|
+
|
|
220
|
+
xorBlocksTo(this.s2, this.s5, z1);
|
|
221
|
+
andBlocksTo(this.s6, this.s7, tmp);
|
|
222
|
+
for (let i = 0; i < 16; i++) z1[i] ^= tmp[i]!;
|
|
223
|
+
|
|
224
|
+
this.update(block.subarray(0, 16), block.subarray(16, 32));
|
|
225
|
+
|
|
226
|
+
for (let i = 0; i < 16; i++) block[i] ^= z0[i]!;
|
|
227
|
+
for (let i = 0; i < 16; i++) block[16 + i] ^= z1[i]!;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Decrypts a 32-byte ciphertext block in-place.
|
|
232
|
+
* @param block - 32-byte buffer (ciphertext in, plaintext out)
|
|
233
|
+
*/
|
|
234
|
+
decInPlace(block: Uint8Array): void {
|
|
235
|
+
this.decTo(block, block);
|
|
236
|
+
}
|
|
237
|
+
|
|
207
238
|
/**
|
|
208
239
|
* Decrypts a partial (final) ciphertext block smaller than 32 bytes.
|
|
209
240
|
* @param cn - Partial ciphertext block (1-31 bytes)
|
|
@@ -376,6 +407,101 @@ export function aegis128LDecryptDetached(
|
|
|
376
407
|
return msg;
|
|
377
408
|
}
|
|
378
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Encrypts a message in-place using AEGIS-128L (detached mode).
|
|
412
|
+
* The input buffer is modified to contain the ciphertext.
|
|
413
|
+
* @param data - Buffer containing plaintext (will be overwritten with ciphertext)
|
|
414
|
+
* @param ad - Associated data (authenticated but not encrypted)
|
|
415
|
+
* @param key - 16-byte encryption key
|
|
416
|
+
* @param nonce - 16-byte nonce (must be unique per message with the same key)
|
|
417
|
+
* @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
|
|
418
|
+
* @returns Authentication tag
|
|
419
|
+
*/
|
|
420
|
+
export function aegis128LEncryptDetachedInPlace(
|
|
421
|
+
data: Uint8Array,
|
|
422
|
+
ad: Uint8Array,
|
|
423
|
+
key: Uint8Array,
|
|
424
|
+
nonce: Uint8Array,
|
|
425
|
+
tagLen: 16 | 32 = 16,
|
|
426
|
+
): Uint8Array {
|
|
427
|
+
const state = new Aegis128LState();
|
|
428
|
+
state.init(key, nonce);
|
|
429
|
+
|
|
430
|
+
const adPadded = zeroPad(ad, 32);
|
|
431
|
+
for (let i = 0; i + 32 <= adPadded.length; i += 32) {
|
|
432
|
+
state.absorb(adPadded.subarray(i, i + 32));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const msgLen = data.length;
|
|
436
|
+
const fullBlocksLen = Math.floor(msgLen / 32) * 32;
|
|
437
|
+
|
|
438
|
+
for (let i = 0; i < fullBlocksLen; i += 32) {
|
|
439
|
+
state.encInPlace(data.subarray(i, i + 32));
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (msgLen > fullBlocksLen) {
|
|
443
|
+
const lastPartial = data.subarray(fullBlocksLen);
|
|
444
|
+
const lastBlock = zeroPad(lastPartial, 32);
|
|
445
|
+
const encBlock = state.enc(lastBlock);
|
|
446
|
+
lastPartial.set(encBlock.subarray(0, lastPartial.length));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return state.finalize(BigInt(ad.length * 8), BigInt(msgLen * 8), tagLen);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Decrypts a message in-place using AEGIS-128L (detached mode).
|
|
454
|
+
* The input buffer is modified to contain the plaintext (or zeroed on failure).
|
|
455
|
+
* @param data - Buffer containing ciphertext (will be overwritten with plaintext)
|
|
456
|
+
* @param tag - Authentication tag (16 or 32 bytes)
|
|
457
|
+
* @param ad - Associated data (must match what was used during encryption)
|
|
458
|
+
* @param key - 16-byte encryption key
|
|
459
|
+
* @param nonce - 16-byte nonce (must match what was used during encryption)
|
|
460
|
+
* @returns True if authentication succeeds, false otherwise
|
|
461
|
+
*/
|
|
462
|
+
export function aegis128LDecryptDetachedInPlace(
|
|
463
|
+
data: Uint8Array,
|
|
464
|
+
tag: Uint8Array,
|
|
465
|
+
ad: Uint8Array,
|
|
466
|
+
key: Uint8Array,
|
|
467
|
+
nonce: Uint8Array,
|
|
468
|
+
): boolean {
|
|
469
|
+
const tagLen = tag.length as 16 | 32;
|
|
470
|
+
const state = new Aegis128LState();
|
|
471
|
+
state.init(key, nonce);
|
|
472
|
+
|
|
473
|
+
const adPadded = zeroPad(ad, 32);
|
|
474
|
+
for (let i = 0; i + 32 <= adPadded.length; i += 32) {
|
|
475
|
+
state.absorb(adPadded.subarray(i, i + 32));
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const msgLen = data.length;
|
|
479
|
+
const fullBlocksLen = Math.floor(msgLen / 32) * 32;
|
|
480
|
+
|
|
481
|
+
for (let i = 0; i < fullBlocksLen; i += 32) {
|
|
482
|
+
state.decInPlace(data.subarray(i, i + 32));
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (msgLen > fullBlocksLen) {
|
|
486
|
+
const lastPartial = data.subarray(fullBlocksLen);
|
|
487
|
+
const decrypted = state.decPartial(lastPartial);
|
|
488
|
+
lastPartial.set(decrypted);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const expectedTag = state.finalize(
|
|
492
|
+
BigInt(ad.length * 8),
|
|
493
|
+
BigInt(msgLen * 8),
|
|
494
|
+
tagLen,
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
if (!constantTimeEqual(tag, expectedTag)) {
|
|
498
|
+
data.fill(0);
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
|
|
379
505
|
/** Nonce size for AEGIS-128L in bytes. */
|
|
380
506
|
export const AEGIS_128L_NONCE_SIZE = 16;
|
|
381
507
|
|