From 94e3982bfcdf4e12163342fbd0379a62aa7a5f07 Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Fri, 29 Jun 2018 15:22:18 +0200 Subject: [PATCH 1/4] Heap & asm pooling --- src/aes/aes.ts | 47 ++++++++++++++++++++++++++++++++++----- src/hash/hash.ts | 33 ++++++++++++++++++++++++++- src/hash/sha1/sha1.ts | 12 +++------- src/hash/sha256/sha256.ts | 12 +++------- src/hash/sha512/sha512.ts | 12 +++------- 5 files changed, 82 insertions(+), 34 deletions(-) diff --git a/src/aes/aes.ts b/src/aes/aes.ts index 54635d3..ed3ff37 100644 --- a/src/aes/aes.ts +++ b/src/aes/aes.ts @@ -2,6 +2,9 @@ import { AES_asm, AES_mode } from './aes.asm'; import { _heap_init, _heap_write, is_bytes } from '../other/utils'; import { IllegalArgumentError, SecurityError } from '../other/errors'; +const heap_pool = []; +const asm_pool = []; + export abstract class AES { protected readonly heap: Uint8Array; protected readonly asm: AES_asm; @@ -13,14 +16,36 @@ export abstract class AES { protected constructor(key: Uint8Array, iv: Uint8Array | undefined, padding = true, mode: AES_mode) { this.mode = mode; - // The AES "worker" - this.heap = _heap_init().subarray(AES_asm.HEAP_DATA); - this.asm = new AES_asm(null, this.heap.buffer); - // The AES object state this.pos = 0; this.len = 0; + this.key = key; + this.iv = iv; + this.padding = padding; + + // The AES "worker" + this.acquire_asm(); + } + + protected acquire_asm() { + if (this.heap === undefined && this.asm === undefined) { + this.heap = heap_pool.pop() || _heap_init().subarray(AES_asm.HEAP_DATA); + this.asm = asm_pool.pop() || AES_asm(null, this.heap.buffer); + this.reset(this.key, this.iv); + } + } + + protected release_asm() { + this.iv = this.heap.slice(0, this.asm.get_iv(AES_asm.HEAP_DATA)); + + heap_pool.push(this.heap); + asm_pool.push(this.asm); + this.heap = undefined; + this.asm = undefined; + } + + protected reset(key, iv) { // Key const keylen = key.length; if (keylen !== 16 && keylen !== 24 && keylen !== 32) throw new IllegalArgumentError('illegal key size'); @@ -48,13 +73,13 @@ export abstract class AES { } else { this.asm.set_iv(0, 0, 0, 0); } - - this.padding = padding; } AES_Encrypt_process(data: Uint8Array): Uint8Array { if (!is_bytes(data)) throw new TypeError("data isn't of expected type"); + this.acquire_asm(); + let asm = this.asm; let heap = this.heap; let amode = AES_asm.ENC[this.mode]; @@ -96,6 +121,8 @@ export abstract class AES { } AES_Encrypt_finish(): Uint8Array { + this.acquire_asm(); + let asm = this.asm; let heap = this.heap; let amode = AES_asm.ENC[this.mode]; @@ -128,12 +155,16 @@ export abstract class AES { this.pos = 0; this.len = 0; + this.release_asm(); + return result; } AES_Decrypt_process(data: Uint8Array): Uint8Array { if (!is_bytes(data)) throw new TypeError("data isn't of expected type"); + this.acquire_asm(); + let asm = this.asm; let heap = this.heap; let amode = AES_asm.DEC[this.mode]; @@ -181,6 +212,8 @@ export abstract class AES { } AES_Decrypt_finish(): Uint8Array { + this.acquire_asm(); + let asm = this.asm; let heap = this.heap; let amode = AES_asm.DEC[this.mode]; @@ -221,6 +254,8 @@ export abstract class AES { this.pos = 0; this.len = 0; + this.release_asm(); + return result; } } diff --git a/src/hash/hash.ts b/src/hash/hash.ts index 52f112e..735a3a2 100644 --- a/src/hash/hash.ts +++ b/src/hash/hash.ts @@ -1,4 +1,4 @@ -import { _heap_write } from '../other/utils'; +import { _heap_init, _heap_write } from '../other/utils'; import { IllegalStateError } from '../other/errors'; import { sha1result } from './sha1/sha1.asm'; import { sha256result } from './sha256/sha256.asm'; @@ -13,7 +13,32 @@ export abstract class Hash { public BLOCK_SIZE!: number; public HASH_SIZE!: number; + protected static heap_pool !: Array; + protected static asm_pool !: Array; + protected static asm_function!: Function; + + constructor() { + this.acquire_asm(); + } + + protected acquire_asm() { + if (this.heap === undefined && this.asm === undefined) { + this.heap = this.constructor.heap_pool.pop() || _heap_init(); + this.asm = this.constructor.asm_pool.pop() || this.constructor.asm_function({ Uint8Array: Uint8Array }, null, this.heap.buffer); + this.reset(); + } + } + + protected release_asm() { + this.constructor.heap_pool.push(this.heap)); + this.constructor.asm_pool.push(this.asm); + this.heap = undefined; + this.asm = undefined; + } + reset() { + this.acquire_asm(); + this.result = null; this.pos = 0; this.len = 0; @@ -26,6 +51,8 @@ export abstract class Hash { process(data: Uint8Array) { if (this.result !== null) throw new IllegalStateError('state must be reset before processing new data'); + this.acquire_asm(); + let asm = this.asm; let heap = this.heap; let hpos = this.pos; @@ -57,6 +84,8 @@ export abstract class Hash { finish() { if (this.result !== null) throw new IllegalStateError('state must be reset before processing new data'); + this.acquire_asm(); + this.asm.finish(this.pos, this.len, 0); this.result = new Uint8Array(this.HASH_SIZE); @@ -65,6 +94,8 @@ export abstract class Hash { this.pos = 0; this.len = 0; + this.release_asm(); + return this; } } diff --git a/src/hash/sha1/sha1.ts b/src/hash/sha1/sha1.ts index 55d1292..33c2f95 100644 --- a/src/hash/sha1/sha1.ts +++ b/src/hash/sha1/sha1.ts @@ -1,6 +1,5 @@ import { sha1_asm, sha1result } from './sha1.asm'; import { Hash } from '../hash'; -import { _heap_init } from '../../other/utils'; export const _sha1_block_size = 64; export const _sha1_hash_size = 20; @@ -11,12 +10,7 @@ export class Sha1 extends Hash { public BLOCK_SIZE = _sha1_block_size; public HASH_SIZE = _sha1_hash_size; - constructor() { - super(); - - this.heap = _heap_init(); - this.asm = sha1_asm({ Uint8Array: Uint8Array }, null, this.heap.buffer); - - this.reset(); - } + protected static heap_pool = []; + protected static asm_pool = []; + protected static asm_function = sha1_asm; } diff --git a/src/hash/sha256/sha256.ts b/src/hash/sha256/sha256.ts index 885d773..32b9adb 100644 --- a/src/hash/sha256/sha256.ts +++ b/src/hash/sha256/sha256.ts @@ -1,6 +1,5 @@ import { sha256_asm, sha256result } from './sha256.asm'; import { Hash } from '../hash'; -import { _heap_init } from '../../other/utils'; export const _sha256_block_size = 64; export const _sha256_hash_size = 32; @@ -11,12 +10,7 @@ export class Sha256 extends Hash { public BLOCK_SIZE = _sha256_block_size; public HASH_SIZE = _sha256_hash_size; - constructor() { - super(); - - this.heap = _heap_init(); - this.asm = sha256_asm({ Uint8Array: Uint8Array }, null, this.heap.buffer); - - this.reset(); - } + protected static heap_pool = []; + protected static asm_pool = []; + protected static asm_function = sha256_asm; } diff --git a/src/hash/sha512/sha512.ts b/src/hash/sha512/sha512.ts index ddaaa42..0c2191e 100644 --- a/src/hash/sha512/sha512.ts +++ b/src/hash/sha512/sha512.ts @@ -1,6 +1,5 @@ import { sha512_asm, sha512result } from './sha512.asm'; import { Hash } from '../hash'; -import { _heap_init } from '../../other/utils'; export const _sha512_block_size = 128; export const _sha512_hash_size = 64; @@ -11,12 +10,7 @@ export class Sha512 extends Hash { public BLOCK_SIZE = _sha512_block_size; public HASH_SIZE = _sha512_hash_size; - constructor() { - super(); - - this.heap = _heap_init(); - this.asm = sha512_asm({ Uint8Array: Uint8Array }, null, this.heap.buffer); - - this.reset(); - } + protected static heap_pool = []; + protected static asm_pool = []; + protected static asm_function = sha512_asm; } From dc1dfafae9e62b72138617f07d397e91093a97db Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Mon, 2 Jul 2018 16:24:14 +0200 Subject: [PATCH 2/4] Bring back convenience function hash.bytes() --- src/hash/sha1/sha1.ts | 4 ++++ src/hash/sha256/sha256.ts | 4 ++++ src/hash/sha512/sha512.ts | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/hash/sha1/sha1.ts b/src/hash/sha1/sha1.ts index 33c2f95..f9895ad 100644 --- a/src/hash/sha1/sha1.ts +++ b/src/hash/sha1/sha1.ts @@ -13,4 +13,8 @@ export class Sha1 extends Hash { protected static heap_pool = []; protected static asm_pool = []; protected static asm_function = sha1_asm; + + static bytes(data: Uint8Array): Uint8Array { + return new Sha1().process(data).finish().result; + } } diff --git a/src/hash/sha256/sha256.ts b/src/hash/sha256/sha256.ts index 32b9adb..fc85f77 100644 --- a/src/hash/sha256/sha256.ts +++ b/src/hash/sha256/sha256.ts @@ -13,4 +13,8 @@ export class Sha256 extends Hash { protected static heap_pool = []; protected static asm_pool = []; protected static asm_function = sha256_asm; + + static bytes(data: Uint8Array): Uint8Array { + return new Sha256().process(data).finish().result; + } } diff --git a/src/hash/sha512/sha512.ts b/src/hash/sha512/sha512.ts index 0c2191e..e2c1e45 100644 --- a/src/hash/sha512/sha512.ts +++ b/src/hash/sha512/sha512.ts @@ -13,4 +13,8 @@ export class Sha512 extends Hash { protected static heap_pool = []; protected static asm_pool = []; protected static asm_function = sha512_asm; + + static bytes(data: Uint8Array): Uint8Array { + return new Sha512().process(data).finish().result; + } } From cfe5f527aee0bb57de3344c132af363e968a1e2d Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Tue, 31 Jul 2018 17:28:07 +0200 Subject: [PATCH 3/4] Correctly save AES_CTR nonce when pooling --- src/aes/aes.asm.js | 33 +++++++++++++++++++++++++++++++++ src/aes/aes.ts | 2 +- src/aes/ctr.ts | 5 ++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/aes/aes.asm.js b/src/aes/aes.asm.js index 8c3c095..94085bb 100644 --- a/src/aes/aes.asm.js +++ b/src/aes/aes.asm.js @@ -767,6 +767,38 @@ export var AES_asm = function () { return 16; } + /** + * Store the internal nonce vector into the heap. + * @instance + * @memberof AES_asm + * @param {number} pos - offset where to put the data + * @return {number} The number of bytes have been written into the heap, always 16. + */ + function get_nonce(pos) { + pos = pos | 0; + + if (pos & 15) return -1; + + DATA[pos | 0] = N0 >>> 24, + DATA[pos | 1] = N0 >>> 16 & 255, + DATA[pos | 2] = N0 >>> 8 & 255, + DATA[pos | 3] = N0 & 255, + DATA[pos | 4] = N1 >>> 24, + DATA[pos | 5] = N1 >>> 16 & 255, + DATA[pos | 6] = N1 >>> 8 & 255, + DATA[pos | 7] = N1 & 255, + DATA[pos | 8] = N2 >>> 24, + DATA[pos | 9] = N2 >>> 16 & 255, + DATA[pos | 10] = N2 >>> 8 & 255, + DATA[pos | 11] = N2 & 255, + DATA[pos | 12] = N3 >>> 24, + DATA[pos | 13] = N3 >>> 16 & 255, + DATA[pos | 14] = N3 >>> 8 & 255, + DATA[pos | 15] = N3 & 255; + + return 16; + } + /** * GCM initialization. * @instance @@ -887,6 +919,7 @@ export var AES_asm = function () { set_counter: set_counter, get_state: get_state, get_iv: get_iv, + get_nonce: get_nonce, gcm_init: gcm_init, cipher: cipher, mac: mac, diff --git a/src/aes/aes.ts b/src/aes/aes.ts index ed3ff37..f7f72a0 100644 --- a/src/aes/aes.ts +++ b/src/aes/aes.ts @@ -37,7 +37,7 @@ export abstract class AES { } protected release_asm() { - this.iv = this.heap.slice(0, this.asm.get_iv(AES_asm.HEAP_DATA)); + this.iv = this.heap.slice(0, this.asm[this.mode === 'CTR' ? 'get_nonce' : 'get_iv'](AES_asm.HEAP_DATA)); heap_pool.push(this.heap); asm_pool.push(this.asm); diff --git a/src/aes/ctr.ts b/src/aes/ctr.ts index 061c159..dacf477 100644 --- a/src/aes/ctr.ts +++ b/src/aes/ctr.ts @@ -12,9 +12,12 @@ export class AES_CTR extends AES { } constructor(key: Uint8Array, nonce: Uint8Array) { - super(key, undefined, false, 'CTR'); + super(key, nonce, false, 'CTR'); delete this.padding; + } + protected reset(key: Uint8Array, nonce: Uint8Array) { + super.reset(key, undefined); this.AES_CTR_set_options(nonce); } From 1e13a5ef83dc4866bdc683856dddfd031ba60ba3 Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Tue, 31 Jul 2018 18:16:27 +0200 Subject: [PATCH 4/4] Add tests for pooling asm instances between different modes --- test/aes.js | 486 ++++++++++++++++++++++++++++------------------------ 1 file changed, 258 insertions(+), 228 deletions(-) diff --git a/test/aes.js b/test/aes.js index 77a62cd..b5528c9 100644 --- a/test/aes.js +++ b/test/aes.js @@ -123,50 +123,49 @@ describe('AES', () => { }); }); + const ctr_aes_vectors = [ + [ + // key + asmCrypto.hex_to_bytes('2b7e151628aed2a6abf7158809cf4f3c'), + // nonce + asmCrypto.hex_to_bytes('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'), + // input message + asmCrypto.hex_to_bytes('6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710'), + // encrypted message + asmCrypto.hex_to_bytes('874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee') + ], + [ + // key + asmCrypto.hex_to_bytes('2b7e151628aed2a6abf7158809cf4f3c'), + // nonce + asmCrypto.hex_to_bytes('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'), + // input message + asmCrypto.hex_to_bytes('6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c'), + // encrypted message + asmCrypto.hex_to_bytes('874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f300') + ], + [ + // key + asmCrypto.hex_to_bytes('2b7e151628aed2a6abf7158809cf4f3c'), + // nonce + asmCrypto.hex_to_bytes('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'), + // input message + asmCrypto.hex_to_bytes('6bc1bee22e409f96e93d7e11739317'), + // encrypted message + asmCrypto.hex_to_bytes('874d6191b620e3261bef6864990db6') + ], + [ + // key + asmCrypto.hex_to_bytes('603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4'), + // nonce + asmCrypto.hex_to_bytes('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'), + // input message + asmCrypto.hex_to_bytes('6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710'), + // encrypted message + asmCrypto.hex_to_bytes('601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c52b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6') + ] + ]; describe('CTR', () => { - const ctr_aes_vectors = [ - [ - // key - asmCrypto.hex_to_bytes('2b7e151628aed2a6abf7158809cf4f3c'), - // nonce - asmCrypto.hex_to_bytes('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'), - // input message - asmCrypto.hex_to_bytes('6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710'), - // encrypted message - asmCrypto.hex_to_bytes('874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee') - ], - [ - // key - asmCrypto.hex_to_bytes('2b7e151628aed2a6abf7158809cf4f3c'), - // nonce - asmCrypto.hex_to_bytes('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'), - // input message - asmCrypto.hex_to_bytes('6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c'), - // encrypted message - asmCrypto.hex_to_bytes('874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f300') - ], - [ - // key - asmCrypto.hex_to_bytes('2b7e151628aed2a6abf7158809cf4f3c'), - // nonce - asmCrypto.hex_to_bytes('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'), - // input message - asmCrypto.hex_to_bytes('6bc1bee22e409f96e93d7e11739317'), - // encrypted message - asmCrypto.hex_to_bytes('874d6191b620e3261bef6864990db6') - ], - [ - // key - asmCrypto.hex_to_bytes('603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4'), - // nonce - asmCrypto.hex_to_bytes('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'), - // input message - asmCrypto.hex_to_bytes('6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710'), - // encrypted message - asmCrypto.hex_to_bytes('601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c52b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6') - ] - ]; - it('asmCrypto.AES_CTR.encrypt / asmCrypto.AES_CTR.decrypt', function () { for (let i = 0; i < ctr_aes_vectors.length; ++i) { const key = new Uint8Array(ctr_aes_vectors[i][0]); @@ -180,192 +179,191 @@ describe('AES', () => { } }); }); + const gcm_aes_vectors = [ + [ + // key + asmCrypto.hex_to_bytes('00000000000000000000000000000000'), + // nonce + asmCrypto.hex_to_bytes('000000000000000000000000'), + // adata + undefined, + // tagSize + 16, + // input message + asmCrypto.string_to_bytes(''), + // encrypted message + asmCrypto.hex_to_bytes('58e2fccefa7e3061367f1d57a4e7455a') + ], + [ + // key + asmCrypto.hex_to_bytes('00000000000000000000000000000000'), + // nonce + asmCrypto.hex_to_bytes('000000000000000000000000'), + // adata + asmCrypto.string_to_bytes(''), + // tagSize + 16, + // input message + asmCrypto.hex_to_bytes('00000000000000000000000000000000'), + // encrypted message + asmCrypto.hex_to_bytes('0388dace60b6a392f328c2b971b2fe78ab6e47d42cec13bdf53a67b21257bddf') + ], + [ + // key + asmCrypto.hex_to_bytes('feffe9928665731c6d6a8f9467308308'), + // nonce + asmCrypto.hex_to_bytes('cafebabefacedbaddecaf888'), + // adata + asmCrypto.string_to_bytes(''), + // tagSize + 16, + // input message + asmCrypto.hex_to_bytes('d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255'), + // encrypted message + asmCrypto.hex_to_bytes('42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f59854d5c2af327cd64a62cf35abd2ba6fab4') + ], + [ + // key + asmCrypto.hex_to_bytes('feffe9928665731c6d6a8f9467308308'), + // nonce + asmCrypto.hex_to_bytes('cafebabefacedbaddecaf888'), + // adata + asmCrypto.hex_to_bytes('feedfacedeadbeeffeedfacedeadbeefabaddad2'), + // tagSize + 16, + // input message + asmCrypto.hex_to_bytes('d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39'), + // encrypted message + asmCrypto.hex_to_bytes('42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e0915bc94fbc3221a5db94fae95ae7121a47') + ], + [ + // key + asmCrypto.hex_to_bytes('feffe9928665731c6d6a8f9467308308'), + // nonce + asmCrypto.hex_to_bytes('cafebabefacedbad'), + // adata + asmCrypto.hex_to_bytes('feedfacedeadbeeffeedfacedeadbeefabaddad2'), + // tagSize + 16, + // input message + asmCrypto.hex_to_bytes('d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39'), + // encrypted message + asmCrypto.hex_to_bytes('61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f45983612d2e79e3b0785561be14aaca2fccb') + ], + [ + // key + asmCrypto.hex_to_bytes('feffe9928665731c6d6a8f9467308308'), + // nonce + asmCrypto.hex_to_bytes('9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b'), + // adata + asmCrypto.hex_to_bytes('feedfacedeadbeeffeedfacedeadbeefabaddad2'), + // tagSize + 16, + // input message + asmCrypto.hex_to_bytes('d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39'), + // encrypted message + asmCrypto.hex_to_bytes('8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5619cc5aefffe0bfa462af43c1699d050') + ], + [ + // key + asmCrypto.hex_to_bytes('0000000000000000000000000000000000000000000000000000000000000000'), + // nonce + asmCrypto.hex_to_bytes('000000000000000000000000'), + // adata + asmCrypto.string_to_bytes(''), + // tagSize + 16, + // input message + asmCrypto.hex_to_bytes('00000000000000000000000000000000'), + // encrypted message + asmCrypto.hex_to_bytes('cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919') + ], + [ + // key + asmCrypto.hex_to_bytes('0000000000000000000000000000000000000000000000000000000000000000'), + // nonce + asmCrypto.hex_to_bytes('000000000000000000000000'), + // adata + asmCrypto.string_to_bytes(''), + // tagSize + 16, + // input message + asmCrypto.hex_to_bytes(''), + // encrypted message + asmCrypto.hex_to_bytes('530f8afbc74536b9a963b4f1c4cb738b') + ], + [ + // key + asmCrypto.hex_to_bytes('0000000000000000000000000000000000000000000000000000000000000000'), + // nonce + asmCrypto.hex_to_bytes('000000000000000000000000'), + // adata + asmCrypto.string_to_bytes(''), + // tagSize + 16, + // input message + asmCrypto.string_to_bytes(''), + // encrypted message + asmCrypto.hex_to_bytes('530f8afbc74536b9a963b4f1c4cb738b') + ], + [ + // key + asmCrypto.hex_to_bytes('0000000000000000000000000000000000000000000000000000000000000000'), + // nonce + asmCrypto.hex_to_bytes('000000000000000000000000'), + // adata + asmCrypto.string_to_bytes(''), + // tagSize + 16, + // input message + asmCrypto.hex_to_bytes('00000000000000000000000000000000'), + // encrypted message + asmCrypto.hex_to_bytes('cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919') + ], + [ + // key + asmCrypto.hex_to_bytes('feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308'), + // nonce + asmCrypto.hex_to_bytes('9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b'), + // adata + asmCrypto.hex_to_bytes('feedfacedeadbeeffeedfacedeadbeefabaddad2'), + // tagSize + 16, + // input message + asmCrypto.hex_to_bytes('d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39'), + // encrypted message + asmCrypto.hex_to_bytes('5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3fa44a8266ee1c8eb0c8b5d4cf5ae9f19a') + ], + [ // Test case for issue #70 (https://github.com/vibornoff/asmcrypto.js/issues/70) + // key + asmCrypto.hex_to_bytes('00000000000000000000000000000000'), + // nonce + asmCrypto.hex_to_bytes('00'), + // adata + asmCrypto.string_to_bytes(''), + // tagSize + 16, + // input message + asmCrypto.hex_to_bytes('00'), + // encrypted message + asmCrypto.hex_to_bytes('e9d60634580263ebab909efa6623dafc61') + ], + [ // Test case for issue #70 (https://github.com/vibornoff/asmcrypto.js/issues/92) + // key + asmCrypto.base64_to_bytes('dGQhii+B7+eLLHRiOA690w=='), + // nonce + asmCrypto.base64_to_bytes('R8q1njARXS7urWv3'), + // adata + undefined, + // tagSize + 16, + // input message + asmCrypto.base64_to_bytes('dGQhwoovwoHDr8OnwossdGI4DsK9w5M='), + // encrypted message + asmCrypto.base64_to_bytes('L3zqVYAOsRk7zMg2KsNTVShcad8TjIQ7umfsvia21QO0XTj8vaeR') + ], + ]; describe('GCM', () => { - const gcm_aes_vectors = [ - [ - // key - asmCrypto.hex_to_bytes('00000000000000000000000000000000'), - // nonce - asmCrypto.hex_to_bytes('000000000000000000000000'), - // adata - undefined, - // tagSize - 16, - // input message - asmCrypto.string_to_bytes(''), - // encrypted message - asmCrypto.hex_to_bytes('58e2fccefa7e3061367f1d57a4e7455a') - ], - [ - // key - asmCrypto.hex_to_bytes('00000000000000000000000000000000'), - // nonce - asmCrypto.hex_to_bytes('000000000000000000000000'), - // adata - asmCrypto.string_to_bytes(''), - // tagSize - 16, - // input message - asmCrypto.hex_to_bytes('00000000000000000000000000000000'), - // encrypted message - asmCrypto.hex_to_bytes('0388dace60b6a392f328c2b971b2fe78ab6e47d42cec13bdf53a67b21257bddf') - ], - [ - // key - asmCrypto.hex_to_bytes('feffe9928665731c6d6a8f9467308308'), - // nonce - asmCrypto.hex_to_bytes('cafebabefacedbaddecaf888'), - // adata - asmCrypto.string_to_bytes(''), - // tagSize - 16, - // input message - asmCrypto.hex_to_bytes('d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255'), - // encrypted message - asmCrypto.hex_to_bytes('42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f59854d5c2af327cd64a62cf35abd2ba6fab4') - ], - [ - // key - asmCrypto.hex_to_bytes('feffe9928665731c6d6a8f9467308308'), - // nonce - asmCrypto.hex_to_bytes('cafebabefacedbaddecaf888'), - // adata - asmCrypto.hex_to_bytes('feedfacedeadbeeffeedfacedeadbeefabaddad2'), - // tagSize - 16, - // input message - asmCrypto.hex_to_bytes('d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39'), - // encrypted message - asmCrypto.hex_to_bytes('42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e0915bc94fbc3221a5db94fae95ae7121a47') - ], - [ - // key - asmCrypto.hex_to_bytes('feffe9928665731c6d6a8f9467308308'), - // nonce - asmCrypto.hex_to_bytes('cafebabefacedbad'), - // adata - asmCrypto.hex_to_bytes('feedfacedeadbeeffeedfacedeadbeefabaddad2'), - // tagSize - 16, - // input message - asmCrypto.hex_to_bytes('d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39'), - // encrypted message - asmCrypto.hex_to_bytes('61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f45983612d2e79e3b0785561be14aaca2fccb') - ], - [ - // key - asmCrypto.hex_to_bytes('feffe9928665731c6d6a8f9467308308'), - // nonce - asmCrypto.hex_to_bytes('9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b'), - // adata - asmCrypto.hex_to_bytes('feedfacedeadbeeffeedfacedeadbeefabaddad2'), - // tagSize - 16, - // input message - asmCrypto.hex_to_bytes('d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39'), - // encrypted message - asmCrypto.hex_to_bytes('8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5619cc5aefffe0bfa462af43c1699d050') - ], - [ - // key - asmCrypto.hex_to_bytes('0000000000000000000000000000000000000000000000000000000000000000'), - // nonce - asmCrypto.hex_to_bytes('000000000000000000000000'), - // adata - asmCrypto.string_to_bytes(''), - // tagSize - 16, - // input message - asmCrypto.hex_to_bytes('00000000000000000000000000000000'), - // encrypted message - asmCrypto.hex_to_bytes('cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919') - ], - [ - // key - asmCrypto.hex_to_bytes('0000000000000000000000000000000000000000000000000000000000000000'), - // nonce - asmCrypto.hex_to_bytes('000000000000000000000000'), - // adata - asmCrypto.string_to_bytes(''), - // tagSize - 16, - // input message - asmCrypto.hex_to_bytes(''), - // encrypted message - asmCrypto.hex_to_bytes('530f8afbc74536b9a963b4f1c4cb738b') - ], - [ - // key - asmCrypto.hex_to_bytes('0000000000000000000000000000000000000000000000000000000000000000'), - // nonce - asmCrypto.hex_to_bytes('000000000000000000000000'), - // adata - asmCrypto.string_to_bytes(''), - // tagSize - 16, - // input message - asmCrypto.string_to_bytes(''), - // encrypted message - asmCrypto.hex_to_bytes('530f8afbc74536b9a963b4f1c4cb738b') - ], - [ - // key - asmCrypto.hex_to_bytes('0000000000000000000000000000000000000000000000000000000000000000'), - // nonce - asmCrypto.hex_to_bytes('000000000000000000000000'), - // adata - asmCrypto.string_to_bytes(''), - // tagSize - 16, - // input message - asmCrypto.hex_to_bytes('00000000000000000000000000000000'), - // encrypted message - asmCrypto.hex_to_bytes('cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919') - ], - [ - // key - asmCrypto.hex_to_bytes('feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308'), - // nonce - asmCrypto.hex_to_bytes('9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b'), - // adata - asmCrypto.hex_to_bytes('feedfacedeadbeeffeedfacedeadbeefabaddad2'), - // tagSize - 16, - // input message - asmCrypto.hex_to_bytes('d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39'), - // encrypted message - asmCrypto.hex_to_bytes('5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3fa44a8266ee1c8eb0c8b5d4cf5ae9f19a') - ], - [ // Test case for issue #70 (https://github.com/vibornoff/asmcrypto.js/issues/70) - // key - asmCrypto.hex_to_bytes('00000000000000000000000000000000'), - // nonce - asmCrypto.hex_to_bytes('00'), - // adata - asmCrypto.string_to_bytes(''), - // tagSize - 16, - // input message - asmCrypto.hex_to_bytes('00'), - // encrypted message - asmCrypto.hex_to_bytes('e9d60634580263ebab909efa6623dafc61') - ], - [ // Test case for issue #70 (https://github.com/vibornoff/asmcrypto.js/issues/92) - // key - asmCrypto.base64_to_bytes('dGQhii+B7+eLLHRiOA690w=='), - // nonce - asmCrypto.base64_to_bytes('R8q1njARXS7urWv3'), - // adata - undefined, - // tagSize - 16, - // input message - asmCrypto.base64_to_bytes('dGQhwoovwoHDr8OnwossdGI4DsK9w5M='), - // encrypted message - asmCrypto.base64_to_bytes('L3zqVYAOsRk7zMg2KsNTVShcad8TjIQ7umfsvia21QO0XTj8vaeR') - ], - ]; - it("asmCrypto.AES_GCM.encrypt", function () { for (let i = 0; i < gcm_aes_vectors.length; ++i) { const key = gcm_aes_vectors[i][0]; @@ -705,4 +703,36 @@ describe('AES', () => { } }); }); + describe('Pooling', () => { + it('asmCrypto.AES_CTR / asmCrypto.AES_ECB', () => { + const key = new Uint8Array(ctr_aes_vectors[2][0]); + const nonce = new Uint8Array(ctr_aes_vectors[2][1]); + const clear = new Uint8Array(ctr_aes_vectors[2][2]); + const cipher = new Uint8Array(ctr_aes_vectors[2][3]); + + const ctr = new asmCrypto.AES_CTR(key, nonce); + expect(asmCrypto.bytes_to_hex(ctr.encrypt(asmCrypto.hex_to_bytes('6bc1bee22e409f96e93d7e117393172a'))), 'first call to encrypt').to.be.equal('874d6191b620e3261bef6864990db6ce'); + const ecb = new asmCrypto.AES_ECB(key, nonce); + const ecb2 = new asmCrypto.AES_ECB(key, nonce); + ecb2.encrypt(clear); + expect(asmCrypto.bytes_to_hex(ctr.encrypt(asmCrypto.hex_to_bytes('ae2d8a571e03ac9c9eb76fac45af8e51'))), 'second call to encrypt').to.be.equal('c401492f668a9bd32003a7b75215e215'); + }); + it('asmCrypto.AES_GCM / asmCrypto.AES_CCM', () => { + // Note: GCM and CCM are currently not pooled. + + const key = gcm_aes_vectors[0][0]; + const nonce = gcm_aes_vectors[0][1]; + const adata = gcm_aes_vectors[0][2]; + const tagsize = gcm_aes_vectors[0][3]; + const cleartext = gcm_aes_vectors[0][4]; + const ciphertext = gcm_aes_vectors[0][5]; + + const gcm = new asmCrypto.AES_GCM(key, nonce, adata, tagsize); + expect(asmCrypto.bytes_to_hex(gcm.encrypt(cleartext)), 'first call to encrypt').to.be.equal(asmCrypto.bytes_to_hex(ciphertext)); + const ccm = new asmCrypto.AES_CCM(key, nonce, adata, tagsize); + const ccm2 = new asmCrypto.AES_CCM(key, nonce, adata, tagsize); + ccm2.encrypt(cleartext); + expect(asmCrypto.bytes_to_hex(gcm.encrypt(cleartext)), 'second call to encrypt').to.be.equal(asmCrypto.bytes_to_hex(ciphertext)); + }); + }); });