Skip to content
This repository has been archived by the owner on Feb 7, 2023. It is now read-only.

Commit

Permalink
Use composition instead of inheritance for AES
Browse files Browse the repository at this point in the history
  • Loading branch information
alippai committed Dec 2, 2018
1 parent c3e02e5 commit 2821dd1
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 139 deletions.
18 changes: 9 additions & 9 deletions src/aes/aes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import { AES_asm, AES_mode } from './aes.asm';
import { _heap_init, _heap_write, is_bytes } from '../other/utils';
import { IllegalArgumentError, SecurityError } from '../other/errors';

export abstract class AES {
protected readonly heap: Uint8Array;
protected readonly asm: AES_asm;
export class AES {
public readonly heap: Uint8Array;
public readonly asm: AES_asm;
private readonly mode: string;
protected padding: boolean; // TODO: This should be `private readonly`, hacking for AES-CFB?!
protected pos: number = 0;
protected len: number = 0;
public padding: boolean; // TODO: This should be `private readonly`, hacking for AES-CFB?!
public pos: number = 0;
public len: number = 0;

protected constructor(key: Uint8Array, iv: Uint8Array | undefined, padding = true, mode: AES_mode) {
constructor(key: Uint8Array, iv: Uint8Array | undefined, padding = true, mode: AES_mode, heap?: Uint8Array, asm?: AES_asm) {
this.mode = mode;

// The AES "worker"
this.heap = _heap_init().subarray(AES_asm.HEAP_DATA);
this.asm = new AES_asm(null, this.heap.buffer);
this.heap = heap ? heap : _heap_init().subarray(AES_asm.HEAP_DATA);
this.asm = asm ? asm : new AES_asm(null, this.heap.buffer);

// The AES object state
this.pos = 0;
Expand Down
16 changes: 9 additions & 7 deletions src/aes/cbc.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { AES } from './aes';
import { joinBytes } from '../other/utils';

export class AES_CBC extends AES {
export class AES_CBC {
private aes: AES;

static encrypt(data: Uint8Array, key: Uint8Array, padding: boolean = true, iv?: Uint8Array): Uint8Array {
return new AES_CBC(key, iv, padding).encrypt(data);
}
Expand All @@ -10,20 +12,20 @@ export class AES_CBC extends AES {
return new AES_CBC(key, iv, padding).decrypt(data);
}

constructor(key: Uint8Array, iv?: Uint8Array, padding: boolean = true) {
super(key, iv, padding, 'CBC');
constructor(key: Uint8Array, iv?: Uint8Array, padding: boolean = true, aes?: AES) {
this.aes = aes ? aes : new AES(key, iv, padding, 'CBC');
}

encrypt(data: Uint8Array): Uint8Array {
const r1 = this.AES_Encrypt_process(data);
const r2 = this.AES_Encrypt_finish();
const r1 = this.aes.AES_Encrypt_process(data);
const r2 = this.aes.AES_Encrypt_finish();

return joinBytes(r1, r2);
}

decrypt(data: Uint8Array): Uint8Array {
const r1 = this.AES_Decrypt_process(data);
const r2 = this.AES_Decrypt_finish();
const r1 = this.aes.AES_Decrypt_process(data);
const r2 = this.aes.AES_Decrypt_finish();

return joinBytes(r1, r2);
}
Expand Down
70 changes: 36 additions & 34 deletions src/aes/ccm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ import { IllegalArgumentError, IllegalStateError, SecurityError } from '../other
const _AES_CCM_adata_maxLength = 65279; // 2^16 - 2^8
const _AES_CCM_data_maxLength = 4503599627370480; // 2^52 - 2^4

export class AES_CCM extends AES {
export class AES_CCM {
private readonly tagSize: number;
private readonly lengthSize: number;
private nonce: Uint8Array;
private readonly adata: Uint8Array | undefined;
private counter: number = 1;
private dataLength: number = -1;
private aes: AES;

static encrypt(
clear: Uint8Array,
Expand All @@ -54,8 +55,9 @@ export class AES_CCM extends AES {
adata: Uint8Array | undefined,
tagSize: number = 16,
dataLength: number,
aes?: AES,
) {
super(key, undefined, undefined, 'CCM');
this.aes = aes ? aes : new AES(key, undefined, undefined, 'CCM');

// Tag size
if (tagSize < 4 || tagSize > 16 || tagSize & 1) throw new IllegalArgumentError('illegal tagSize value');
Expand Down Expand Up @@ -140,16 +142,16 @@ export class AES_CCM extends AES {
}

this._cbc_mac_process(data);
this.asm.get_state(AES_asm.HEAP_DATA);
this.aes.asm.get_state(AES_asm.HEAP_DATA);

const iv = new Uint8Array(this.heap.subarray(0, 16));
const iv = new Uint8Array(this.aes.heap.subarray(0, 16));
const ivview = new DataView(iv.buffer, iv.byteOffset, iv.byteLength);
this.asm.set_iv(ivview.getUint32(0), ivview.getUint32(4), ivview.getUint32(8), ivview.getUint32(12));
this.aes.asm.set_iv(ivview.getUint32(0), ivview.getUint32(4), ivview.getUint32(8), ivview.getUint32(12));
}

_cbc_mac_process(data: Uint8Array): void {
const heap = this.heap;
const asm = this.asm;
const heap = this.aes.heap;
const asm = this.aes.asm;
let dpos = 0;
let dlen = data.length || 0;
let wlen = 0;
Expand All @@ -165,14 +167,14 @@ export class AES_CCM extends AES {
}

AES_CCM_Encrypt_process(data: Uint8Array): Uint8Array {
const asm = this.asm;
const heap = this.heap;
const asm = this.aes.asm;
const heap = this.aes.heap;

let dpos = 0;
let dlen = data.length || 0;
let counter = this.counter;
let pos = this.pos;
let len = this.len;
let pos = this.aes.pos;
let len = this.aes.len;

const rlen = (len + dlen) & -16;
let rpos = 0;
Expand Down Expand Up @@ -207,18 +209,18 @@ export class AES_CCM extends AES {
}

this.counter = counter;
this.pos = pos;
this.len = len;
this.aes.pos = pos;
this.aes.len = len;

return result;
}

AES_CCM_Encrypt_finish(): Uint8Array {
const asm = this.asm;
const heap = this.heap;
const asm = this.aes.asm;
const heap = this.aes.heap;
const tagSize = this.tagSize;
const pos = this.pos;
const len = this.len;
const pos = this.aes.pos;
const len = this.aes.len;

const result = new Uint8Array(len + tagSize);

Expand All @@ -235,21 +237,21 @@ export class AES_CCM extends AES {
result.set(heap.subarray(0, tagSize), len);

this.counter = 1;
this.pos = 0;
this.len = 0;
this.aes.pos = 0;
this.aes.len = 0;

return result;
}

AES_CCM_Decrypt_process(data: Uint8Array): Uint8Array {
let dpos = 0;
let dlen = data.length || 0;
const asm = this.asm;
const heap = this.heap;
const asm = this.aes.asm;
const heap = this.aes.heap;
let counter = this.counter;
const tagSize = this.tagSize;
let pos = this.pos;
let len = this.len;
let pos = this.aes.pos;
let len = this.aes.len;
let rpos = 0;
const rlen = len + dlen > tagSize ? (len + dlen - tagSize) & -16 : 0;
const tlen = len + dlen - rlen;
Expand Down Expand Up @@ -281,18 +283,18 @@ export class AES_CCM extends AES {
}

this.counter = counter;
this.pos = pos;
this.len = len;
this.aes.pos = pos;
this.aes.len = len;

return result;
}

AES_CCM_Decrypt_finish(): Uint8Array {
const asm = this.asm;
const heap = this.heap;
const asm = this.aes.asm;
const heap = this.aes.heap;
const tagSize = this.tagSize;
const pos = this.pos;
const len = this.len;
const pos = this.aes.pos;
const len = this.aes.len;
const rlen = len - tagSize;

if (len < tagSize) throw new IllegalStateError('authentication tag not found');
Expand All @@ -316,8 +318,8 @@ export class AES_CCM extends AES {
if (acheck) throw new SecurityError('data integrity check failed');

this.counter = 1;
this.pos = 0;
this.len = 0;
this.aes.pos = 0;
this.aes.len = 0;

return result;
}
Expand All @@ -326,7 +328,7 @@ export class AES_CCM extends AES {
if (size < 8 || size > 48) throw new IllegalArgumentError('illegal counter size');

const mask = Math.pow(2, size) - 1;
this.asm.set_mask(0, 0, (mask / 0x100000000) | 0, mask | 0);
this.aes.asm.set_mask(0, 0, (mask / 0x100000000) | 0, mask | 0);

const len = nonce.length;
if (!len || len > 16) throw new IllegalArgumentError('illegal nonce size');
Expand All @@ -336,12 +338,12 @@ export class AES_CCM extends AES {
const view = new DataView(new ArrayBuffer(16));
new Uint8Array(view.buffer).set(nonce);

this.asm.set_nonce(view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12));
this.aes.asm.set_nonce(view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12));

if (counter < 0 || counter >= Math.pow(2, size)) throw new IllegalArgumentError('illegal counter value');

this.counter = counter;

this.asm.set_counter(0, 0, (counter / 0x100000000) | 0, counter | 0);
this.aes.asm.set_counter(0, 0, (counter / 0x100000000) | 0, counter | 0);
}
}
18 changes: 10 additions & 8 deletions src/aes/cfb.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { AES } from './aes';
import { joinBytes } from '../other/utils';

export class AES_CFB extends AES {
export class AES_CFB {
private aes: AES;

static encrypt(data: Uint8Array, key: Uint8Array, iv?: Uint8Array): Uint8Array {
return new AES_CFB(key, iv).encrypt(data);
}
Expand All @@ -10,21 +12,21 @@ export class AES_CFB extends AES {
return new AES_CFB(key, iv).decrypt(data);
}

constructor(key: Uint8Array, iv?: Uint8Array) {
super(key, iv, true, 'CFB');
delete this.padding;
constructor(key: Uint8Array, iv?: Uint8Array, aes?: AES) {
this.aes = aes ? aes : new AES(key, iv, true, 'CFB');
delete this.aes.padding;
}

encrypt(data: Uint8Array): Uint8Array {
const r1 = this.AES_Encrypt_process(data);
const r2 = this.AES_Encrypt_finish();
const r1 = this.aes.AES_Encrypt_process(data);
const r2 = this.aes.AES_Encrypt_finish();

return joinBytes(r1, r2);
}

decrypt(data: Uint8Array): Uint8Array {
const r1 = this.AES_Decrypt_process(data);
const r2 = this.AES_Decrypt_finish();
const r1 = this.aes.AES_Decrypt_process(data);
const r2 = this.aes.AES_Decrypt_finish();

return joinBytes(r1, r2);
}
Expand Down
26 changes: 14 additions & 12 deletions src/aes/ctr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { AES } from './aes';
import { IllegalArgumentError } from '../other/errors';
import { joinBytes } from '../other/utils';

export class AES_CTR extends AES {
export class AES_CTR {
private aes: AES;

static encrypt(data: Uint8Array, key: Uint8Array, nonce: Uint8Array): Uint8Array {
return new AES_CTR(key, nonce).encrypt(data);
}
Expand All @@ -11,23 +13,23 @@ export class AES_CTR extends AES {
return new AES_CTR(key, nonce).encrypt(data);
}

constructor(key: Uint8Array, nonce: Uint8Array) {
super(key, undefined, false, 'CTR');
delete this.padding;
constructor(key: Uint8Array, nonce: Uint8Array, aes?: AES) {
this.aes = aes ? aes : new AES(key, undefined, false, 'CTR');
delete this.aes.padding;

this.AES_CTR_set_options(nonce);
}

encrypt(data: Uint8Array): Uint8Array {
const r1 = this.AES_Encrypt_process(data);
const r2 = this.AES_Encrypt_finish();
const r1 = this.aes.AES_Encrypt_process(data);
const r2 = this.aes.AES_Encrypt_finish();

return joinBytes(r1, r2);
}

decrypt(data: Uint8Array): Uint8Array {
const r1 = this.AES_Encrypt_process(data);
const r2 = this.AES_Encrypt_finish();
const r1 = this.aes.AES_Encrypt_process(data);
const r2 = this.aes.AES_Encrypt_finish();

return joinBytes(r1, r2);
}
Expand All @@ -37,10 +39,10 @@ export class AES_CTR extends AES {
if (size < 8 || size > 48) throw new IllegalArgumentError('illegal counter size');

let mask = Math.pow(2, size) - 1;
this.asm.set_mask(0, 0, (mask / 0x100000000) | 0, mask | 0);
this.aes.asm.set_mask(0, 0, (mask / 0x100000000) | 0, mask | 0);
} else {
size = 48;
this.asm.set_mask(0, 0, 0xffff, 0xffffffff);
this.aes.asm.set_mask(0, 0, 0xffff, 0xffffffff);
}

if (nonce !== undefined) {
Expand All @@ -50,15 +52,15 @@ export class AES_CTR extends AES {
let view = new DataView(new ArrayBuffer(16));
new Uint8Array(view.buffer).set(nonce);

this.asm.set_nonce(view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12));
this.aes.asm.set_nonce(view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12));
} else {
throw new Error('nonce is required');
}

if (counter !== undefined) {
if (counter < 0 || counter >= Math.pow(2, size)) throw new IllegalArgumentError('illegal counter value');

this.asm.set_counter(0, 0, (counter / 0x100000000) | 0, counter | 0);
this.aes.asm.set_counter(0, 0, (counter / 0x100000000) | 0, counter | 0);
}
}
}
15 changes: 8 additions & 7 deletions src/aes/ecb.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { AES } from './aes';
import { joinBytes } from '../other/utils';

export class AES_ECB extends AES {
export class AES_ECB {
private aes: AES;
static encrypt(data: Uint8Array, key: Uint8Array, padding: boolean = false): Uint8Array {
return new AES_ECB(key, padding).encrypt(data);
}
Expand All @@ -10,20 +11,20 @@ export class AES_ECB extends AES {
return new AES_ECB(key, padding).decrypt(data);
}

constructor(key: Uint8Array, padding: boolean = false) {
super(key, undefined, padding, 'ECB');
constructor(key: Uint8Array, padding: boolean = false, aes?: AES) {
this.aes = aes ? aes : new AES(key, undefined, padding, 'ECB');
}

encrypt(data: Uint8Array): Uint8Array {
const r1 = this.AES_Encrypt_process(data);
const r2 = this.AES_Encrypt_finish();
const r1 = this.aes.AES_Encrypt_process(data);
const r2 = this.aes.AES_Encrypt_finish();

return joinBytes(r1, r2);
}

decrypt(data: Uint8Array): Uint8Array {
const r1 = this.AES_Decrypt_process(data);
const r2 = this.AES_Decrypt_finish();
const r1 = this.aes.AES_Decrypt_process(data);
const r2 = this.aes.AES_Decrypt_finish();

return joinBytes(r1, r2);
}
Expand Down
Loading

0 comments on commit 2821dd1

Please sign in to comment.