You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							230 lines
						
					
					
						
							6.5 KiB
						
					
					
				
			
		
		
	
	
							230 lines
						
					
					
						
							6.5 KiB
						
					
					
				/** | 
						|
 * Cipher base API. | 
						|
 * | 
						|
 * @author Dave Longley | 
						|
 * | 
						|
 * Copyright (c) 2010-2014 Digital Bazaar, Inc. | 
						|
 */ | 
						|
var forge = require('./forge'); | 
						|
require('./util'); | 
						|
 | 
						|
module.exports = forge.cipher = forge.cipher || {}; | 
						|
 | 
						|
// registered algorithms | 
						|
forge.cipher.algorithms = forge.cipher.algorithms || {}; | 
						|
 | 
						|
/** | 
						|
 * Creates a cipher object that can be used to encrypt data using the given | 
						|
 * algorithm and key. The algorithm may be provided as a string value for a | 
						|
 * previously registered algorithm or it may be given as a cipher algorithm | 
						|
 * API object. | 
						|
 * | 
						|
 * @param algorithm the algorithm to use, either a string or an algorithm API | 
						|
 *          object. | 
						|
 * @param key the key to use, as a binary-encoded string of bytes or a | 
						|
 *          byte buffer. | 
						|
 * | 
						|
 * @return the cipher. | 
						|
 */ | 
						|
forge.cipher.createCipher = function(algorithm, key) { | 
						|
  var api = algorithm; | 
						|
  if(typeof api === 'string') { | 
						|
    api = forge.cipher.getAlgorithm(api); | 
						|
    if(api) { | 
						|
      api = api(); | 
						|
    } | 
						|
  } | 
						|
  if(!api) { | 
						|
    throw new Error('Unsupported algorithm: ' + algorithm); | 
						|
  } | 
						|
 | 
						|
  // assume block cipher | 
						|
  return new forge.cipher.BlockCipher({ | 
						|
    algorithm: api, | 
						|
    key: key, | 
						|
    decrypt: false | 
						|
  }); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Creates a decipher object that can be used to decrypt data using the given | 
						|
 * algorithm and key. The algorithm may be provided as a string value for a | 
						|
 * previously registered algorithm or it may be given as a cipher algorithm | 
						|
 * API object. | 
						|
 * | 
						|
 * @param algorithm the algorithm to use, either a string or an algorithm API | 
						|
 *          object. | 
						|
 * @param key the key to use, as a binary-encoded string of bytes or a | 
						|
 *          byte buffer. | 
						|
 * | 
						|
 * @return the cipher. | 
						|
 */ | 
						|
forge.cipher.createDecipher = function(algorithm, key) { | 
						|
  var api = algorithm; | 
						|
  if(typeof api === 'string') { | 
						|
    api = forge.cipher.getAlgorithm(api); | 
						|
    if(api) { | 
						|
      api = api(); | 
						|
    } | 
						|
  } | 
						|
  if(!api) { | 
						|
    throw new Error('Unsupported algorithm: ' + algorithm); | 
						|
  } | 
						|
 | 
						|
  // assume block cipher | 
						|
  return new forge.cipher.BlockCipher({ | 
						|
    algorithm: api, | 
						|
    key: key, | 
						|
    decrypt: true | 
						|
  }); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Registers an algorithm by name. If the name was already registered, the | 
						|
 * algorithm API object will be overwritten. | 
						|
 * | 
						|
 * @param name the name of the algorithm. | 
						|
 * @param algorithm the algorithm API object. | 
						|
 */ | 
						|
forge.cipher.registerAlgorithm = function(name, algorithm) { | 
						|
  name = name.toUpperCase(); | 
						|
  forge.cipher.algorithms[name] = algorithm; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Gets a registered algorithm by name. | 
						|
 * | 
						|
 * @param name the name of the algorithm. | 
						|
 * | 
						|
 * @return the algorithm, if found, null if not. | 
						|
 */ | 
						|
forge.cipher.getAlgorithm = function(name) { | 
						|
  name = name.toUpperCase(); | 
						|
  if(name in forge.cipher.algorithms) { | 
						|
    return forge.cipher.algorithms[name]; | 
						|
  } | 
						|
  return null; | 
						|
}; | 
						|
 | 
						|
var BlockCipher = forge.cipher.BlockCipher = function(options) { | 
						|
  this.algorithm = options.algorithm; | 
						|
  this.mode = this.algorithm.mode; | 
						|
  this.blockSize = this.mode.blockSize; | 
						|
  this._finish = false; | 
						|
  this._input = null; | 
						|
  this.output = null; | 
						|
  this._op = options.decrypt ? this.mode.decrypt : this.mode.encrypt; | 
						|
  this._decrypt = options.decrypt; | 
						|
  this.algorithm.initialize(options); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Starts or restarts the encryption or decryption process, whichever | 
						|
 * was previously configured. | 
						|
 * | 
						|
 * For non-GCM mode, the IV may be a binary-encoded string of bytes, an array | 
						|
 * of bytes, a byte buffer, or an array of 32-bit integers. If the IV is in | 
						|
 * bytes, then it must be Nb (16) bytes in length. If the IV is given in as | 
						|
 * 32-bit integers, then it must be 4 integers long. | 
						|
 * | 
						|
 * Note: an IV is not required or used in ECB mode. | 
						|
 * | 
						|
 * For GCM-mode, the IV must be given as a binary-encoded string of bytes or | 
						|
 * a byte buffer. The number of bytes should be 12 (96 bits) as recommended | 
						|
 * by NIST SP-800-38D but another length may be given. | 
						|
 * | 
						|
 * @param options the options to use: | 
						|
 *          iv the initialization vector to use as a binary-encoded string of | 
						|
 *            bytes, null to reuse the last ciphered block from a previous | 
						|
 *            update() (this "residue" method is for legacy support only). | 
						|
 *          additionalData additional authentication data as a binary-encoded | 
						|
 *            string of bytes, for 'GCM' mode, (default: none). | 
						|
 *          tagLength desired length of authentication tag, in bits, for | 
						|
 *            'GCM' mode (0-128, default: 128). | 
						|
 *          tag the authentication tag to check if decrypting, as a | 
						|
 *             binary-encoded string of bytes. | 
						|
 *          output the output the buffer to write to, null to create one. | 
						|
 */ | 
						|
BlockCipher.prototype.start = function(options) { | 
						|
  options = options || {}; | 
						|
  var opts = {}; | 
						|
  for(var key in options) { | 
						|
    opts[key] = options[key]; | 
						|
  } | 
						|
  opts.decrypt = this._decrypt; | 
						|
  this._finish = false; | 
						|
  this._input = forge.util.createBuffer(); | 
						|
  this.output = options.output || forge.util.createBuffer(); | 
						|
  this.mode.start(opts); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Updates the next block according to the cipher mode. | 
						|
 * | 
						|
 * @param input the buffer to read from. | 
						|
 */ | 
						|
BlockCipher.prototype.update = function(input) { | 
						|
  if(input) { | 
						|
    // input given, so empty it into the input buffer | 
						|
    this._input.putBuffer(input); | 
						|
  } | 
						|
 | 
						|
  // do cipher operation until it needs more input and not finished | 
						|
  while(!this._op.call(this.mode, this._input, this.output, this._finish) && | 
						|
    !this._finish) {} | 
						|
 | 
						|
  // free consumed memory from input buffer | 
						|
  this._input.compact(); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Finishes encrypting or decrypting. | 
						|
 * | 
						|
 * @param pad a padding function to use in CBC mode, null for default, | 
						|
 *          signature(blockSize, buffer, decrypt). | 
						|
 * | 
						|
 * @return true if successful, false on error. | 
						|
 */ | 
						|
BlockCipher.prototype.finish = function(pad) { | 
						|
  // backwards-compatibility w/deprecated padding API | 
						|
  // Note: will overwrite padding functions even after another start() call | 
						|
  if(pad && (this.mode.name === 'ECB' || this.mode.name === 'CBC')) { | 
						|
    this.mode.pad = function(input) { | 
						|
      return pad(this.blockSize, input, false); | 
						|
    }; | 
						|
    this.mode.unpad = function(output) { | 
						|
      return pad(this.blockSize, output, true); | 
						|
    }; | 
						|
  } | 
						|
 | 
						|
  // build options for padding and afterFinish functions | 
						|
  var options = {}; | 
						|
  options.decrypt = this._decrypt; | 
						|
 | 
						|
  // get # of bytes that won't fill a block | 
						|
  options.overflow = this._input.length() % this.blockSize; | 
						|
 | 
						|
  if(!this._decrypt && this.mode.pad) { | 
						|
    if(!this.mode.pad(this._input, options)) { | 
						|
      return false; | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  // do final update | 
						|
  this._finish = true; | 
						|
  this.update(); | 
						|
 | 
						|
  if(this._decrypt && this.mode.unpad) { | 
						|
    if(!this.mode.unpad(this.output, options)) { | 
						|
      return false; | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  if(this.mode.afterFinish) { | 
						|
    if(!this.mode.afterFinish(this.output, options)) { | 
						|
      return false; | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return true; | 
						|
};
 | 
						|
 |