rc4
Version:
RC4 random number generator
205 lines (162 loc) • 4.63 kB
JavaScript
"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;