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/aegis128x.ts
CHANGED
|
@@ -224,6 +224,35 @@ export class Aegis128XState {
|
|
|
224
224
|
return out;
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Encrypts a plaintext block in-place.
|
|
229
|
+
* @param block - Buffer (plaintext in, ciphertext out), size 32*degree bytes
|
|
230
|
+
*/
|
|
231
|
+
encInPlace(block: Uint8Array): void {
|
|
232
|
+
this.computeZ();
|
|
233
|
+
const z0 = this.z0;
|
|
234
|
+
const z1 = this.z1;
|
|
235
|
+
|
|
236
|
+
const halfRateBytes = this.rate / 16;
|
|
237
|
+
const rateBytes = this.rate / 8;
|
|
238
|
+
|
|
239
|
+
this.update(
|
|
240
|
+
block.subarray(0, halfRateBytes),
|
|
241
|
+
block.subarray(halfRateBytes, rateBytes),
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
for (let i = 0; i < halfRateBytes; i++) block[i] ^= z0[i]!;
|
|
245
|
+
for (let i = 0; i < halfRateBytes; i++) block[halfRateBytes + i] ^= z1[i]!;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Decrypts a ciphertext block in-place.
|
|
250
|
+
* @param block - Buffer (ciphertext in, plaintext out), size 32*degree bytes
|
|
251
|
+
*/
|
|
252
|
+
decInPlace(block: Uint8Array): void {
|
|
253
|
+
this.decTo(block, block);
|
|
254
|
+
}
|
|
255
|
+
|
|
227
256
|
/**
|
|
228
257
|
* Decrypts a partial (final) ciphertext block.
|
|
229
258
|
* @param cn - Partial ciphertext block (smaller than 32*degree bytes)
|
|
@@ -487,6 +516,145 @@ export function aegis128XDecryptDetached(
|
|
|
487
516
|
return msg;
|
|
488
517
|
}
|
|
489
518
|
|
|
519
|
+
/**
|
|
520
|
+
* Encrypts a message in-place using AEGIS-128X (detached mode).
|
|
521
|
+
* The input buffer is modified to contain the ciphertext.
|
|
522
|
+
* @param data - Buffer containing plaintext (will be overwritten with ciphertext)
|
|
523
|
+
* @param ad - Associated data (authenticated but not encrypted)
|
|
524
|
+
* @param key - 16-byte encryption key
|
|
525
|
+
* @param nonce - 16-byte nonce (must be unique per message with the same key)
|
|
526
|
+
* @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
|
|
527
|
+
* @param degree - Parallelism degree (default: 2)
|
|
528
|
+
* @returns Authentication tag
|
|
529
|
+
*/
|
|
530
|
+
export function aegis128XEncryptDetachedInPlace(
|
|
531
|
+
data: Uint8Array,
|
|
532
|
+
ad: Uint8Array,
|
|
533
|
+
key: Uint8Array,
|
|
534
|
+
nonce: Uint8Array,
|
|
535
|
+
tagLen: 16 | 32 = 16,
|
|
536
|
+
degree: number = 2,
|
|
537
|
+
): Uint8Array {
|
|
538
|
+
const state = new Aegis128XState(degree);
|
|
539
|
+
const rateBytes = (256 * degree) / 8;
|
|
540
|
+
|
|
541
|
+
state.init(key, nonce);
|
|
542
|
+
|
|
543
|
+
const adPadded = zeroPad(ad, rateBytes);
|
|
544
|
+
for (let i = 0; i + rateBytes <= adPadded.length; i += rateBytes) {
|
|
545
|
+
state.absorb(adPadded.subarray(i, i + rateBytes));
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const msgLen = data.length;
|
|
549
|
+
const fullBlocksLen = Math.floor(msgLen / rateBytes) * rateBytes;
|
|
550
|
+
|
|
551
|
+
for (let i = 0; i < fullBlocksLen; i += rateBytes) {
|
|
552
|
+
state.encInPlace(data.subarray(i, i + rateBytes));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (msgLen > fullBlocksLen) {
|
|
556
|
+
const lastPartial = data.subarray(fullBlocksLen);
|
|
557
|
+
const lastBlock = zeroPad(lastPartial, rateBytes);
|
|
558
|
+
const encBlock = state.enc(lastBlock);
|
|
559
|
+
lastPartial.set(encBlock.subarray(0, lastPartial.length));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return state.finalize(BigInt(ad.length * 8), BigInt(msgLen * 8), tagLen);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Decrypts a message in-place using AEGIS-128X (detached mode).
|
|
567
|
+
* The input buffer is modified to contain the plaintext (or zeroed on failure).
|
|
568
|
+
* @param data - Buffer containing ciphertext (will be overwritten with plaintext)
|
|
569
|
+
* @param tag - Authentication tag (16 or 32 bytes)
|
|
570
|
+
* @param ad - Associated data (must match what was used during encryption)
|
|
571
|
+
* @param key - 16-byte encryption key
|
|
572
|
+
* @param nonce - 16-byte nonce (must match what was used during encryption)
|
|
573
|
+
* @param degree - Parallelism degree (default: 2)
|
|
574
|
+
* @returns True if authentication succeeds, false otherwise
|
|
575
|
+
*/
|
|
576
|
+
export function aegis128XDecryptDetachedInPlace(
|
|
577
|
+
data: Uint8Array,
|
|
578
|
+
tag: Uint8Array,
|
|
579
|
+
ad: Uint8Array,
|
|
580
|
+
key: Uint8Array,
|
|
581
|
+
nonce: Uint8Array,
|
|
582
|
+
degree: number = 2,
|
|
583
|
+
): boolean {
|
|
584
|
+
const tagLen = tag.length as 16 | 32;
|
|
585
|
+
const state = new Aegis128XState(degree);
|
|
586
|
+
const rateBytes = (256 * degree) / 8;
|
|
587
|
+
|
|
588
|
+
state.init(key, nonce);
|
|
589
|
+
|
|
590
|
+
const adPadded = zeroPad(ad, rateBytes);
|
|
591
|
+
for (let i = 0; i + rateBytes <= adPadded.length; i += rateBytes) {
|
|
592
|
+
state.absorb(adPadded.subarray(i, i + rateBytes));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const msgLen = data.length;
|
|
596
|
+
const fullBlocksLen = Math.floor(msgLen / rateBytes) * rateBytes;
|
|
597
|
+
|
|
598
|
+
for (let i = 0; i < fullBlocksLen; i += rateBytes) {
|
|
599
|
+
state.decInPlace(data.subarray(i, i + rateBytes));
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (msgLen > fullBlocksLen) {
|
|
603
|
+
const lastPartial = data.subarray(fullBlocksLen);
|
|
604
|
+
const decrypted = state.decPartial(lastPartial);
|
|
605
|
+
lastPartial.set(decrypted);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const expectedTag = state.finalize(
|
|
609
|
+
BigInt(ad.length * 8),
|
|
610
|
+
BigInt(msgLen * 8),
|
|
611
|
+
tagLen,
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
if (!constantTimeEqual(tag, expectedTag)) {
|
|
615
|
+
data.fill(0);
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/** AEGIS-128X2 in-place encryption - detached mode (degree=2). */
|
|
623
|
+
export const aegis128X2EncryptDetachedInPlace = (
|
|
624
|
+
data: Uint8Array,
|
|
625
|
+
ad: Uint8Array,
|
|
626
|
+
key: Uint8Array,
|
|
627
|
+
nonce: Uint8Array,
|
|
628
|
+
tagLen: 16 | 32 = 16,
|
|
629
|
+
) => aegis128XEncryptDetachedInPlace(data, ad, key, nonce, tagLen, 2);
|
|
630
|
+
|
|
631
|
+
/** AEGIS-128X2 in-place decryption - detached mode (degree=2). */
|
|
632
|
+
export const aegis128X2DecryptDetachedInPlace = (
|
|
633
|
+
data: Uint8Array,
|
|
634
|
+
tag: Uint8Array,
|
|
635
|
+
ad: Uint8Array,
|
|
636
|
+
key: Uint8Array,
|
|
637
|
+
nonce: Uint8Array,
|
|
638
|
+
) => aegis128XDecryptDetachedInPlace(data, tag, ad, key, nonce, 2);
|
|
639
|
+
|
|
640
|
+
/** AEGIS-128X4 in-place encryption - detached mode (degree=4). */
|
|
641
|
+
export const aegis128X4EncryptDetachedInPlace = (
|
|
642
|
+
data: Uint8Array,
|
|
643
|
+
ad: Uint8Array,
|
|
644
|
+
key: Uint8Array,
|
|
645
|
+
nonce: Uint8Array,
|
|
646
|
+
tagLen: 16 | 32 = 16,
|
|
647
|
+
) => aegis128XEncryptDetachedInPlace(data, ad, key, nonce, tagLen, 4);
|
|
648
|
+
|
|
649
|
+
/** AEGIS-128X4 in-place decryption - detached mode (degree=4). */
|
|
650
|
+
export const aegis128X4DecryptDetachedInPlace = (
|
|
651
|
+
data: Uint8Array,
|
|
652
|
+
tag: Uint8Array,
|
|
653
|
+
ad: Uint8Array,
|
|
654
|
+
key: Uint8Array,
|
|
655
|
+
nonce: Uint8Array,
|
|
656
|
+
) => aegis128XDecryptDetachedInPlace(data, tag, ad, key, nonce, 4);
|
|
657
|
+
|
|
490
658
|
/** Nonce size for AEGIS-128X in bytes. */
|
|
491
659
|
export const AEGIS_128X_NONCE_SIZE = 16;
|
|
492
660
|
|
package/src/aegis256-bs.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
*
|
|
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
|
|
84
|
-
this.
|
|
85
|
-
this.
|
|
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.
|
|
122
|
-
this.
|
|
123
|
-
this.
|
|
124
|
-
this.
|
|
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.
|
|
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
|
|
145
|
-
const z = this.tmp;
|
|
146
|
-
const t = createAesBlock();
|
|
161
|
+
const t = this.tmp;
|
|
147
162
|
|
|
148
|
-
|
|
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
|
-
|
|
160
|
-
|
|
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,61 +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
|
-
|
|
189
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
201
|
+
encInPlace(block: Uint8Array): void {
|
|
202
|
+
this.encTo(block, block);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
decInPlace(block: Uint8Array): void {
|
|
206
|
+
this.decTo(block, block);
|
|
207
|
+
}
|
|
208
|
+
|
|
218
209
|
decPartial(cn: Uint8Array): Uint8Array {
|
|
219
|
-
const st = this.st;
|
|
220
210
|
const msg = this.tmp;
|
|
221
211
|
|
|
222
212
|
const padded = zeroPad(cn, RATE);
|
|
223
213
|
blockFromBytes(msg, padded);
|
|
224
214
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
(msg[i]! ^
|
|
228
|
-
st[wordIdx(1, i)]! ^
|
|
229
|
-
st[wordIdx(4, i)]! ^
|
|
230
|
-
st[wordIdx(5, i)]! ^
|
|
231
|
-
(st[wordIdx(2, i)]! & st[wordIdx(3, i)]!)) >>>
|
|
232
|
-
0;
|
|
233
|
-
}
|
|
215
|
+
this.keystreamPacked();
|
|
216
|
+
blockXor(msg, msg, this.z);
|
|
234
217
|
|
|
235
218
|
const pad = new Uint8Array(RATE);
|
|
236
219
|
blockToBytes(pad, msg);
|
|
@@ -240,18 +223,14 @@ export class Aegis256BsState {
|
|
|
240
223
|
pad.fill(0, cn.length);
|
|
241
224
|
blockFromBytes(msg, pad);
|
|
242
225
|
|
|
243
|
-
this.
|
|
244
|
-
this.
|
|
226
|
+
this.packConstantInput(msg);
|
|
227
|
+
this.aegisRoundPacked();
|
|
245
228
|
|
|
246
229
|
return xn;
|
|
247
230
|
}
|
|
248
231
|
|
|
249
232
|
/**
|
|
250
233
|
* Finalizes encryption/decryption and produces an authentication tag.
|
|
251
|
-
* @param adLen - Associated data length in bytes
|
|
252
|
-
* @param msgLen - Message length in bytes
|
|
253
|
-
* @param tagLen - Tag length (16 or 32 bytes)
|
|
254
|
-
* @returns Authentication tag
|
|
255
234
|
*/
|
|
256
235
|
finalize(adLen: number, msgLen: number, tagLen: 16 | 32 = 16): Uint8Array {
|
|
257
236
|
const st = this.st;
|
|
@@ -262,14 +241,20 @@ export class Aegis256BsState {
|
|
|
262
241
|
tmp[2] = ((msgLen * 8) & 0xffffffff) >>> 0;
|
|
263
242
|
tmp[3] = Math.floor((msgLen * 8) / 0x100000000) >>> 0;
|
|
264
243
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
244
|
+
const unpacked = this.st1;
|
|
245
|
+
unpacked.set(st);
|
|
246
|
+
unpack(unpacked);
|
|
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;
|
|
269
252
|
|
|
253
|
+
this.packConstantInput(tmp);
|
|
270
254
|
for (let i = 0; i < 7; i++) {
|
|
271
|
-
this.
|
|
255
|
+
this.aegisRoundPacked();
|
|
272
256
|
}
|
|
257
|
+
unpack(st);
|
|
273
258
|
|
|
274
259
|
if (tagLen === 16) {
|
|
275
260
|
const tag = new Uint8Array(16);
|
|
@@ -307,12 +292,6 @@ export class Aegis256BsState {
|
|
|
307
292
|
|
|
308
293
|
/**
|
|
309
294
|
* Encrypts a message using bitsliced AEGIS-256 (detached mode).
|
|
310
|
-
* @param msg - Plaintext message
|
|
311
|
-
* @param ad - Associated data (authenticated but not encrypted)
|
|
312
|
-
* @param key - 32-byte encryption key
|
|
313
|
-
* @param nonce - 32-byte nonce (must be unique per message with the same key)
|
|
314
|
-
* @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
|
|
315
|
-
* @returns Object containing ciphertext and authentication tag separately
|
|
316
295
|
*/
|
|
317
296
|
export function aegis256BsEncryptDetached(
|
|
318
297
|
msg: Uint8Array,
|
|
@@ -349,12 +328,6 @@ export function aegis256BsEncryptDetached(
|
|
|
349
328
|
|
|
350
329
|
/**
|
|
351
330
|
* Decrypts a message using bitsliced AEGIS-256 (detached mode).
|
|
352
|
-
* @param ct - Ciphertext
|
|
353
|
-
* @param tag - Authentication tag (16 or 32 bytes)
|
|
354
|
-
* @param ad - Associated data (must match what was used during encryption)
|
|
355
|
-
* @param key - 32-byte encryption key
|
|
356
|
-
* @param nonce - 32-byte nonce (must match what was used during encryption)
|
|
357
|
-
* @returns Decrypted plaintext, or null if authentication fails
|
|
358
331
|
*/
|
|
359
332
|
export function aegis256BsDecryptDetached(
|
|
360
333
|
ct: Uint8Array,
|
|
@@ -393,19 +366,80 @@ export function aegis256BsDecryptDetached(
|
|
|
393
366
|
return msg;
|
|
394
367
|
}
|
|
395
368
|
|
|
369
|
+
export function aegis256BsEncryptDetachedInPlace(
|
|
370
|
+
data: Uint8Array,
|
|
371
|
+
ad: Uint8Array,
|
|
372
|
+
key: Uint8Array,
|
|
373
|
+
nonce: Uint8Array,
|
|
374
|
+
tagLen: 16 | 32 = 16,
|
|
375
|
+
): Uint8Array {
|
|
376
|
+
const state = new Aegis256BsState();
|
|
377
|
+
state.init(key, nonce);
|
|
378
|
+
|
|
379
|
+
const adPadded = zeroPad(ad, RATE);
|
|
380
|
+
for (let i = 0; i + RATE <= adPadded.length; i += RATE) {
|
|
381
|
+
state.absorb(adPadded.subarray(i, i + RATE));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const msgLen = data.length;
|
|
385
|
+
const fullBlocksLen = Math.floor(msgLen / RATE) * RATE;
|
|
386
|
+
|
|
387
|
+
for (let i = 0; i < fullBlocksLen; i += RATE) {
|
|
388
|
+
state.encInPlace(data.subarray(i, i + RATE));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (msgLen > fullBlocksLen) {
|
|
392
|
+
const lastPartial = data.subarray(fullBlocksLen);
|
|
393
|
+
const lastBlock = zeroPad(lastPartial, RATE);
|
|
394
|
+
const encBlock = state.enc(lastBlock);
|
|
395
|
+
lastPartial.set(encBlock.subarray(0, lastPartial.length));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return state.finalize(ad.length, msgLen, tagLen);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function aegis256BsDecryptDetachedInPlace(
|
|
402
|
+
data: Uint8Array,
|
|
403
|
+
tag: Uint8Array,
|
|
404
|
+
ad: Uint8Array,
|
|
405
|
+
key: Uint8Array,
|
|
406
|
+
nonce: Uint8Array,
|
|
407
|
+
): boolean {
|
|
408
|
+
const tagLen = tag.length as 16 | 32;
|
|
409
|
+
const state = new Aegis256BsState();
|
|
410
|
+
state.init(key, nonce);
|
|
411
|
+
|
|
412
|
+
const adPadded = zeroPad(ad, RATE);
|
|
413
|
+
for (let i = 0; i + RATE <= adPadded.length; i += RATE) {
|
|
414
|
+
state.absorb(adPadded.subarray(i, i + RATE));
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const msgLen = data.length;
|
|
418
|
+
const fullBlocksLen = Math.floor(msgLen / RATE) * RATE;
|
|
419
|
+
|
|
420
|
+
for (let i = 0; i < fullBlocksLen; i += RATE) {
|
|
421
|
+
state.decInPlace(data.subarray(i, i + RATE));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (msgLen > fullBlocksLen) {
|
|
425
|
+
const lastPartial = data.subarray(fullBlocksLen);
|
|
426
|
+
const decrypted = state.decPartial(lastPartial);
|
|
427
|
+
lastPartial.set(decrypted);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const expectedTag = state.finalize(ad.length, msgLen, tagLen);
|
|
431
|
+
|
|
432
|
+
if (!constantTimeEqual(tag, expectedTag)) {
|
|
433
|
+
data.fill(0);
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
|
|
396
440
|
export const AEGIS_256_BS_NONCE_SIZE = 32;
|
|
397
441
|
export const AEGIS_256_BS_KEY_SIZE = 32;
|
|
398
442
|
|
|
399
|
-
/**
|
|
400
|
-
* Encrypts a message using bitsliced AEGIS-256.
|
|
401
|
-
* Returns a single buffer containing nonce || ciphertext || tag.
|
|
402
|
-
* @param msg - Plaintext message
|
|
403
|
-
* @param ad - Associated data (authenticated but not encrypted)
|
|
404
|
-
* @param key - 32-byte encryption key
|
|
405
|
-
* @param nonce - 32-byte nonce (optional, generates random nonce if not provided)
|
|
406
|
-
* @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
|
|
407
|
-
* @returns Concatenated nonce || ciphertext || tag
|
|
408
|
-
*/
|
|
409
443
|
export function aegis256BsEncrypt(
|
|
410
444
|
msg: Uint8Array,
|
|
411
445
|
ad: Uint8Array,
|
|
@@ -432,15 +466,6 @@ export function aegis256BsEncrypt(
|
|
|
432
466
|
return result;
|
|
433
467
|
}
|
|
434
468
|
|
|
435
|
-
/**
|
|
436
|
-
* Decrypts a message using bitsliced AEGIS-256.
|
|
437
|
-
* Expects input as nonce || ciphertext || tag.
|
|
438
|
-
* @param sealed - Concatenated nonce || ciphertext || tag
|
|
439
|
-
* @param ad - Associated data (must match what was used during encryption)
|
|
440
|
-
* @param key - 32-byte encryption key
|
|
441
|
-
* @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
|
|
442
|
-
* @returns Decrypted plaintext, or null if authentication fails
|
|
443
|
-
*/
|
|
444
469
|
export function aegis256BsDecrypt(
|
|
445
470
|
sealed: Uint8Array,
|
|
446
471
|
ad: Uint8Array,
|
|
@@ -457,14 +482,6 @@ export function aegis256BsDecrypt(
|
|
|
457
482
|
return aegis256BsDecryptDetached(ct, tag, ad, key, nonce);
|
|
458
483
|
}
|
|
459
484
|
|
|
460
|
-
/**
|
|
461
|
-
* Computes a MAC (Message Authentication Code) using bitsliced AEGIS-256.
|
|
462
|
-
* @param data - Data to authenticate
|
|
463
|
-
* @param key - 32-byte key
|
|
464
|
-
* @param nonce - 32-byte nonce (optional, uses zero nonce if null)
|
|
465
|
-
* @param tagLen - Tag length: 16 or 32 bytes (default: 16)
|
|
466
|
-
* @returns Authentication tag
|
|
467
|
-
*/
|
|
468
485
|
export function aegis256BsMac(
|
|
469
486
|
data: Uint8Array,
|
|
470
487
|
key: Uint8Array,
|
|
@@ -482,14 +499,6 @@ export function aegis256BsMac(
|
|
|
482
499
|
return state.finalize(data.length, tagLen, tagLen);
|
|
483
500
|
}
|
|
484
501
|
|
|
485
|
-
/**
|
|
486
|
-
* Verifies a MAC computed using bitsliced AEGIS-256.
|
|
487
|
-
* @param data - Data to verify
|
|
488
|
-
* @param tag - Expected authentication tag (16 or 32 bytes)
|
|
489
|
-
* @param key - 32-byte key
|
|
490
|
-
* @param nonce - 32-byte nonce (optional, uses zero nonce if null)
|
|
491
|
-
* @returns True if the tag is valid, false otherwise
|
|
492
|
-
*/
|
|
493
502
|
export function aegis256BsMacVerify(
|
|
494
503
|
data: Uint8Array,
|
|
495
504
|
tag: Uint8Array,
|
|
@@ -501,18 +510,10 @@ export function aegis256BsMacVerify(
|
|
|
501
510
|
return constantTimeEqual(tag, expectedTag);
|
|
502
511
|
}
|
|
503
512
|
|
|
504
|
-
/**
|
|
505
|
-
* Generates a random 32-byte key for bitsliced AEGIS-256.
|
|
506
|
-
* @returns 32-byte encryption key
|
|
507
|
-
*/
|
|
508
513
|
export function aegis256BsCreateKey(): Uint8Array {
|
|
509
514
|
return randomBytes(AEGIS_256_BS_KEY_SIZE);
|
|
510
515
|
}
|
|
511
516
|
|
|
512
|
-
/**
|
|
513
|
-
* Generates a random 32-byte nonce for bitsliced AEGIS-256.
|
|
514
|
-
* @returns 32-byte nonce
|
|
515
|
-
*/
|
|
516
517
|
export function aegis256BsCreateNonce(): Uint8Array {
|
|
517
518
|
return randomBytes(AEGIS_256_BS_NONCE_SIZE);
|
|
518
519
|
}
|