UNPKG

rc4

Version:

RC4 random number generator

205 lines (162 loc) 4.63 kB
"use strict"; // Based on RC4 algorithm, as described in // http://en.wikipedia.org/wiki/RC4 function isInteger(n) { return parseInt(n, 10) === n; } function createRC4(N) { function identityPermutation() { var s = new Array(N); for (var i = 0; i < N; i++) { s[i] = i; } return s; } // :: string | array integer -> array integer function seed(key) { if (key === undefined) { key = new Array(N); for (var k = 0; k < N; k++) { key[k] = Math.floor(Math.random() * N); } } else if (typeof key === "string") { // to string key = "" + key; key = key.split("").map(function (c) { return c.charCodeAt(0) % N; }); } else if (Array.isArray(key)) { if (!key.every(function (v) { return typeof v === "number" && v === (v | 0); })) { throw new TypeError("invalid seed key specified: not array of integers"); } } else { throw new TypeError("invalid seed key specified"); } var keylen = key.length; // resed state var s = identityPermutation(); var j = 0; for (var i = 0; i < N; i++) { j = (j + s[i] + key[i % keylen]) % N; var tmp = s[i]; s[i] = s[j]; s[j] = tmp; } return s; } /* eslint-disable no-shadow */ function RC4(key) { this.s = seed(key); this.i = 0; this.j = 0; } /* eslint-enable no-shadow */ RC4.prototype.randomNative = function () { this.i = (this.i + 1) % N; this.j = (this.j + this.s[this.i]) % N; var tmp = this.s[this.i]; this.s[this.i] = this.s[this.j]; this.s[this.j] = tmp; var k = this.s[(this.s[this.i] + this.s[this.j]) % N]; return k; }; RC4.prototype.randomUInt32 = function () { var a = this.randomByte(); var b = this.randomByte(); var c = this.randomByte(); var d = this.randomByte(); return ((a * 256 + b) * 256 + c) * 256 + d; }; RC4.prototype.randomFloat = function () { return this.randomUInt32() / 0x100000000; }; RC4.prototype.random = function () { var a; var b; if (arguments.length === 1) { a = 0; b = arguments[0]; } else if (arguments.length === 2) { a = arguments[0]; b = arguments[1]; } else { throw new TypeError("random takes one or two integer arguments"); } if (!isInteger(a) || !isInteger(b)) { throw new TypeError("random takes one or two integer arguments"); } return a + this.randomUInt32() % (b - a + 1); }; RC4.prototype.currentState = function () { return { i: this.i, j: this.j, s: this.s.slice(), // copy }; }; RC4.prototype.setState = function (state) { var s = state.s; var i = state.i; var j = state.j; /* eslint-disable yoda */ if (!(i === (i | 0) && 0 <= i && i < N)) { throw new Error("state.i should be integer [0, " + (N - 1) + "]"); } if (!(j === (j | 0) && 0 <= j && j < N)) { throw new Error("state.j should be integer [0, " + (N - 1) + "]"); } /* eslint-enable yoda */ // check length if (!Array.isArray(s) || s.length !== N) { throw new Error("state should be array of length " + N); } // check that all params are there for (var k = 0; k < N; k++) { if (s.indexOf(k) === -1) { throw new Error("state should be permutation of 0.." + (N - 1) + ": " + k + " is missing"); } } this.i = i; this.j = j; this.s = s.slice(); // assign copy }; return RC4; } var RC4 = createRC4(256); RC4.prototype.randomByte = RC4.prototype.randomNative; var RC4small = createRC4(16); RC4small.prototype.randomByte = function () { var a = this.randomNative(); var b = this.randomNative(); return a * 16 + b; }; var ordA = "a".charCodeAt(0); var ord0 = "0".charCodeAt(0); function toHex(n) { return n < 10 ? String.fromCharCode(ord0 + n) : String.fromCharCode(ordA + n - 10); } function fromHex(c) { return parseInt(c, 16); } RC4small.prototype.currentStateString = function () { var state = this.currentState(); var i = toHex(state.i); var j = toHex(state.j); var res = i + j + state.s.map(toHex).join(""); return res; }; RC4small.prototype.setStateString = function (stateString) { if (!stateString.match(/^[0-9a-f]{18}$/)) { throw new TypeError("RC4small stateString should be 18 hex character string"); } var i = fromHex(stateString[0]); var j = fromHex(stateString[1]); var s = stateString.split("").slice(2).map(fromHex); this.setState({ i: i, j: j, s: s, }); }; RC4.RC4small = RC4small; module.exports = RC4;