Skip to content

Commit

Permalink
perf(AES): reduce amount of array copies
Browse files Browse the repository at this point in the history
  • Loading branch information
aykxt committed Mar 12, 2021
1 parent 5288fa3 commit 5db6174
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 60 deletions.
72 changes: 34 additions & 38 deletions src/aes/aes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class AES {
}
}

encrypt(data: Uint8Array): Uint8Array {
encrypt(data: Uint8Array) {
let t0 = ((data[0] << 24) |
(data[1] << 16) |
(data[2] << 8) |
Expand Down Expand Up @@ -131,34 +131,32 @@ export class AES {
t3 = a3;
}

const decrypted = new Uint8Array(16);
let tt = this.#ke[4 * this.#nr];
decrypted[0] = S[t0 >>> 24] ^ (tt >>> 24);
decrypted[1] = (S[(t1 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
decrypted[2] = (S[(t2 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
decrypted[3] = (S[t3 & 0xff] ^ tt) & 0xff;
data[0] = S[t0 >>> 24] ^ (tt >>> 24);
data[1] = (S[(t1 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
data[2] = (S[(t2 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
data[3] = (S[t3 & 0xff] ^ tt) & 0xff;

tt = this.#ke[4 * this.#nr + 1];
decrypted[4] = S[t1 >>> 24] ^ (tt >>> 24);
decrypted[5] = (S[(t2 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
decrypted[6] = (S[(t3 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
decrypted[7] = (S[t0 & 0xff] ^ tt) & 0xff;
data[4] = S[t1 >>> 24] ^ (tt >>> 24);
data[5] = (S[(t2 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
data[6] = (S[(t3 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
data[7] = (S[t0 & 0xff] ^ tt) & 0xff;

tt = this.#ke[4 * this.#nr + 2];
decrypted[8] = S[t2 >>> 24] ^ (tt >>> 24);
decrypted[9] = (S[(t3 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
decrypted[10] = (S[(t0 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
decrypted[11] = (S[t1 & 0xff] ^ tt) & 0xff;
data[8] = S[t2 >>> 24] ^ (tt >>> 24);
data[9] = (S[(t3 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
data[10] = (S[(t0 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
data[11] = (S[t1 & 0xff] ^ tt) & 0xff;

tt = this.#ke[4 * this.#nr + 3];
decrypted[12] = S[t3 >>> 24] ^ (tt >>> 24);
decrypted[13] = (S[(t0 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
decrypted[14] = (S[(t1 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
decrypted[15] = (S[t2 & 0xff] ^ tt) & 0xff;
return decrypted;
data[12] = S[t3 >>> 24] ^ (tt >>> 24);
data[13] = (S[(t0 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
data[14] = (S[(t1 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
data[15] = (S[t2 & 0xff] ^ tt) & 0xff;
}

decrypt(data: Uint8Array): Uint8Array {
decrypt(data: Uint8Array) {
let t0 = ((data[0] << 24) |
(data[1] << 16) |
(data[2] << 8) |
Expand Down Expand Up @@ -206,30 +204,28 @@ export class AES {
t3 = a3;
}

const encrypted = new Uint8Array(16);
let tt = this.#kd[4 * this.#nr];
encrypted[0] = SI[t0 >>> 24] ^ (tt >>> 24);
encrypted[1] = (SI[(t3 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
encrypted[2] = (SI[(t2 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
encrypted[3] = (SI[t1 & 0xff] ^ tt) & 0xff;
data[0] = SI[t0 >>> 24] ^ (tt >>> 24);
data[1] = (SI[(t3 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
data[2] = (SI[(t2 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
data[3] = (SI[t1 & 0xff] ^ tt) & 0xff;

tt = this.#kd[4 * this.#nr + 1];
encrypted[4] = SI[t1 >>> 24] ^ (tt >>> 24);
encrypted[5] = (SI[(t0 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
encrypted[6] = (SI[(t3 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
encrypted[7] = (SI[t2 & 0xff] ^ tt) & 0xff;
data[4] = SI[t1 >>> 24] ^ (tt >>> 24);
data[5] = (SI[(t0 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
data[6] = (SI[(t3 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
data[7] = (SI[t2 & 0xff] ^ tt) & 0xff;

tt = this.#kd[4 * this.#nr + 2];
encrypted[8] = SI[t2 >>> 24] ^ tt >>> 24;
encrypted[9] = (SI[(t1 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
encrypted[10] = (SI[(t0 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
encrypted[11] = (SI[t3 & 0xff] ^ tt) & 0xff;
data[8] = SI[t2 >>> 24] ^ tt >>> 24;
data[9] = (SI[(t1 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
data[10] = (SI[(t0 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
data[11] = (SI[t3 & 0xff] ^ tt) & 0xff;

tt = this.#kd[4 * this.#nr + 3];
encrypted[12] = SI[t3 >>> 24] ^ (tt >> 24);
encrypted[13] = (SI[(t2 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
encrypted[14] = (SI[(t1 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
encrypted[15] = (SI[t0 & 0xff] ^ tt) & 0xff;
return encrypted;
data[12] = SI[t3 >>> 24] ^ (tt >> 24);
data[13] = (SI[(t2 >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
data[14] = (SI[(t1 >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
data[15] = (SI[t0 & 0xff] ^ tt) & 0xff;
}
}
43 changes: 21 additions & 22 deletions src/aes/cipher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,24 @@ export class AesEcb implements BlockCipher {
encrypt(data: Uint8Array): Uint8Array {
data = pad(data, this.padding, AES.BLOCK_SIZE);

const encrypted = new Uint8Array(data.length);
const encrypted = data.slice();

for (let i = 0; i < data.length; i += 16) {
const block = data.slice(i, i + 16);
encrypted.set(this.#aes.encrypt(block), i);
for (let i = 0; i < data.length; i += AES.BLOCK_SIZE) {
this.#aes.encrypt(encrypted.subarray(i, i + AES.BLOCK_SIZE));
}

return encrypted;
}

decrypt(data: Uint8Array): Uint8Array {
if ((data.length % 16) !== 0) {
if ((data.length % AES.BLOCK_SIZE) !== 0) {
throw new Error("invalid data size (must be multiple of 16 bytes)");
}

const decrypted = new Uint8Array(data.length);
const decrypted = data.slice();

for (let i = 0; i < data.length; i += 16) {
const block = data.slice(i, i + 16);
decrypted.set(this.#aes.decrypt(block), i);
for (let i = 0; i < data.length; i += AES.BLOCK_SIZE) {
this.#aes.decrypt(decrypted.subarray(i, i + AES.BLOCK_SIZE));
}

return unpad(decrypted, this.padding, AES.BLOCK_SIZE);
Expand All @@ -50,7 +48,7 @@ export class AesCbc implements BlockCipher {
iv: Uint8Array,
private padding: Padding = Padding.NONE,
) {
if (iv.length != 16) {
if (iv.length != AES.BLOCK_SIZE) {
throw new Error("invalid initialation vector size (must be 16 bytes)");
}

Expand All @@ -61,17 +59,17 @@ export class AesCbc implements BlockCipher {
encrypt(data: Uint8Array): Uint8Array {
data = pad(data, this.padding, AES.BLOCK_SIZE);

const encrypted = new Uint8Array(data.length);
const encrypted = data.slice();

for (let i = 0; i < data.length; i += 16) {
const block = data.slice(i, i + 16);
for (let i = 0; i < data.length; i += AES.BLOCK_SIZE) {
const block = encrypted.subarray(i, i + AES.BLOCK_SIZE);

for (let j = 0; j < 16; j++) {
for (let j = 0; j < AES.BLOCK_SIZE; j++) {
block[j] ^= this.#prev[j];
}

this.#prev = this.#aes.encrypt(block);
encrypted.set(this.#prev, i);
this.#aes.encrypt(block);
this.#prev = block;
}

return encrypted;
Expand All @@ -82,16 +80,17 @@ export class AesCbc implements BlockCipher {
throw new Error("invalid data size (must be multiple of 16 bytes)");
}

const decrypted = new Uint8Array(data.length);
const decrypted = data.slice();

for (let i = 0; i < data.length; i += 16) {
const block = this.#aes.decrypt(data.slice(i, i + 16));
for (let i = 0; i < data.length; i += AES.BLOCK_SIZE) {
const block = decrypted.subarray(i, i + AES.BLOCK_SIZE);
this.#aes.decrypt(block);

for (let j = 0; j < 16; j++) {
decrypted[i + j] = block[j] ^ this.#prev[j];
for (let j = 0; j < AES.BLOCK_SIZE; j++) {
block[j] ^= this.#prev[j];
}

this.#prev = data.slice(i, i + 16);
this.#prev = data.subarray(i, i + AES.BLOCK_SIZE);
}

return unpad(decrypted, this.padding, AES.BLOCK_SIZE);
Expand Down

0 comments on commit 5db6174

Please sign in to comment.