aegis-aead 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +241 -60
- package/dist/aegis128l-bs.d.ts +162 -0
- package/dist/aegis128l-bs.d.ts.map +1 -0
- package/dist/aegis128l-bs.js +470 -0
- package/dist/aegis128l-bs.js.map +1 -0
- package/dist/aegis128l.d.ts +42 -5
- package/dist/aegis128l.d.ts.map +1 -1
- package/dist/aegis128l.js +79 -5
- package/dist/aegis128l.js.map +1 -1
- package/dist/aegis128x.d.ts +67 -12
- package/dist/aegis128x.d.ts.map +1 -1
- package/dist/aegis128x.js +102 -9
- package/dist/aegis128x.js.map +1 -1
- package/dist/aegis256-bs.d.ts +151 -0
- package/dist/aegis256-bs.d.ts.map +1 -0
- package/dist/aegis256-bs.js +398 -0
- package/dist/aegis256-bs.js.map +1 -0
- package/dist/aegis256.d.ts +42 -5
- package/dist/aegis256.d.ts.map +1 -1
- package/dist/aegis256.js +79 -5
- package/dist/aegis256.js.map +1 -1
- package/dist/aegis256x.d.ts +67 -12
- package/dist/aegis256x.d.ts.map +1 -1
- package/dist/aegis256x.js +102 -9
- package/dist/aegis256x.js.map +1 -1
- package/dist/aes-bs.d.ts +71 -0
- package/dist/aes-bs.d.ts.map +1 -0
- package/dist/aes-bs.js +399 -0
- package/dist/aes-bs.js.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/random.d.ts +22 -0
- package/dist/random.d.ts.map +1 -0
- package/dist/random.js +36 -0
- package/dist/random.js.map +1 -0
- package/package.json +1 -1
- package/src/aegis128l-bs.ts +602 -0
- package/src/aegis128l.ts +112 -5
- package/src/aegis128x.ts +174 -15
- package/src/aegis256-bs.ts +518 -0
- package/src/aegis256.ts +112 -5
- package/src/aegis256x.ts +174 -15
- package/src/aes-bs.ts +459 -0
- package/src/index.ts +66 -0
- package/src/random.ts +41 -0
- package/README.md~ +0 -154
package/src/aegis256x.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
xorBlocksTo,
|
|
11
11
|
zeroPad,
|
|
12
12
|
} from "./aes.js";
|
|
13
|
+
import { randomBytes } from "./random.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* AEGIS-256X cipher state with configurable parallelism degree.
|
|
@@ -359,16 +360,16 @@ export class Aegis256XState {
|
|
|
359
360
|
}
|
|
360
361
|
|
|
361
362
|
/**
|
|
362
|
-
* Encrypts a message using AEGIS-256X.
|
|
363
|
+
* Encrypts a message using AEGIS-256X (detached mode).
|
|
363
364
|
* @param msg - Plaintext message
|
|
364
365
|
* @param ad - Associated data (authenticated but not encrypted)
|
|
365
366
|
* @param key - 32-byte encryption key
|
|
366
367
|
* @param nonce - 32-byte nonce (must be unique per message with the same key)
|
|
367
368
|
* @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
|
|
368
369
|
* @param degree - Parallelism degree (default: 2)
|
|
369
|
-
* @returns Object containing ciphertext and authentication tag
|
|
370
|
+
* @returns Object containing ciphertext and authentication tag separately
|
|
370
371
|
*/
|
|
371
|
-
export function
|
|
372
|
+
export function aegis256XEncryptDetached(
|
|
372
373
|
msg: Uint8Array,
|
|
373
374
|
ad: Uint8Array,
|
|
374
375
|
key: Uint8Array,
|
|
@@ -406,7 +407,7 @@ export function aegis256XEncrypt(
|
|
|
406
407
|
}
|
|
407
408
|
|
|
408
409
|
/**
|
|
409
|
-
* Decrypts a message using AEGIS-256X.
|
|
410
|
+
* Decrypts a message using AEGIS-256X (detached mode).
|
|
410
411
|
* @param ct - Ciphertext
|
|
411
412
|
* @param tag - Authentication tag (16 or 32 bytes)
|
|
412
413
|
* @param ad - Associated data (must match what was used during encryption)
|
|
@@ -415,7 +416,7 @@ export function aegis256XEncrypt(
|
|
|
415
416
|
* @param degree - Parallelism degree (default: 2)
|
|
416
417
|
* @returns Decrypted plaintext, or null if authentication fails
|
|
417
418
|
*/
|
|
418
|
-
export function
|
|
419
|
+
export function aegis256XDecryptDetached(
|
|
419
420
|
ct: Uint8Array,
|
|
420
421
|
tag: Uint8Array,
|
|
421
422
|
ad: Uint8Array,
|
|
@@ -460,41 +461,169 @@ export function aegis256XDecrypt(
|
|
|
460
461
|
return msg;
|
|
461
462
|
}
|
|
462
463
|
|
|
464
|
+
/** Nonce size for AEGIS-256X in bytes. */
|
|
465
|
+
export const AEGIS_256X_NONCE_SIZE = 32;
|
|
466
|
+
|
|
467
|
+
/** Key size for AEGIS-256X in bytes. */
|
|
468
|
+
export const AEGIS_256X_KEY_SIZE = 32;
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Encrypts a message using AEGIS-256X.
|
|
472
|
+
* Returns a single buffer containing nonce || ciphertext || tag.
|
|
473
|
+
* @param msg - Plaintext message
|
|
474
|
+
* @param ad - Associated data (authenticated but not encrypted)
|
|
475
|
+
* @param key - 32-byte encryption key
|
|
476
|
+
* @param nonce - 32-byte nonce (optional, generates random nonce if not provided)
|
|
477
|
+
* @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
|
|
478
|
+
* @param degree - Parallelism degree (default: 2)
|
|
479
|
+
* @returns Concatenated nonce || ciphertext || tag
|
|
480
|
+
*/
|
|
481
|
+
export function aegis256XEncrypt(
|
|
482
|
+
msg: Uint8Array,
|
|
483
|
+
ad: Uint8Array,
|
|
484
|
+
key: Uint8Array,
|
|
485
|
+
nonce: Uint8Array | null = null,
|
|
486
|
+
tagLen: 16 | 32 = 16,
|
|
487
|
+
degree: number = 2,
|
|
488
|
+
): Uint8Array {
|
|
489
|
+
const actualNonce = nonce ?? randomBytes(AEGIS_256X_NONCE_SIZE);
|
|
490
|
+
const state = new Aegis256XState(degree);
|
|
491
|
+
const rateBytes = (128 * degree) / 8;
|
|
492
|
+
|
|
493
|
+
state.init(key, actualNonce);
|
|
494
|
+
|
|
495
|
+
const adPadded = zeroPad(ad, rateBytes);
|
|
496
|
+
for (let i = 0; i + rateBytes <= adPadded.length; i += rateBytes) {
|
|
497
|
+
state.absorb(adPadded.subarray(i, i + rateBytes));
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const nonceSize = AEGIS_256X_NONCE_SIZE;
|
|
501
|
+
const result = new Uint8Array(nonceSize + msg.length + tagLen);
|
|
502
|
+
result.set(actualNonce, 0);
|
|
503
|
+
|
|
504
|
+
const fullBlocks = Math.floor(msg.length / rateBytes) * rateBytes;
|
|
505
|
+
for (let i = 0; i < fullBlocks; i += rateBytes) {
|
|
506
|
+
state.encTo(
|
|
507
|
+
msg.subarray(i, i + rateBytes),
|
|
508
|
+
result.subarray(nonceSize + i, nonceSize + i + rateBytes),
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (msg.length > fullBlocks) {
|
|
513
|
+
const lastBlock = zeroPad(msg.subarray(fullBlocks), rateBytes);
|
|
514
|
+
const encBlock = state.enc(lastBlock);
|
|
515
|
+
result.set(
|
|
516
|
+
encBlock.subarray(0, msg.length - fullBlocks),
|
|
517
|
+
nonceSize + fullBlocks,
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const tag = state.finalize(
|
|
522
|
+
BigInt(ad.length * 8),
|
|
523
|
+
BigInt(msg.length * 8),
|
|
524
|
+
tagLen,
|
|
525
|
+
);
|
|
526
|
+
result.set(tag, nonceSize + msg.length);
|
|
527
|
+
|
|
528
|
+
return result;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Decrypts a message using AEGIS-256X.
|
|
533
|
+
* Expects input as nonce || ciphertext || tag.
|
|
534
|
+
* @param sealed - Concatenated nonce || ciphertext || tag
|
|
535
|
+
* @param ad - Associated data (must match what was used during encryption)
|
|
536
|
+
* @param key - 32-byte encryption key
|
|
537
|
+
* @param tagLen - Authentication tag length: 16 or 32 bytes (default: 16)
|
|
538
|
+
* @param degree - Parallelism degree (default: 2)
|
|
539
|
+
* @returns Decrypted plaintext, or null if authentication fails
|
|
540
|
+
*/
|
|
541
|
+
export function aegis256XDecrypt(
|
|
542
|
+
sealed: Uint8Array,
|
|
543
|
+
ad: Uint8Array,
|
|
544
|
+
key: Uint8Array,
|
|
545
|
+
tagLen: 16 | 32 = 16,
|
|
546
|
+
degree: number = 2,
|
|
547
|
+
): Uint8Array | null {
|
|
548
|
+
const nonceSize = AEGIS_256X_NONCE_SIZE;
|
|
549
|
+
if (sealed.length < nonceSize + tagLen) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
const nonce = sealed.subarray(0, nonceSize);
|
|
553
|
+
const ct = sealed.subarray(nonceSize, sealed.length - tagLen);
|
|
554
|
+
const tag = sealed.subarray(sealed.length - tagLen);
|
|
555
|
+
return aegis256XDecryptDetached(ct, tag, ad, key, nonce, degree);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/** AEGIS-256X2 encryption - detached mode (degree=2). */
|
|
559
|
+
export const aegis256X2EncryptDetached = (
|
|
560
|
+
msg: Uint8Array,
|
|
561
|
+
ad: Uint8Array,
|
|
562
|
+
key: Uint8Array,
|
|
563
|
+
nonce: Uint8Array,
|
|
564
|
+
tagLen: 16 | 32 = 16,
|
|
565
|
+
) => aegis256XEncryptDetached(msg, ad, key, nonce, tagLen, 2);
|
|
566
|
+
|
|
567
|
+
/** AEGIS-256X2 decryption - detached mode (degree=2). */
|
|
568
|
+
export const aegis256X2DecryptDetached = (
|
|
569
|
+
ct: Uint8Array,
|
|
570
|
+
tag: Uint8Array,
|
|
571
|
+
ad: Uint8Array,
|
|
572
|
+
key: Uint8Array,
|
|
573
|
+
nonce: Uint8Array,
|
|
574
|
+
) => aegis256XDecryptDetached(ct, tag, ad, key, nonce, 2);
|
|
575
|
+
|
|
576
|
+
/** AEGIS-256X4 encryption - detached mode (degree=4). */
|
|
577
|
+
export const aegis256X4EncryptDetached = (
|
|
578
|
+
msg: Uint8Array,
|
|
579
|
+
ad: Uint8Array,
|
|
580
|
+
key: Uint8Array,
|
|
581
|
+
nonce: Uint8Array,
|
|
582
|
+
tagLen: 16 | 32 = 16,
|
|
583
|
+
) => aegis256XEncryptDetached(msg, ad, key, nonce, tagLen, 4);
|
|
584
|
+
|
|
585
|
+
/** AEGIS-256X4 decryption - detached mode (degree=4). */
|
|
586
|
+
export const aegis256X4DecryptDetached = (
|
|
587
|
+
ct: Uint8Array,
|
|
588
|
+
tag: Uint8Array,
|
|
589
|
+
ad: Uint8Array,
|
|
590
|
+
key: Uint8Array,
|
|
591
|
+
nonce: Uint8Array,
|
|
592
|
+
) => aegis256XDecryptDetached(ct, tag, ad, key, nonce, 4);
|
|
593
|
+
|
|
463
594
|
/** AEGIS-256X2 encryption (degree=2). */
|
|
464
595
|
export const aegis256X2Encrypt = (
|
|
465
596
|
msg: Uint8Array,
|
|
466
597
|
ad: Uint8Array,
|
|
467
598
|
key: Uint8Array,
|
|
468
|
-
nonce: Uint8Array,
|
|
599
|
+
nonce: Uint8Array | null = null,
|
|
469
600
|
tagLen: 16 | 32 = 16,
|
|
470
601
|
) => aegis256XEncrypt(msg, ad, key, nonce, tagLen, 2);
|
|
471
602
|
|
|
472
603
|
/** AEGIS-256X2 decryption (degree=2). */
|
|
473
604
|
export const aegis256X2Decrypt = (
|
|
474
|
-
|
|
475
|
-
tag: Uint8Array,
|
|
605
|
+
sealed: Uint8Array,
|
|
476
606
|
ad: Uint8Array,
|
|
477
607
|
key: Uint8Array,
|
|
478
|
-
|
|
479
|
-
) => aegis256XDecrypt(
|
|
608
|
+
tagLen: 16 | 32 = 16,
|
|
609
|
+
) => aegis256XDecrypt(sealed, ad, key, tagLen, 2);
|
|
480
610
|
|
|
481
611
|
/** AEGIS-256X4 encryption (degree=4). */
|
|
482
612
|
export const aegis256X4Encrypt = (
|
|
483
613
|
msg: Uint8Array,
|
|
484
614
|
ad: Uint8Array,
|
|
485
615
|
key: Uint8Array,
|
|
486
|
-
nonce: Uint8Array,
|
|
616
|
+
nonce: Uint8Array | null = null,
|
|
487
617
|
tagLen: 16 | 32 = 16,
|
|
488
618
|
) => aegis256XEncrypt(msg, ad, key, nonce, tagLen, 4);
|
|
489
619
|
|
|
490
620
|
/** AEGIS-256X4 decryption (degree=4). */
|
|
491
621
|
export const aegis256X4Decrypt = (
|
|
492
|
-
|
|
493
|
-
tag: Uint8Array,
|
|
622
|
+
sealed: Uint8Array,
|
|
494
623
|
ad: Uint8Array,
|
|
495
624
|
key: Uint8Array,
|
|
496
|
-
|
|
497
|
-
) => aegis256XDecrypt(
|
|
625
|
+
tagLen: 16 | 32 = 16,
|
|
626
|
+
) => aegis256XDecrypt(sealed, ad, key, tagLen, 4);
|
|
498
627
|
|
|
499
628
|
/**
|
|
500
629
|
* Computes a MAC (Message Authentication Code) using AEGIS-256X.
|
|
@@ -577,3 +706,33 @@ export const aegis256X4MacVerify = (
|
|
|
577
706
|
key: Uint8Array,
|
|
578
707
|
nonce: Uint8Array | null = null,
|
|
579
708
|
) => aegis256XMacVerify(data, tag, key, nonce, 4);
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Generates a random 32-byte key for AEGIS-256X.
|
|
712
|
+
* @returns 32-byte encryption key
|
|
713
|
+
* @throws Error if no cryptographic random source is available
|
|
714
|
+
*/
|
|
715
|
+
export function aegis256XCreateKey(): Uint8Array {
|
|
716
|
+
return randomBytes(AEGIS_256X_KEY_SIZE);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Generates a random 32-byte nonce for AEGIS-256X.
|
|
721
|
+
* @returns 32-byte nonce
|
|
722
|
+
* @throws Error if no cryptographic random source is available
|
|
723
|
+
*/
|
|
724
|
+
export function aegis256XCreateNonce(): Uint8Array {
|
|
725
|
+
return randomBytes(AEGIS_256X_NONCE_SIZE);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/** AEGIS-256X2 key generation (degree=2). */
|
|
729
|
+
export const aegis256X2CreateKey = aegis256XCreateKey;
|
|
730
|
+
|
|
731
|
+
/** AEGIS-256X2 nonce generation (degree=2). */
|
|
732
|
+
export const aegis256X2CreateNonce = aegis256XCreateNonce;
|
|
733
|
+
|
|
734
|
+
/** AEGIS-256X4 key generation (degree=4). */
|
|
735
|
+
export const aegis256X4CreateKey = aegis256XCreateKey;
|
|
736
|
+
|
|
737
|
+
/** AEGIS-256X4 nonce generation (degree=4). */
|
|
738
|
+
export const aegis256X4CreateNonce = aegis256XCreateNonce;
|
package/src/aes-bs.ts
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitsliced AES implementation using 32-bit integers.
|
|
3
|
+
* Processes 8 AES blocks simultaneously for constant-time operation.
|
|
4
|
+
* Based on the barrel-shiftrows representation by Adomnicai and Peyrin.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Bitsliced representation of 8 AES blocks.
|
|
9
|
+
* 32 uint32 words where each bit position corresponds to one of 8 blocks.
|
|
10
|
+
*/
|
|
11
|
+
export type AesBlocks = Uint32Array;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A single AES block as 4 uint32 words (little-endian).
|
|
15
|
+
*/
|
|
16
|
+
export type AesBlock = Uint32Array;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new bitsliced state for 8 AES blocks.
|
|
20
|
+
*/
|
|
21
|
+
export function createAesBlocks(): AesBlocks {
|
|
22
|
+
return new Uint32Array(32);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a new AES block (4 uint32 words).
|
|
27
|
+
*/
|
|
28
|
+
export function createAesBlock(): AesBlock {
|
|
29
|
+
return new Uint32Array(4);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Swap-move operation used in pack/unpack transformations.
|
|
34
|
+
* Swaps bits at positions n between a and b using a mask.
|
|
35
|
+
*/
|
|
36
|
+
function swapmove(
|
|
37
|
+
st: Uint32Array,
|
|
38
|
+
aIdx: number,
|
|
39
|
+
bIdx: number,
|
|
40
|
+
mask: number,
|
|
41
|
+
n: number,
|
|
42
|
+
): void {
|
|
43
|
+
const tmp = ((st[bIdx]! ^ (st[aIdx]! >>> n)) & mask) >>> 0;
|
|
44
|
+
st[bIdx] = (st[bIdx]! ^ tmp) >>> 0;
|
|
45
|
+
st[aIdx] = (st[aIdx]! ^ ((tmp << n) >>> 0)) >>> 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 32-bit left rotation.
|
|
50
|
+
*/
|
|
51
|
+
function rotl32(x: number, b: number): number {
|
|
52
|
+
return ((x << b) | (x >>> (32 - b))) >>> 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* S-box implementation using bitsliced logic gates.
|
|
57
|
+
* Maximov & Ekdahl circuit.
|
|
58
|
+
*/
|
|
59
|
+
function sbox(st: Uint32Array, offset: number): void {
|
|
60
|
+
let u0 = st[offset]!;
|
|
61
|
+
let u1 = st[offset + 1]!;
|
|
62
|
+
let u2 = st[offset + 2]!;
|
|
63
|
+
let u3 = st[offset + 3]!;
|
|
64
|
+
let u4 = st[offset + 4]!;
|
|
65
|
+
let u5 = st[offset + 5]!;
|
|
66
|
+
let u6 = st[offset + 6]!;
|
|
67
|
+
let u7 = st[offset + 7]!;
|
|
68
|
+
|
|
69
|
+
const z24 = (u3 ^ u4) >>> 0;
|
|
70
|
+
const q17 = (u1 ^ u7) >>> 0;
|
|
71
|
+
const q16 = (u5 ^ q17) >>> 0;
|
|
72
|
+
const q0 = (z24 ^ q16) >>> 0;
|
|
73
|
+
const q7 = (z24 ^ u1 ^ u6) >>> 0;
|
|
74
|
+
const q2 = (u2 ^ q0) >>> 0;
|
|
75
|
+
const q1 = (q7 ^ q2) >>> 0;
|
|
76
|
+
const q3 = (u0 ^ q7) >>> 0;
|
|
77
|
+
const q4 = (u0 ^ q2) >>> 0;
|
|
78
|
+
const q5 = (u1 ^ q4) >>> 0;
|
|
79
|
+
const q6 = (u2 ^ u3) >>> 0;
|
|
80
|
+
const q10 = (q6 ^ q7) >>> 0;
|
|
81
|
+
const q8 = (u0 ^ q10) >>> 0;
|
|
82
|
+
const q9 = (q8 ^ q2) >>> 0;
|
|
83
|
+
const q12 = (z24 ^ q17) >>> 0;
|
|
84
|
+
const q15 = (u7 ^ q4) >>> 0;
|
|
85
|
+
const q13 = (z24 ^ q15) >>> 0;
|
|
86
|
+
const q14 = (q15 ^ q0) >>> 0;
|
|
87
|
+
const q11 = u5;
|
|
88
|
+
|
|
89
|
+
// NAND(x, y) = ~(x & y)
|
|
90
|
+
// NOR(x, y) = ~(x | y)
|
|
91
|
+
// XNOR(x, y) = ~(x ^ y)
|
|
92
|
+
// MUX(s, x, y) = (x & s) | (y & ~s)
|
|
93
|
+
const nand = (x: number, y: number) => ~(x & y) >>> 0;
|
|
94
|
+
const nor = (x: number, y: number) => ~(x | y) >>> 0;
|
|
95
|
+
const xnor = (x: number, y: number) => ~(x ^ y) >>> 0;
|
|
96
|
+
const mux = (s: number, x: number, y: number) =>
|
|
97
|
+
(((x & s) >>> 0) | ((y & ~s) >>> 0)) >>> 0;
|
|
98
|
+
|
|
99
|
+
const t20 = nand(q6, q12);
|
|
100
|
+
const t21 = nand(q3, q14);
|
|
101
|
+
const t22 = nand(q1, q16);
|
|
102
|
+
const x0 = (nor(q3, q14) ^ nand(q0, q7) ^ (t20 ^ t22)) >>> 0;
|
|
103
|
+
const x1 = (nor(q4, q13) ^ nand(q10, q11) ^ (t21 ^ t20)) >>> 0;
|
|
104
|
+
const x2 = (nor(q2, q17) ^ nand(q5, q9) ^ (t21 ^ t22)) >>> 0;
|
|
105
|
+
const x3 = (nor(q8, q15) ^ nand(q2, q17) ^ (t21 ^ nand(q4, q13))) >>> 0;
|
|
106
|
+
|
|
107
|
+
const t2 = xnor(nand(x0, x2), nor(x1, x3));
|
|
108
|
+
const y0 = mux(x2, t2, x3);
|
|
109
|
+
const y2 = mux(x0, t2, x1);
|
|
110
|
+
const y1 = mux(t2, x3, mux(x1, x2, 0xffffffff));
|
|
111
|
+
const y3 = mux(t2, x1, mux(x3, x0, 0xffffffff));
|
|
112
|
+
const y02 = (y2 ^ y0) >>> 0;
|
|
113
|
+
const y13 = (y3 ^ y1) >>> 0;
|
|
114
|
+
const y23 = (y3 ^ y2) >>> 0;
|
|
115
|
+
const y01 = (y1 ^ y0) >>> 0;
|
|
116
|
+
const y00 = (y02 ^ y13) >>> 0;
|
|
117
|
+
|
|
118
|
+
const n0 = nand(y01, q11);
|
|
119
|
+
const n1 = nand(y0, q12);
|
|
120
|
+
const n2 = nand(y1, q0);
|
|
121
|
+
const n3 = nand(y23, q17);
|
|
122
|
+
const n4 = nand(y2, q5);
|
|
123
|
+
const n5 = nand(y3, q15);
|
|
124
|
+
const n6 = nand(y13, q14);
|
|
125
|
+
const n7 = nand(y00, q16);
|
|
126
|
+
const n8 = nand(y02, q13);
|
|
127
|
+
const n9 = nand(y01, q7);
|
|
128
|
+
const n10 = nand(y0, q10);
|
|
129
|
+
const n11 = nand(y1, q6);
|
|
130
|
+
const n12 = nand(y23, q2);
|
|
131
|
+
const n13 = nand(y2, q9);
|
|
132
|
+
const n14 = nand(y3, q8);
|
|
133
|
+
const n15 = nand(y13, q3);
|
|
134
|
+
const n16 = nand(y00, q1);
|
|
135
|
+
const n17 = nand(y02, q4);
|
|
136
|
+
|
|
137
|
+
const h1 = (n4 ^ n1 ^ n5) >>> 0;
|
|
138
|
+
u2 = xnor(n2, h1);
|
|
139
|
+
const h2 = (n9 ^ n15) >>> 0;
|
|
140
|
+
u6 = xnor(h2, (n11 ^ n17) >>> 0);
|
|
141
|
+
const h4 = (n11 ^ n14) >>> 0;
|
|
142
|
+
const h5 = (n9 ^ n12) >>> 0;
|
|
143
|
+
u5 = (h4 ^ h5) >>> 0;
|
|
144
|
+
const h7 = (u2 ^ u6) >>> 0;
|
|
145
|
+
const h8 = (n10 ^ h7) >>> 0;
|
|
146
|
+
u7 = xnor((n16 ^ h2) >>> 0, h8);
|
|
147
|
+
const h9 = (n8 ^ h1) >>> 0;
|
|
148
|
+
const h10 = (n13 ^ h8) >>> 0;
|
|
149
|
+
u3 = (h5 ^ h10) >>> 0;
|
|
150
|
+
const h13 = (h4 ^ n7 ^ h9 ^ h10) >>> 0;
|
|
151
|
+
u4 = (n1 ^ h13) >>> 0;
|
|
152
|
+
const h14 = xnor(n0, u7);
|
|
153
|
+
u1 = xnor(n6, (h7 ^ h9 ^ h14) >>> 0);
|
|
154
|
+
u0 = (h13 ^ n3 ^ n4 ^ h14) >>> 0;
|
|
155
|
+
|
|
156
|
+
st[offset] = u0;
|
|
157
|
+
st[offset + 1] = u1;
|
|
158
|
+
st[offset + 2] = u2;
|
|
159
|
+
st[offset + 3] = u3;
|
|
160
|
+
st[offset + 4] = u4;
|
|
161
|
+
st[offset + 5] = u5;
|
|
162
|
+
st[offset + 6] = u6;
|
|
163
|
+
st[offset + 7] = u7;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Apply S-box to all 4 byte positions.
|
|
168
|
+
*/
|
|
169
|
+
function sboxes(st: AesBlocks): void {
|
|
170
|
+
for (let i = 0; i < 4; i++) {
|
|
171
|
+
sbox(st, 8 * i);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* ShiftRows operation in bitsliced form.
|
|
177
|
+
*/
|
|
178
|
+
function shiftrows(st: AesBlocks): void {
|
|
179
|
+
for (let i = 8; i < 16; i++) {
|
|
180
|
+
st[i] = rotl32(st[i]!, 24);
|
|
181
|
+
}
|
|
182
|
+
for (let i = 16; i < 24; i++) {
|
|
183
|
+
st[i] = rotl32(st[i]!, 16);
|
|
184
|
+
}
|
|
185
|
+
for (let i = 24; i < 32; i++) {
|
|
186
|
+
st[i] = rotl32(st[i]!, 8);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* MixColumns operation in bitsliced form.
|
|
192
|
+
*/
|
|
193
|
+
function mixcolumns(st: AesBlocks): void {
|
|
194
|
+
const t2_0 = (st[0]! ^ st[8]!) >>> 0;
|
|
195
|
+
const t2_1 = (st[8]! ^ st[16]!) >>> 0;
|
|
196
|
+
const t2_2 = (st[16]! ^ st[24]!) >>> 0;
|
|
197
|
+
const t2_3 = (st[24]! ^ st[0]!) >>> 0;
|
|
198
|
+
|
|
199
|
+
let t0_0 = (st[7]! ^ st[15]!) >>> 0;
|
|
200
|
+
let t0_1 = (st[15]! ^ st[23]!) >>> 0;
|
|
201
|
+
let t0_2 = (st[23]! ^ st[31]!) >>> 0;
|
|
202
|
+
let t0_3 = (st[31]! ^ st[7]!) >>> 0;
|
|
203
|
+
|
|
204
|
+
let t = st[7]!;
|
|
205
|
+
st[7] = (t2_0 ^ t0_2 ^ st[15]!) >>> 0;
|
|
206
|
+
st[15] = (t2_1 ^ t0_2 ^ t) >>> 0;
|
|
207
|
+
t = st[23]!;
|
|
208
|
+
st[23] = (t2_2 ^ t0_0 ^ st[31]!) >>> 0;
|
|
209
|
+
st[31] = (t2_3 ^ t0_0 ^ t) >>> 0;
|
|
210
|
+
|
|
211
|
+
let t1_0 = (st[6]! ^ st[14]!) >>> 0;
|
|
212
|
+
let t1_1 = (st[14]! ^ st[22]!) >>> 0;
|
|
213
|
+
let t1_2 = (st[22]! ^ st[30]!) >>> 0;
|
|
214
|
+
let t1_3 = (st[30]! ^ st[6]!) >>> 0;
|
|
215
|
+
|
|
216
|
+
t = st[6]!;
|
|
217
|
+
let t_bis = st[14]!;
|
|
218
|
+
st[6] = (t0_0 ^ t2_0 ^ st[14]! ^ t1_2) >>> 0;
|
|
219
|
+
st[14] = (t0_1 ^ t2_1 ^ t1_2 ^ t) >>> 0;
|
|
220
|
+
t = st[22]!;
|
|
221
|
+
st[22] = (t0_2 ^ t2_2 ^ t1_3 ^ t_bis) >>> 0;
|
|
222
|
+
st[30] = (t0_3 ^ t2_3 ^ t1_0 ^ t) >>> 0;
|
|
223
|
+
|
|
224
|
+
t0_0 = (st[5]! ^ st[13]!) >>> 0;
|
|
225
|
+
t0_1 = (st[13]! ^ st[21]!) >>> 0;
|
|
226
|
+
t0_2 = (st[21]! ^ st[29]!) >>> 0;
|
|
227
|
+
t0_3 = (st[29]! ^ st[5]!) >>> 0;
|
|
228
|
+
|
|
229
|
+
t = st[5]!;
|
|
230
|
+
t_bis = st[13]!;
|
|
231
|
+
st[5] = (t1_0 ^ t0_1 ^ st[29]!) >>> 0;
|
|
232
|
+
st[13] = (t1_1 ^ t0_2 ^ t) >>> 0;
|
|
233
|
+
t = st[21]!;
|
|
234
|
+
st[21] = (t1_2 ^ t0_3 ^ t_bis) >>> 0;
|
|
235
|
+
st[29] = (t1_3 ^ t0_0 ^ t) >>> 0;
|
|
236
|
+
|
|
237
|
+
t1_0 = (st[4]! ^ st[12]!) >>> 0;
|
|
238
|
+
t1_1 = (st[12]! ^ st[20]!) >>> 0;
|
|
239
|
+
t1_2 = (st[20]! ^ st[28]!) >>> 0;
|
|
240
|
+
t1_3 = (st[28]! ^ st[4]!) >>> 0;
|
|
241
|
+
|
|
242
|
+
t = st[4]!;
|
|
243
|
+
t_bis = st[12]!;
|
|
244
|
+
st[4] = (t0_0 ^ t2_0 ^ t1_1 ^ st[28]!) >>> 0;
|
|
245
|
+
st[12] = (t0_1 ^ t2_1 ^ t1_2 ^ t) >>> 0;
|
|
246
|
+
t = st[20]!;
|
|
247
|
+
st[20] = (t0_2 ^ t2_2 ^ t1_3 ^ t_bis) >>> 0;
|
|
248
|
+
st[28] = (t0_3 ^ t2_3 ^ t1_0 ^ t) >>> 0;
|
|
249
|
+
|
|
250
|
+
t0_0 = (st[3]! ^ st[11]!) >>> 0;
|
|
251
|
+
t0_1 = (st[11]! ^ st[19]!) >>> 0;
|
|
252
|
+
t0_2 = (st[19]! ^ st[27]!) >>> 0;
|
|
253
|
+
t0_3 = (st[27]! ^ st[3]!) >>> 0;
|
|
254
|
+
|
|
255
|
+
t = st[3]!;
|
|
256
|
+
t_bis = st[11]!;
|
|
257
|
+
st[3] = (t1_0 ^ t2_0 ^ t0_1 ^ st[27]!) >>> 0;
|
|
258
|
+
st[11] = (t1_1 ^ t2_1 ^ t0_2 ^ t) >>> 0;
|
|
259
|
+
t = st[19]!;
|
|
260
|
+
st[19] = (t1_2 ^ t2_2 ^ t0_3 ^ t_bis) >>> 0;
|
|
261
|
+
st[27] = (t1_3 ^ t2_3 ^ t0_0 ^ t) >>> 0;
|
|
262
|
+
|
|
263
|
+
t1_0 = (st[2]! ^ st[10]!) >>> 0;
|
|
264
|
+
t1_1 = (st[10]! ^ st[18]!) >>> 0;
|
|
265
|
+
t1_2 = (st[18]! ^ st[26]!) >>> 0;
|
|
266
|
+
t1_3 = (st[26]! ^ st[2]!) >>> 0;
|
|
267
|
+
|
|
268
|
+
t = st[2]!;
|
|
269
|
+
t_bis = st[10]!;
|
|
270
|
+
st[2] = (t0_0 ^ t1_1 ^ st[26]!) >>> 0;
|
|
271
|
+
st[10] = (t0_1 ^ t1_2 ^ t) >>> 0;
|
|
272
|
+
t = st[18]!;
|
|
273
|
+
st[18] = (t0_2 ^ t1_3 ^ t_bis) >>> 0;
|
|
274
|
+
st[26] = (t0_3 ^ t1_0 ^ t) >>> 0;
|
|
275
|
+
|
|
276
|
+
t0_0 = (st[1]! ^ st[9]!) >>> 0;
|
|
277
|
+
t0_1 = (st[9]! ^ st[17]!) >>> 0;
|
|
278
|
+
t0_2 = (st[17]! ^ st[25]!) >>> 0;
|
|
279
|
+
t0_3 = (st[25]! ^ st[1]!) >>> 0;
|
|
280
|
+
|
|
281
|
+
t = st[1]!;
|
|
282
|
+
t_bis = st[9]!;
|
|
283
|
+
st[1] = (t1_0 ^ t0_1 ^ st[25]!) >>> 0;
|
|
284
|
+
st[9] = (t1_1 ^ t0_2 ^ t) >>> 0;
|
|
285
|
+
t = st[17]!;
|
|
286
|
+
st[17] = (t1_2 ^ t0_3 ^ t_bis) >>> 0;
|
|
287
|
+
st[25] = (t1_3 ^ t0_0 ^ t) >>> 0;
|
|
288
|
+
|
|
289
|
+
t = st[0]!;
|
|
290
|
+
t_bis = st[8]!;
|
|
291
|
+
st[0] = (t0_0 ^ t2_1 ^ st[24]!) >>> 0;
|
|
292
|
+
st[8] = (t0_1 ^ t2_2 ^ t) >>> 0;
|
|
293
|
+
t = st[16]!;
|
|
294
|
+
st[16] = (t0_2 ^ t2_3 ^ t_bis) >>> 0;
|
|
295
|
+
st[24] = (t0_3 ^ t2_0 ^ t) >>> 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Complete AES round (SubBytes + ShiftRows + MixColumns).
|
|
300
|
+
* Note: AddRoundKey is handled separately in AEGIS.
|
|
301
|
+
*/
|
|
302
|
+
export function aesRound(st: AesBlocks): void {
|
|
303
|
+
sboxes(st);
|
|
304
|
+
shiftrows(st);
|
|
305
|
+
mixcolumns(st);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Pack 8 AES blocks into bitsliced representation.
|
|
310
|
+
*/
|
|
311
|
+
export function pack(st: AesBlocks): void {
|
|
312
|
+
for (let i = 0; i < 8; i++) {
|
|
313
|
+
swapmove(st, i, i + 8, 0x00ff00ff, 8);
|
|
314
|
+
swapmove(st, i + 16, i + 24, 0x00ff00ff, 8);
|
|
315
|
+
}
|
|
316
|
+
for (let i = 0; i < 16; i++) {
|
|
317
|
+
swapmove(st, i, i + 16, 0x0000ffff, 16);
|
|
318
|
+
}
|
|
319
|
+
for (let i = 0; i < 32; i += 8) {
|
|
320
|
+
swapmove(st, i + 1, i, 0x55555555, 1);
|
|
321
|
+
swapmove(st, i + 3, i + 2, 0x55555555, 1);
|
|
322
|
+
swapmove(st, i + 5, i + 4, 0x55555555, 1);
|
|
323
|
+
swapmove(st, i + 7, i + 6, 0x55555555, 1);
|
|
324
|
+
swapmove(st, i + 2, i, 0x33333333, 2);
|
|
325
|
+
swapmove(st, i + 3, i + 1, 0x33333333, 2);
|
|
326
|
+
swapmove(st, i + 6, i + 4, 0x33333333, 2);
|
|
327
|
+
swapmove(st, i + 7, i + 5, 0x33333333, 2);
|
|
328
|
+
swapmove(st, i + 4, i, 0x0f0f0f0f, 4);
|
|
329
|
+
swapmove(st, i + 5, i + 1, 0x0f0f0f0f, 4);
|
|
330
|
+
swapmove(st, i + 6, i + 2, 0x0f0f0f0f, 4);
|
|
331
|
+
swapmove(st, i + 7, i + 3, 0x0f0f0f0f, 4);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Unpack bitsliced representation to 8 AES blocks.
|
|
337
|
+
*/
|
|
338
|
+
export function unpack(st: AesBlocks): void {
|
|
339
|
+
for (let i = 0; i < 32; i += 8) {
|
|
340
|
+
swapmove(st, i + 1, i, 0x55555555, 1);
|
|
341
|
+
swapmove(st, i + 3, i + 2, 0x55555555, 1);
|
|
342
|
+
swapmove(st, i + 5, i + 4, 0x55555555, 1);
|
|
343
|
+
swapmove(st, i + 7, i + 6, 0x55555555, 1);
|
|
344
|
+
swapmove(st, i + 2, i, 0x33333333, 2);
|
|
345
|
+
swapmove(st, i + 3, i + 1, 0x33333333, 2);
|
|
346
|
+
swapmove(st, i + 6, i + 4, 0x33333333, 2);
|
|
347
|
+
swapmove(st, i + 7, i + 5, 0x33333333, 2);
|
|
348
|
+
swapmove(st, i + 4, i, 0x0f0f0f0f, 4);
|
|
349
|
+
swapmove(st, i + 5, i + 1, 0x0f0f0f0f, 4);
|
|
350
|
+
swapmove(st, i + 6, i + 2, 0x0f0f0f0f, 4);
|
|
351
|
+
swapmove(st, i + 7, i + 3, 0x0f0f0f0f, 4);
|
|
352
|
+
}
|
|
353
|
+
for (let i = 0; i < 16; i++) {
|
|
354
|
+
swapmove(st, i, i + 16, 0x0000ffff, 16);
|
|
355
|
+
}
|
|
356
|
+
for (let i = 0; i < 8; i++) {
|
|
357
|
+
swapmove(st, i, i + 8, 0x00ff00ff, 8);
|
|
358
|
+
swapmove(st, i + 16, i + 24, 0x00ff00ff, 8);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Pack only blocks 0 and 4 (used for constant inputs in AEGIS-128L).
|
|
364
|
+
*/
|
|
365
|
+
export function pack04(st: AesBlocks): void {
|
|
366
|
+
swapmove(st, 0, 0 + 8, 0x00ff00ff, 8);
|
|
367
|
+
swapmove(st, 0 + 16, 0 + 24, 0x00ff00ff, 8);
|
|
368
|
+
swapmove(st, 4, 4 + 8, 0x00ff00ff, 8);
|
|
369
|
+
swapmove(st, 4 + 16, 4 + 24, 0x00ff00ff, 8);
|
|
370
|
+
|
|
371
|
+
swapmove(st, 0, 0 + 16, 0x0000ffff, 16);
|
|
372
|
+
swapmove(st, 4, 4 + 16, 0x0000ffff, 16);
|
|
373
|
+
swapmove(st, 8, 8 + 16, 0x0000ffff, 16);
|
|
374
|
+
swapmove(st, 12, 12 + 16, 0x0000ffff, 16);
|
|
375
|
+
|
|
376
|
+
for (let i = 0; i < 32; i += 8) {
|
|
377
|
+
swapmove(st, i + 1, i, 0x55555555, 1);
|
|
378
|
+
swapmove(st, i + 5, i + 4, 0x55555555, 1);
|
|
379
|
+
swapmove(st, i + 2, i, 0x33333333, 2);
|
|
380
|
+
swapmove(st, i + 3, i + 1, 0x33333333, 2);
|
|
381
|
+
swapmove(st, i + 6, i + 4, 0x33333333, 2);
|
|
382
|
+
swapmove(st, i + 7, i + 5, 0x33333333, 2);
|
|
383
|
+
swapmove(st, i + 4, i, 0x0f0f0f0f, 4);
|
|
384
|
+
swapmove(st, i + 5, i + 1, 0x0f0f0f0f, 4);
|
|
385
|
+
swapmove(st, i + 6, i + 2, 0x0f0f0f0f, 4);
|
|
386
|
+
swapmove(st, i + 7, i + 3, 0x0f0f0f0f, 4);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Computes the index into the bitsliced state array.
|
|
392
|
+
* @param block - Block index (0-7)
|
|
393
|
+
* @param word - Word index (0-3)
|
|
394
|
+
*/
|
|
395
|
+
export function wordIdx(block: number, word: number): number {
|
|
396
|
+
return block + word * 8;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Rotate all blocks right by 1 position (for AEGIS state rotation).
|
|
401
|
+
* Performs (st[i] & 0xfefefefe) >> 1 | (st[i] & 0x01010101) << 7
|
|
402
|
+
*/
|
|
403
|
+
export function blocksRotr(st: AesBlocks): void {
|
|
404
|
+
for (let i = 0; i < 32; i++) {
|
|
405
|
+
st[i] =
|
|
406
|
+
(((st[i]! & 0xfefefefe) >>> 1) | ((st[i]! & 0x01010101) << 7)) >>> 0;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Put a single AES block into the bitsliced state at the given block position.
|
|
412
|
+
*/
|
|
413
|
+
export function blocksPut(st: AesBlocks, s: AesBlock, block: number): void {
|
|
414
|
+
st[wordIdx(block, 0)] = s[0]!;
|
|
415
|
+
st[wordIdx(block, 1)] = s[1]!;
|
|
416
|
+
st[wordIdx(block, 2)] = s[2]!;
|
|
417
|
+
st[wordIdx(block, 3)] = s[3]!;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Load a 16-byte buffer into an AES block (4 uint32 little-endian).
|
|
422
|
+
*/
|
|
423
|
+
export function blockFromBytes(out: AesBlock, src: Uint8Array): void {
|
|
424
|
+
const view = new DataView(src.buffer, src.byteOffset, src.byteLength);
|
|
425
|
+
out[0] = view.getUint32(0, true);
|
|
426
|
+
out[1] = view.getUint32(4, true);
|
|
427
|
+
out[2] = view.getUint32(8, true);
|
|
428
|
+
out[3] = view.getUint32(12, true);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Store an AES block to a 16-byte buffer (little-endian).
|
|
433
|
+
*/
|
|
434
|
+
export function blockToBytes(out: Uint8Array, src: AesBlock): void {
|
|
435
|
+
const view = new DataView(out.buffer, out.byteOffset, out.byteLength);
|
|
436
|
+
view.setUint32(0, src[0]!, true);
|
|
437
|
+
view.setUint32(4, src[1]!, true);
|
|
438
|
+
view.setUint32(8, src[2]!, true);
|
|
439
|
+
view.setUint32(12, src[3]!, true);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* XOR two AES blocks: out = a ^ b
|
|
444
|
+
*/
|
|
445
|
+
export function blockXor(out: AesBlock, a: AesBlock, b: AesBlock): void {
|
|
446
|
+
out[0] = (a[0]! ^ b[0]!) >>> 0;
|
|
447
|
+
out[1] = (a[1]! ^ b[1]!) >>> 0;
|
|
448
|
+
out[2] = (a[2]! ^ b[2]!) >>> 0;
|
|
449
|
+
out[3] = (a[3]! ^ b[3]!) >>> 0;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* XOR two bitsliced states: a ^= b
|
|
454
|
+
*/
|
|
455
|
+
export function blocksXor(a: AesBlocks, b: AesBlocks): void {
|
|
456
|
+
for (let i = 0; i < 32; i++) {
|
|
457
|
+
a[i] = (a[i]! ^ b[i]!) >>> 0;
|
|
458
|
+
}
|
|
459
|
+
}
|