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