| // Copyright 2005 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| /** |
| * @fileoverview ARC4 streamcipher implementation. A description of the |
| * algorithm can be found at: |
| * http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt. |
| * |
| * Usage: |
| * <code> |
| * var arc4 = new goog.crypt.Arc4(); |
| * arc4.setKey(key); |
| * arc4.discard(1536); |
| * arc4.crypt(bytes); |
| * </code> |
| * |
| * Note: For converting between strings and byte arrays, goog.crypt.base64 may |
| * be useful. |
| * |
| */ |
| |
| goog.provide('goog.crypt.Arc4'); |
| |
| goog.require('goog.asserts'); |
| |
| |
| |
| /** |
| * ARC4 streamcipher implementation. |
| * @constructor |
| * @final |
| * @struct |
| */ |
| goog.crypt.Arc4 = function() { |
| /** |
| * A permutation of all 256 possible bytes. |
| * @type {Array<number>} |
| * @private |
| */ |
| this.state_ = []; |
| |
| /** |
| * 8 bit index pointer into this.state_. |
| * @type {number} |
| * @private |
| */ |
| this.index1_ = 0; |
| |
| /** |
| * 8 bit index pointer into this.state_. |
| * @type {number} |
| * @private |
| */ |
| this.index2_ = 0; |
| }; |
| |
| |
| /** |
| * Initialize the cipher for use with new key. |
| * @param {Array<number>} key A byte array containing the key. |
| * @param {number=} opt_length Indicates # of bytes to take from the key. |
| */ |
| goog.crypt.Arc4.prototype.setKey = function(key, opt_length) { |
| goog.asserts.assertArray(key, 'Key parameter must be a byte array'); |
| |
| if (!opt_length) { |
| opt_length = key.length; |
| } |
| |
| var state = this.state_; |
| |
| for (var i = 0; i < 256; ++i) { |
| state[i] = i; |
| } |
| |
| var j = 0; |
| for (var i = 0; i < 256; ++i) { |
| j = (j + state[i] + key[i % opt_length]) & 255; |
| |
| var tmp = state[i]; |
| state[i] = state[j]; |
| state[j] = tmp; |
| } |
| |
| this.index1_ = 0; |
| this.index2_ = 0; |
| }; |
| |
| |
| /** |
| * Discards n bytes of the keystream. |
| * These days 1536 is considered a decent amount to drop to get the key state |
| * warmed-up enough for secure usage. This is not done in the constructor to |
| * preserve efficiency for use cases that do not need this. |
| * NOTE: Discard is identical to crypt without actually xoring any data. It's |
| * unfortunate to have this code duplicated, but this was done for performance |
| * reasons. Alternatives which were attempted: |
| * 1. Create a temp array of the correct length and pass it to crypt. This |
| * works but needlessly allocates an array. But more importantly this |
| * requires choosing an array type (Array or Uint8Array) in discard, and |
| * choosing a different type than will be passed to crypt by the client |
| * code hurts the javascript engines ability to optimize crypt (7x hit in |
| * v8). |
| * 2. Make data option in crypt so discard can pass null, this has a huge |
| * perf hit for crypt. |
| * @param {number} length Number of bytes to disregard from the stream. |
| */ |
| goog.crypt.Arc4.prototype.discard = function(length) { |
| var i = this.index1_; |
| var j = this.index2_; |
| var state = this.state_; |
| |
| for (var n = 0; n < length; ++n) { |
| i = (i + 1) & 255; |
| j = (j + state[i]) & 255; |
| |
| var tmp = state[i]; |
| state[i] = state[j]; |
| state[j] = tmp; |
| } |
| |
| this.index1_ = i; |
| this.index2_ = j; |
| }; |
| |
| |
| /** |
| * En- or decrypt (same operation for streamciphers like ARC4) |
| * @param {Array<number>|Uint8Array} data The data to be xor-ed in place. |
| * @param {number=} opt_length The number of bytes to crypt. |
| */ |
| goog.crypt.Arc4.prototype.crypt = function(data, opt_length) { |
| if (!opt_length) { |
| opt_length = data.length; |
| } |
| var i = this.index1_; |
| var j = this.index2_; |
| var state = this.state_; |
| |
| for (var n = 0; n < opt_length; ++n) { |
| i = (i + 1) & 255; |
| j = (j + state[i]) & 255; |
| |
| var tmp = state[i]; |
| state[i] = state[j]; |
| state[j] = tmp; |
| |
| data[n] ^= state[(state[i] + state[j]) & 255]; |
| } |
| |
| this.index1_ = i; |
| this.index2_ = j; |
| }; |