142 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| '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;
 | |
| }
 | |
| 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);
 | |
| };
 |