Swap-Aggregator-Subgraph/node_modules/asmcrypto.js/dist_es8/aes/ccm.js
Richa-iitr d211083153 Revert "Revert "added handler""
This reverts commit c36ee8c5ca.
2022-07-03 07:30:05 +05:30

276 lines
11 KiB
JavaScript
Executable File

/**
* Counter with CBC-MAC (CCM)
*
* Due to JS limitations (52 bits of Number precision) maximum encrypted message length
* is limited to ~4 PiB ( 2^52 - 16 ) per `nonce`-`key` pair.
* That also limits `lengthSize` parameter maximum value to 7 (not 8 as described in RFC3610).
*
* Additional authenticated data `adata` maximum length is chosen to be no more than 65279 bytes ( 2^16 - 2^8 ),
* which is considered enough for the most of use-cases.
*
* And one more important thing: in case of progressive ciphering of a data stream (in other
* words when data can't be held in-memory at a whole and are ciphered chunk-by-chunk)
* you have to know the `dataLength` in advance and pass that value to the cipher options.
*/
import { AES_asm } from './aes.asm';
import { AES } from './aes';
import { _heap_write } from '../other/utils';
import { IllegalArgumentError, IllegalStateError, SecurityError } from '../other/errors';
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 {
constructor(key, nonce, adata, tagSize = 16, dataLength) {
super(key, undefined, undefined, 'CCM');
this.counter = 1;
this.dataLength = -1;
// Tag size
if (tagSize < 4 || tagSize > 16 || tagSize & 1)
throw new IllegalArgumentError('illegal tagSize value');
this.tagSize = tagSize;
// Nonce
this.nonce = nonce;
if (nonce.length < 8 || nonce.length > 13)
throw new IllegalArgumentError('illegal nonce length');
this.lengthSize = 15 - nonce.length;
nonce = new Uint8Array(nonce.length + 1);
nonce[0] = this.lengthSize - 1;
nonce.set(this.nonce, 1);
if (dataLength < 0 || dataLength > _AES_CCM_data_maxLength || dataLength > Math.pow(2, 8 * this.lengthSize) - 16)
throw new IllegalArgumentError('illegal dataLength value');
if (adata !== undefined) {
if (adata.length > _AES_CCM_adata_maxLength)
throw new IllegalArgumentError('illegal adata length');
this.adata = adata.length ? adata : undefined;
}
this.dataLength = dataLength;
this.counter = 1;
this.AES_CCM_calculate_iv();
this.AES_CTR_set_options(nonce, this.counter, 8 * this.lengthSize);
}
static encrypt(clear, key, nonce, adata, tagsize = 16) {
return new AES_CCM(key, nonce, adata, tagsize, clear.length).encrypt(clear);
}
static decrypt(cipher, key, nonce, adata, tagsize = 16) {
return new AES_CCM(key, nonce, adata, tagsize, cipher.length - tagsize).decrypt(cipher);
}
encrypt(data) {
this.dataLength = data.length || 0;
const result1 = this.AES_CCM_Encrypt_process(data);
const result2 = this.AES_CCM_Encrypt_finish();
const result = new Uint8Array(result1.length + result2.length);
if (result1.length)
result.set(result1);
if (result2.length)
result.set(result2, result1.length);
return result;
}
decrypt(data) {
this.dataLength = data.length || 0;
const result1 = this.AES_CCM_Decrypt_process(data);
const result2 = this.AES_CCM_Decrypt_finish();
const result = new Uint8Array(result1.length + result2.length);
if (result1.length)
result.set(result1);
if (result2.length)
result.set(result2, result1.length);
return result;
}
AES_CCM_calculate_iv() {
const nonce = this.nonce;
const adata = this.adata;
const tagSize = this.tagSize;
const lengthSize = this.lengthSize;
const dataLength = this.dataLength;
const data = new Uint8Array(16 + (adata ? 2 + adata.length : 0));
// B0: flags(adata?, M', L'), nonce, len(data)
data[0] = (adata ? 64 : 0) | ((tagSize - 2) << 2) | (lengthSize - 1);
data.set(nonce, 1);
if (lengthSize > 6)
data[9] = ((dataLength / 0x100000000) >>> 16) & 15;
if (lengthSize > 5)
data[10] = ((dataLength / 0x100000000) >>> 8) & 255;
if (lengthSize > 4)
data[11] = (dataLength / 0x100000000) & 255;
if (lengthSize > 3)
data[12] = dataLength >>> 24;
if (lengthSize > 2)
data[13] = (dataLength >>> 16) & 255;
data[14] = (dataLength >>> 8) & 255;
data[15] = dataLength & 255;
// B*: len(adata), adata
if (adata) {
data[16] = (adata.length >>> 8) & 255;
data[17] = adata.length & 255;
data.set(adata, 18);
}
this._cbc_mac_process(data);
this.asm.get_state(AES_asm.HEAP_DATA);
const iv = new Uint8Array(this.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));
}
_cbc_mac_process(data) {
const heap = this.heap;
const asm = this.asm;
let dpos = 0;
let dlen = data.length || 0;
let wlen = 0;
while (dlen > 0) {
wlen = _heap_write(heap, 0, data, dpos, dlen);
while (wlen & 15)
heap[wlen++] = 0;
dpos += wlen;
dlen -= wlen;
asm.mac(AES_asm.MAC.CBC, AES_asm.HEAP_DATA, wlen);
}
}
AES_CCM_Encrypt_process(data) {
const asm = this.asm;
const heap = this.heap;
let dpos = 0;
let dlen = data.length || 0;
let counter = this.counter;
let pos = this.pos;
let len = this.len;
const rlen = (len + dlen) & -16;
let rpos = 0;
let wlen = 0;
if (((counter - 1) << 4) + len + dlen > _AES_CCM_data_maxLength)
// ??? should check against lengthSize
throw new RangeError('counter overflow');
const result = new Uint8Array(rlen);
while (dlen > 0) {
wlen = _heap_write(heap, pos + len, data, dpos, dlen);
len += wlen;
dpos += wlen;
dlen -= wlen;
wlen = asm.mac(AES_asm.MAC.CBC, AES_asm.HEAP_DATA + pos, len);
wlen = asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA + pos, wlen);
if (wlen)
result.set(heap.subarray(pos, pos + wlen), rpos);
counter += wlen >>> 4;
rpos += wlen;
if (wlen < len) {
pos += wlen;
len -= wlen;
}
else {
pos = 0;
len = 0;
}
}
this.counter = counter;
this.pos = pos;
this.len = len;
return result;
}
AES_CCM_Encrypt_finish() {
const asm = this.asm;
const heap = this.heap;
const tagSize = this.tagSize;
const pos = this.pos;
const len = this.len;
const result = new Uint8Array(len + tagSize);
let i = len;
for (; i & 15; i++)
heap[pos + i] = 0;
asm.mac(AES_asm.MAC.CBC, AES_asm.HEAP_DATA + pos, i);
asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA + pos, i);
if (len)
result.set(heap.subarray(pos, pos + len));
asm.set_counter(0, 0, 0, 0);
asm.get_iv(AES_asm.HEAP_DATA);
asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA, 16);
result.set(heap.subarray(0, tagSize), len);
this.counter = 1;
this.pos = 0;
this.len = 0;
return result;
}
AES_CCM_Decrypt_process(data) {
let dpos = 0;
let dlen = data.length || 0;
const asm = this.asm;
const heap = this.heap;
let counter = this.counter;
const tagSize = this.tagSize;
let pos = this.pos;
let len = this.len;
let rpos = 0;
const rlen = len + dlen > tagSize ? (len + dlen - tagSize) & -16 : 0;
const tlen = len + dlen - rlen;
let wlen = 0;
if (((counter - 1) << 4) + len + dlen > _AES_CCM_data_maxLength)
throw new RangeError('counter overflow');
const result = new Uint8Array(rlen);
while (dlen > tlen) {
wlen = _heap_write(heap, pos + len, data, dpos, dlen - tlen);
len += wlen;
dpos += wlen;
dlen -= wlen;
wlen = asm.cipher(AES_asm.DEC.CTR, AES_asm.HEAP_DATA + pos, wlen);
wlen = asm.mac(AES_asm.MAC.CBC, AES_asm.HEAP_DATA + pos, wlen);
if (wlen)
result.set(heap.subarray(pos, pos + wlen), rpos);
counter += wlen >>> 4;
rpos += wlen;
pos = 0;
len = 0;
}
if (dlen > 0) {
len += _heap_write(heap, 0, data, dpos, dlen);
}
this.counter = counter;
this.pos = pos;
this.len = len;
return result;
}
AES_CCM_Decrypt_finish() {
const asm = this.asm;
const heap = this.heap;
const tagSize = this.tagSize;
const pos = this.pos;
const len = this.len;
const rlen = len - tagSize;
if (len < tagSize)
throw new IllegalStateError('authentication tag not found');
const result = new Uint8Array(rlen);
const atag = new Uint8Array(heap.subarray(pos + rlen, pos + len));
asm.cipher(AES_asm.DEC.CTR, AES_asm.HEAP_DATA + pos, (rlen + 15) & -16);
result.set(heap.subarray(pos, pos + rlen));
let i = rlen;
for (; i & 15; i++)
heap[pos + i] = 0;
asm.mac(AES_asm.MAC.CBC, AES_asm.HEAP_DATA + pos, i);
asm.set_counter(0, 0, 0, 0);
asm.get_iv(AES_asm.HEAP_DATA);
asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA, 16);
let acheck = 0;
for (let j = 0; j < tagSize; ++j)
acheck |= atag[j] ^ heap[j];
if (acheck)
throw new SecurityError('data integrity check failed');
this.counter = 1;
this.pos = 0;
this.len = 0;
return result;
}
AES_CTR_set_options(nonce, counter, size) {
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);
const len = nonce.length;
if (!len || len > 16)
throw new IllegalArgumentError('illegal nonce size');
this.nonce = nonce;
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));
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);
}
}