| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 | 'use strict';var assert = require('minimalistic-assert');function Cipher(options) {  this.options = options;  this.type = this.options.type;  this.blockSize = 8;  this._init();  this.buffer = new Array(this.blockSize);  this.bufferOff = 0;  this.padding = options.padding !== false}module.exports = Cipher;Cipher.prototype._init = function _init() {  // Might be overrided};Cipher.prototype.update = function update(data) {  if (data.length === 0)    return [];  if (this.type === 'decrypt')    return this._updateDecrypt(data);  else    return this._updateEncrypt(data);};Cipher.prototype._buffer = function _buffer(data, off) {  // Append data to buffer  var min = Math.min(this.buffer.length - this.bufferOff, data.length - off);  for (var i = 0; i < min; i++)    this.buffer[this.bufferOff + i] = data[off + i];  this.bufferOff += min;  // Shift next  return min;};Cipher.prototype._flushBuffer = function _flushBuffer(out, off) {  this._update(this.buffer, 0, out, off);  this.bufferOff = 0;  return this.blockSize;};Cipher.prototype._updateEncrypt = function _updateEncrypt(data) {  var inputOff = 0;  var outputOff = 0;  var count = ((this.bufferOff + data.length) / this.blockSize) | 0;  var out = new Array(count * this.blockSize);  if (this.bufferOff !== 0) {    inputOff += this._buffer(data, inputOff);    if (this.bufferOff === this.buffer.length)      outputOff += this._flushBuffer(out, outputOff);  }  // Write blocks  var max = data.length - ((data.length - inputOff) % this.blockSize);  for (; inputOff < max; inputOff += this.blockSize) {    this._update(data, inputOff, out, outputOff);    outputOff += this.blockSize;  }  // Queue rest  for (; inputOff < data.length; inputOff++, this.bufferOff++)    this.buffer[this.bufferOff] = data[inputOff];  return out;};Cipher.prototype._updateDecrypt = function _updateDecrypt(data) {  var inputOff = 0;  var outputOff = 0;  var count = Math.ceil((this.bufferOff + data.length) / this.blockSize) - 1;  var out = new Array(count * this.blockSize);  // TODO(indutny): optimize it, this is far from optimal  for (; count > 0; count--) {    inputOff += this._buffer(data, inputOff);    outputOff += this._flushBuffer(out, outputOff);  }  // Buffer rest of the input  inputOff += this._buffer(data, inputOff);  return out;};Cipher.prototype.final = function final(buffer) {  var first;  if (buffer)    first = this.update(buffer);  var last;  if (this.type === 'encrypt')    last = this._finalEncrypt();  else    last = this._finalDecrypt();  if (first)    return first.concat(last);  else    return last;};Cipher.prototype._pad = function _pad(buffer, off) {  if (off === 0)    return false;  while (off < buffer.length)    buffer[off++] = 0;  return true;};Cipher.prototype._finalEncrypt = function _finalEncrypt() {  if (!this._pad(this.buffer, this.bufferOff))    return [];  var out = new Array(this.blockSize);  this._update(this.buffer, 0, out, 0);  return out;};Cipher.prototype._unpad = function _unpad(buffer) {  return buffer;};Cipher.prototype._finalDecrypt = function _finalDecrypt() {  assert.equal(this.bufferOff, this.blockSize, 'Not enough data to decrypt');  var out = new Array(this.blockSize);  this._flushBuffer(out, 0);  return this._unpad(out);};
 |