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