diff --git a/src/aes/aes.ts b/src/aes/aes.ts index 54635d3..758f8f5 100644 --- a/src/aes/aes.ts +++ b/src/aes/aes.ts @@ -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; diff --git a/src/aes/cbc.ts b/src/aes/cbc.ts index dfa6f4d..8eef734 100644 --- a/src/aes/cbc.ts +++ b/src/aes/cbc.ts @@ -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); } @@ -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); } diff --git a/src/aes/ccm.ts b/src/aes/ccm.ts index 078b2f8..5018a7e 100644 --- a/src/aes/ccm.ts +++ b/src/aes/ccm.ts @@ -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, @@ -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'); @@ -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; @@ -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; @@ -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); @@ -235,8 +237,8 @@ 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; } @@ -244,12 +246,12 @@ export class AES_CCM extends AES { 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; @@ -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'); @@ -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; } @@ -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'); @@ -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); } } diff --git a/src/aes/cfb.ts b/src/aes/cfb.ts index be90668..dfd82c1 100644 --- a/src/aes/cfb.ts +++ b/src/aes/cfb.ts @@ -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); } @@ -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); } diff --git a/src/aes/ctr.ts b/src/aes/ctr.ts index 061c159..c1ae2a9 100644 --- a/src/aes/ctr.ts +++ b/src/aes/ctr.ts @@ -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); } @@ -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); } @@ -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) { @@ -50,7 +52,7 @@ 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'); } @@ -58,7 +60,7 @@ export class AES_CTR extends AES { 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); } } } diff --git a/src/aes/ecb.ts b/src/aes/ecb.ts index 7fc3ef8..423bb67 100644 --- a/src/aes/ecb.ts +++ b/src/aes/ecb.ts @@ -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); } @@ -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); } diff --git a/src/aes/gcm.ts b/src/aes/gcm.ts index 08b9dbc..d2fd703 100644 --- a/src/aes/gcm.ts +++ b/src/aes/gcm.ts @@ -5,9 +5,10 @@ import { AES_asm } from './aes.asm'; const _AES_GCM_data_maxLength = 68719476704; // 2^36 - 2^5 -export class AES_GCM extends AES { +export class AES_GCM { private readonly adata: Uint8Array | undefined; private readonly gamma0: number = 0; + private aes: AES; private counter: number = 1; @@ -31,11 +32,17 @@ export class AES_GCM extends AES { return new AES_GCM(key, nonce, adata, tagsize).decrypt(ciphertext); } - constructor(key: Uint8Array, nonce: Uint8Array, adata?: Uint8Array, private readonly tagSize: number = 16) { - super(key, undefined, false, 'CTR'); + constructor( + key: Uint8Array, + nonce: Uint8Array, + adata?: Uint8Array, + private readonly tagSize: number = 16, + aes?: AES, + ) { + this.aes = aes ? aes : new AES(key, undefined, false, 'CTR'); // Init GCM - this.asm.gcm_init(); + this.aes.asm.gcm_init(); // Tag size if (this.tagSize < 4 || this.tagSize > 16) throw new IllegalArgumentError('illegal tagSize value'); @@ -46,28 +53,28 @@ export class AES_GCM extends AES { if (noncelen !== 12) { this._gcm_mac_process(nonce); - this.heap[0] = 0; - this.heap[1] = 0; - this.heap[2] = 0; - this.heap[3] = 0; - this.heap[4] = 0; - this.heap[5] = 0; - this.heap[6] = 0; - this.heap[7] = 0; - this.heap[8] = 0; - this.heap[9] = 0; - this.heap[10] = 0; - this.heap[11] = noncelen >>> 29; - this.heap[12] = (noncelen >>> 21) & 255; - this.heap[13] = (noncelen >>> 13) & 255; - this.heap[14] = (noncelen >>> 5) & 255; - this.heap[15] = (noncelen << 3) & 255; - this.asm.mac(AES_asm.MAC.GCM, AES_asm.HEAP_DATA, 16); - - this.asm.get_iv(AES_asm.HEAP_DATA); - this.asm.set_iv(0, 0, 0, 0); - - noncebuf.set(this.heap.subarray(0, 16)); + this.aes.heap[0] = 0; + this.aes.heap[1] = 0; + this.aes.heap[2] = 0; + this.aes.heap[3] = 0; + this.aes.heap[4] = 0; + this.aes.heap[5] = 0; + this.aes.heap[6] = 0; + this.aes.heap[7] = 0; + this.aes.heap[8] = 0; + this.aes.heap[9] = 0; + this.aes.heap[10] = 0; + this.aes.heap[11] = noncelen >>> 29; + this.aes.heap[12] = (noncelen >>> 21) & 255; + this.aes.heap[13] = (noncelen >>> 13) & 255; + this.aes.heap[14] = (noncelen >>> 5) & 255; + this.aes.heap[15] = (noncelen << 3) & 255; + this.aes.asm.mac(AES_asm.MAC.GCM, AES_asm.HEAP_DATA, 16); + + this.aes.asm.get_iv(AES_asm.HEAP_DATA); + this.aes.asm.set_iv(0, 0, 0, 0); + + noncebuf.set(this.aes.heap.subarray(0, 16)); } else { noncebuf.set(nonce); noncebuf[15] = 1; @@ -76,8 +83,8 @@ export class AES_GCM extends AES { const nonceview = new DataView(noncebuf.buffer); this.gamma0 = nonceview.getUint32(12); - this.asm.set_nonce(nonceview.getUint32(0), nonceview.getUint32(4), nonceview.getUint32(8), 0); - this.asm.set_mask(0, 0, 0, 0xffffffff); + this.aes.asm.set_nonce(nonceview.getUint32(0), nonceview.getUint32(4), nonceview.getUint32(8), 0); + this.aes.asm.set_mask(0, 0, 0, 0xffffffff); // Associated data if (adata !== undefined) { @@ -96,7 +103,7 @@ export class AES_GCM extends AES { // Counter if (this.counter < 1 || this.counter > 0xffffffff) throw new RangeError('counter must be a positive 32-bit integer'); - this.asm.set_counter(0, 0, 0, (this.gamma0 + this.counter) | 0); + this.aes.asm.set_counter(0, 0, 0, (this.gamma0 + this.counter) | 0); } encrypt(data: Uint8Array) { @@ -110,11 +117,11 @@ export class AES_GCM extends AES { AES_GCM_Encrypt_process(data: Uint8Array): Uint8Array { let dpos = 0; let dlen = data.length || 0; - let asm = this.asm; - let heap = this.heap; + let asm = this.aes.asm; + let heap = this.aes.heap; let counter = this.counter; - let pos = this.pos; - let len = this.len; + let pos = this.aes.pos; + let len = this.aes.len; let rpos = 0; let rlen = (len + dlen) & -16; let wlen = 0; @@ -146,20 +153,20 @@ export class AES_GCM extends AES { } this.counter = counter; - this.pos = pos; - this.len = len; + this.aes.pos = pos; + this.aes.len = len; return result; } AES_GCM_Encrypt_finish(): Uint8Array { - let asm = this.asm; - let heap = this.heap; + let asm = this.aes.asm; + let heap = this.aes.heap; let counter = this.counter; let tagSize = this.tagSize; let adata = this.adata; - let pos = this.pos; - let len = this.len; + let pos = this.aes.pos; + let len = this.aes.len; const result = new Uint8Array(len + tagSize); @@ -196,8 +203,8 @@ export class AES_GCM 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; } @@ -205,12 +212,12 @@ export class AES_GCM extends AES { AES_GCM_Decrypt_process(data: Uint8Array): Uint8Array { let dpos = 0; let dlen = data.length || 0; - let asm = this.asm; - let heap = this.heap; + let asm = this.aes.asm; + let heap = this.aes.heap; let counter = this.counter; let tagSize = this.tagSize; - let pos = this.pos; - let len = this.len; + let pos = this.aes.pos; + let len = this.aes.len; let rpos = 0; let rlen = len + dlen > tagSize ? (len + dlen - tagSize) & -16 : 0; let tlen = len + dlen - rlen; @@ -242,20 +249,20 @@ export class AES_GCM extends AES { } this.counter = counter; - this.pos = pos; - this.len = len; + this.aes.pos = pos; + this.aes.len = len; return result; } AES_GCM_Decrypt_finish() { - let asm = this.asm; - let heap = this.heap; + let asm = this.aes.asm; + let heap = this.aes.heap; let tagSize = this.tagSize; let adata = this.adata; let counter = this.counter; - let pos = this.pos; - let len = this.len; + let pos = this.aes.pos; + let len = this.aes.len; let rlen = len - tagSize; if (len < tagSize) throw new IllegalStateError('authentication tag not found'); @@ -298,8 +305,8 @@ export class AES_GCM 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; } @@ -327,8 +334,8 @@ export class AES_GCM extends AES { } _gcm_mac_process(data: Uint8Array) { - 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; diff --git a/src/aes/ofb.ts b/src/aes/ofb.ts index 8a09d1d..436a52a 100644 --- a/src/aes/ofb.ts +++ b/src/aes/ofb.ts @@ -1,7 +1,9 @@ import { AES } from './aes'; import { joinBytes } from '../other/utils'; -export class AES_OFB extends AES { +export class AES_OFB { + private aes: AES; + static encrypt(data: Uint8Array, key: Uint8Array, iv?: Uint8Array): Uint8Array { return new AES_OFB(key, iv).encrypt(data); } @@ -10,20 +12,20 @@ export class AES_OFB extends AES { return new AES_OFB(key, iv).decrypt(data); } - constructor(key: Uint8Array, iv?: Uint8Array) { - super(key, iv, false, 'OFB'); + constructor(key: Uint8Array, iv?: Uint8Array, aes?: AES) { + this.aes = aes ? aes : new AES(key, iv, false, 'OFB'); } 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); }