PHP WebShell

Текущая директория: /opt/BitGoJS/node_modules/asmcrypto.js/src/aes/ccm

Просмотр файла: ccm.js

/**
 * 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 choosen to be no more than 65279 bytes ( 2^16 - 2^8 ),
 * wich 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, is_bytes, is_number } from '../../utils';
import { IllegalArgumentError, IllegalStateError, SecurityError } from '../../errors';

var _AES_CCM_adata_maxLength = 65279, // 2^16 - 2^8
  _AES_CCM_data_maxLength = 4503599627370480; // 2^52 - 2^4

export class AES_CCM extends AES {
  constructor(key, nonce, adata, tagSize, dataLength, heap, asm) {
    super(key, undefined, undefined, heap, asm);
    this.tagSize = 16;
    this.lengthSize = 4;
    this.nonce = null;
    this.adata = null;
    this.iv = null;
    this.counter = 1;
    this.dataLength = -1;

    this.AES_CCM_reset(key, undefined, nonce, adata, undefined, undefined, tagSize, dataLength);

    this.mode = 'CCM';
    this.BLOCK_SIZE = 16;
  }

  encrypt(data) {
    return this.AES_CCM_encrypt(data);
  }
  decrypt(data) {
    return this.AES_CCM_decrypt(data);
  }

  AES_CCM_reset(key, iv, nonce, adata, counter, lengthSize, tagSize, dataLength) {
    this.AES_reset(key, iv);

    if (tagSize !== undefined) {
      if (!is_number(tagSize)) throw new TypeError('tagSize must be a number');

      if (tagSize < 4 || tagSize > 16 || tagSize & 1) throw new IllegalArgumentError('illegal tagSize value');

      this.tagSize = tagSize;
    } else {
      this.tagSize = 16;
    }

    if (nonce !== undefined) {
      if (!is_bytes(nonce)) {
        throw new TypeError('unexpected nonce type');
      }

      if (nonce.length < 8 || nonce.length > 13) throw new IllegalArgumentError('illegal nonce length');

      this.nonce = nonce;
      this.lengthSize = lengthSize = 15 - nonce.length;

      nonce = new Uint8Array(nonce.length + 1);
      (nonce[0] = lengthSize - 1), nonce.set(this.nonce, 1);
    } else {
      throw new Error('nonce is required');
    }

    // Either counter, iv
    if (iv !== undefined) {
      if (adata !== undefined) throw new IllegalStateError('you should specify either adata or iv, not both');

      if (!is_number(counter)) throw new TypeError('counter must be a number');

      if (counter < 1 || counter >= Math.pow(2, 8 * lengthSize) - 16)
        throw new IllegalArgumentError('illegal counter value');

      this.counter = counter;
    } else if (adata !== undefined && adata !== null) {
      // Or adata, dataLength
      if (!is_bytes(adata)) {
        throw new TypeError('unexpected adata type');
      }

      if (adata.length > _AES_CCM_adata_maxLength) throw new IllegalArgumentError('illegal adata length');

      if (!is_number(dataLength)) throw new TypeError('dataLength must be a number');

      if (dataLength < 0 || dataLength > _AES_CCM_data_maxLength || dataLength > Math.pow(2, 8 * lengthSize) - 16)
        throw new IllegalArgumentError('illegal dataLength value');

      this.adata = adata.length ? adata : null;
      this.dataLength = dataLength;
      this.counter = counter = 1;

      this.AES_CCM_calculate_iv();
      iv = this.iv;
    } else {
      // Assume adata is empty, check dataLength
      if (!is_number(dataLength)) throw new TypeError('dataLength must be a number');

      if (dataLength < 0 || dataLength > _AES_CCM_data_maxLength || dataLength > Math.pow(2, 8 * lengthSize) - 16)
        throw new IllegalArgumentError('illegal dataLength value');

      this.adata = null;
      this.dataLength = dataLength;
      this.counter = counter = 1;

      this.AES_CCM_calculate_iv();
      iv = this.iv;
    }

    this.AES_set_iv(iv);
    this.AES_CTR_set_options(nonce, counter, 8 * lengthSize);

    return this;
  }

  AES_CCM_calculate_iv() {
    var nonce = this.nonce,
      adata = this.adata,
      tagSize = this.tagSize,
      lengthSize = this.lengthSize,
      dataLength = this.dataLength;

    var 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);

    this.iv = new Uint8Array(this.heap.subarray(0, 16));
  }

  _cbc_mac_process(data) {
    var heap = this.heap,
      asm = this.asm,
      dpos = 0,
      dlen = data.length || 0,
      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_decrypt(data) {
    this.dataLength = data.length || 0;

    var result1 = this.AES_CCM_Decrypt_process(data).result;
    var result2 = this.AES_CCM_Decrypt_finish().result;

    var result = new Uint8Array(result1.length + result2.length);
    if (result1.length) result.set(result1);
    if (result2.length) result.set(result2, result1.length);
    this.result = result;

    return this;
  }

  AES_CCM_encrypt(data) {
    this.dataLength = data.length || 0;

    var result1 = this.AES_CCM_Encrypt_process(data).result;
    var result2 = this.AES_CCM_Encrypt_finish().result;

    var result = new Uint8Array(result1.length + result2.length);
    if (result1.length) result.set(result1);
    if (result2.length) result.set(result2, result1.length);
    this.result = result;

    return this;
  }

  AES_CCM_Encrypt_process(data) {
    if (!is_bytes(data)) throw new TypeError("data isn't of expected type");

    var dpos = 0,
      dlen = data.length || 0,
      asm = this.asm,
      heap = this.heap,
      counter = this.counter,
      pos = this.pos,
      len = this.len,
      rpos = 0,
      rlen = (len + dlen) & -16,
      wlen = 0;

    if (((counter - 1) << 4) + len + dlen > _AES_CCM_data_maxLength)
      // ??? should check against lengthSize
      throw new RangeError('counter overflow');

    var 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.result = result;
    this.counter = counter;
    this.pos = pos;
    this.len = len;

    return this;
  }

  AES_CCM_Encrypt_finish() {
    var asm = this.asm,
      heap = this.heap,
      tagSize = this.tagSize,
      pos = this.pos,
      len = this.len,
      wlen = 0;

    var result = new Uint8Array(len + tagSize);

    for (var i = len; i & 15; i++) heap[pos + i] = 0;

    wlen = asm.mac(AES_asm.MAC.CBC, AES_asm.HEAP_DATA + pos, i);
    wlen = 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.result = result;
    this.counter = 1;
    this.pos = 0;
    this.len = 0;

    return this;
  }

  AES_CCM_Decrypt_process(data) {
    if (!is_bytes(data)) throw new TypeError("data isn't of expected type");

    var dpos = 0,
      dlen = data.length || 0,
      asm = this.asm,
      heap = this.heap,
      counter = this.counter,
      tagSize = this.tagSize,
      pos = this.pos,
      len = this.len,
      rpos = 0,
      rlen = len + dlen > tagSize ? (len + dlen - tagSize) & -16 : 0,
      tlen = len + dlen - rlen,
      wlen = 0;

    if (((counter - 1) << 4) + len + dlen > _AES_CCM_data_maxLength) throw new RangeError('counter overflow');

    var 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.result = result;
    this.counter = counter;
    this.pos = pos;
    this.len = len;

    return this;
  }

  AES_CCM_Decrypt_finish() {
    var asm = this.asm,
      heap = this.heap,
      tagSize = this.tagSize,
      pos = this.pos,
      len = this.len,
      rlen = len - tagSize,
      wlen = 0;

    if (len < tagSize) throw new IllegalStateError('authentication tag not found');

    var result = new Uint8Array(rlen),
      atag = new Uint8Array(heap.subarray(pos + rlen, pos + len));

    wlen = asm.cipher(AES_asm.DEC.CTR, AES_asm.HEAP_DATA + pos, (rlen + 15) & -16);
    result.set(heap.subarray(pos, pos + rlen));

    for (var i = rlen; i & 15; i++) heap[pos + i] = 0;
    wlen = 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);

    var acheck = 0;
    for (var i = 0; i < tagSize; ++i) acheck |= atag[i] ^ heap[i];
    if (acheck) throw new SecurityError('data integrity check failed');

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

    return this;
  }

  reset() {}
}

export class AES_CCM_Encrypt extends AES_CCM {
  constructor(key, nonce, adata, tagSize, dataLength, heap, asm) {
    super(key, nonce, adata, tagSize, dataLength, heap, asm);
  }
  process(data) {
    return this.AES_CCM_Encrypt_process(data);
  }
  finish() {
    return this.AES_CCM_Encrypt_finish();
  }
}

export class AES_CCM_Decrypt extends AES_CCM {
  constructor(key, nonce, adata, tagSize, dataLength, heap, asm) {
    super(key, nonce, adata, tagSize, dataLength, heap, asm);
  }
  process(data) {
    return this.AES_CCM_Decrypt_process(data);
  }
  finish() {
    return this.AES_CCM_Decrypt_finish();
  }
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!