cipher.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. 'use strict';
  2. var assert = require('minimalistic-assert');
  3. function Cipher(options) {
  4. this.options = options;
  5. this.type = this.options.type;
  6. this.blockSize = 8;
  7. this._init();
  8. this.buffer = new Array(this.blockSize);
  9. this.bufferOff = 0;
  10. }
  11. module.exports = Cipher;
  12. Cipher.prototype._init = function _init() {
  13. // Might be overrided
  14. };
  15. Cipher.prototype.update = function update(data) {
  16. if (data.length === 0)
  17. return [];
  18. if (this.type === 'decrypt')
  19. return this._updateDecrypt(data);
  20. else
  21. return this._updateEncrypt(data);
  22. };
  23. Cipher.prototype._buffer = function _buffer(data, off) {
  24. // Append data to buffer
  25. var min = Math.min(this.buffer.length - this.bufferOff, data.length - off);
  26. for (var i = 0; i < min; i++)
  27. this.buffer[this.bufferOff + i] = data[off + i];
  28. this.bufferOff += min;
  29. // Shift next
  30. return min;
  31. };
  32. Cipher.prototype._flushBuffer = function _flushBuffer(out, off) {
  33. this._update(this.buffer, 0, out, off);
  34. this.bufferOff = 0;
  35. return this.blockSize;
  36. };
  37. Cipher.prototype._updateEncrypt = function _updateEncrypt(data) {
  38. var inputOff = 0;
  39. var outputOff = 0;
  40. var count = ((this.bufferOff + data.length) / this.blockSize) | 0;
  41. var out = new Array(count * this.blockSize);
  42. if (this.bufferOff !== 0) {
  43. inputOff += this._buffer(data, inputOff);
  44. if (this.bufferOff === this.buffer.length)
  45. outputOff += this._flushBuffer(out, outputOff);
  46. }
  47. // Write blocks
  48. var max = data.length - ((data.length - inputOff) % this.blockSize);
  49. for (; inputOff < max; inputOff += this.blockSize) {
  50. this._update(data, inputOff, out, outputOff);
  51. outputOff += this.blockSize;
  52. }
  53. // Queue rest
  54. for (; inputOff < data.length; inputOff++, this.bufferOff++)
  55. this.buffer[this.bufferOff] = data[inputOff];
  56. return out;
  57. };
  58. Cipher.prototype._updateDecrypt = function _updateDecrypt(data) {
  59. var inputOff = 0;
  60. var outputOff = 0;
  61. var count = Math.ceil((this.bufferOff + data.length) / this.blockSize) - 1;
  62. var out = new Array(count * this.blockSize);
  63. // TODO(indutny): optimize it, this is far from optimal
  64. for (; count > 0; count--) {
  65. inputOff += this._buffer(data, inputOff);
  66. outputOff += this._flushBuffer(out, outputOff);
  67. }
  68. // Buffer rest of the input
  69. inputOff += this._buffer(data, inputOff);
  70. return out;
  71. };
  72. Cipher.prototype.final = function final(buffer) {
  73. var first;
  74. if (buffer)
  75. first = this.update(buffer);
  76. var last;
  77. if (this.type === 'encrypt')
  78. last = this._finalEncrypt();
  79. else
  80. last = this._finalDecrypt();
  81. if (first)
  82. return first.concat(last);
  83. else
  84. return last;
  85. };
  86. Cipher.prototype._pad = function _pad(buffer, off) {
  87. if (off === 0)
  88. return false;
  89. while (off < buffer.length)
  90. buffer[off++] = 0;
  91. return true;
  92. };
  93. Cipher.prototype._finalEncrypt = function _finalEncrypt() {
  94. if (!this._pad(this.buffer, this.bufferOff))
  95. return [];
  96. var out = new Array(this.blockSize);
  97. this._update(this.buffer, 0, out, 0);
  98. return out;
  99. };
  100. Cipher.prototype._unpad = function _unpad(buffer) {
  101. return buffer;
  102. };
  103. Cipher.prototype._finalDecrypt = function _finalDecrypt() {
  104. assert.equal(this.bufferOff, this.blockSize, 'Not enough data to decrypt');
  105. var out = new Array(this.blockSize);
  106. this._flushBuffer(out, 0);
  107. return this._unpad(out);
  108. };