| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 | /** * 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 algorithmsforge.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;};
 |