/** * Title: RXCipher * Description: Simple and relatively quick stream cipher algorithm - JavaScript implementation * Author: Jonathan Voss * Date: 8/7/2012 (v1.0); 12/16/2014 (v2.0); 11/19/2015 (v3.0) * Version: 3.2 * Namespace: github.com/k98kurz * License: MIT * * Basic byte-level algorithm * t = plaintext byte, c = ciphertext byte, k = encryption keystream byte * function enc (t, k) { return (t+k>255 ? t+k-256 : t+k)^k; } * function dec (c, k) { return (c^k)-k<0 ? (c^k)-k+256 : (c^k)-k; } * * * Class implementation * Methods: * encrypt ( plaintext ) * encrypts plaintext with key stream derived from skey * decrypt ( ciphertext) * decrypts ciphertext with key stream derived from skey * getKeyStream () * returns a KeyStream for PRNG generation * reset () * resets the internal KeyStream * * Changes: * 2.0 (12/16/2014): added pseudo-random key stream; dropped automatic hex conversion of ciphertext * 2.1 (12/29/2014): changed default seed value for XORShiftPlus PRNG * 2.2 (11/18/2015): converts to/from UTF-8 encoding; new keystream uses multiple csprngs and XORs the key with csprng values * 2.3 (11/18/2015): added explicit IV support; made base RXEncrypt & RXDecrypt use a Uint8 byte * 3.0 (11/19/2015): changed KeyStream to use an internally mixed 64 byte state; improved IV handling; improved performance * 3.1 (02/01/2016): updated KeyStream to use improved/fixed XORShiftPlus class; encrypt/decrypt use hex; encryptRaw/decryptRaw use Uint8Array * 3.2 (03/08/2016): bug fix: getKeyStream now returns actual KeyStream; reset method returns this */ function RXCipher (key, iv) { var skey, siv = iv, stream; skey = (typeof key == 'object' && key instanceof Uint8Array) ? fromUTF8Array(key) : key; siv = (typeof iv == 'object' && iv instanceof Uint8Array) ? fromUTF8Array(iv) : iv; stream = new KeyStream(skey, siv); function encrypt (plaintext) { var ciphertext = new Uint8Array(plaintext.length), g, t, k, key; key = stream.get(plaintext.length); for (var i = 0, ij = plaintext.length; i < ij; ++i) { ciphertext[i] = plaintext[i] ^ key[i]; } return ciphertext; } function decrypt (ciphertext) { var plaintext = new Uint8Array(ciphertext.length), g, c, k, key; key = stream.get(ciphertext.length); for (var i = 0, ij = ciphertext.length; i < ij; ++i) { plaintext[i] = ciphertext[i] ^ key[i]; } return plaintext; } this.encrypt = function (plaintext) { var ciphertext = encrypt(toUTF8Array(plaintext)); return toHex(ciphertext); }; this.decrypt = function (ciphertext) { var plaintext = decrypt(fromHex(ciphertext)); return fromUTF8Array(plaintext); }; this.encryptRaw = function (plaintext) { return encrypt(toUTF8Array(plaintext)); }; this.decryptRaw = function (ciphertext) { return fromUTF8Array(decrypt(ciphertext)); }; this.getKeyStream = function () { return new KeyStream(skey, siv); }; this.reset = function () { stream = new KeyStream (skey, siv); return this; }; function RXEncrypt (t, k, workingByte) { workingByte[0] = (t+k); return workingByte[0]^k; } function RXDecrypt (c, k, workingByte) { workingByte[0] = (c^k) - k; return workingByte[0]; } /** * Class: XORShiftPlus: "non-linear" PRNG * Description: Based upon the xorshift128+ generator, one of the fastest generators passing BigCrush. * Methods: * next(n) returns Uint32Array of n random numbers * nextHex(n) returns Array of n hexidecimal numbers * bytes(n) returns Uint8Array of n random bytes * bytesHex(n) returns Array of n random bytes in hexidecimal * hex(n) returns hexidecimal string of n bytes * Note: This generates with higher periodicity and better random distribution than the linear XORShift */ function XORShiftPlus (seed) { var x = new Uint32Array(1), y = new Uint32Array(1), z = new Uint32Array(1), w = new Uint32Array(1); x[0] = seed ? seed|0 : 317973455; y[0] = x[0]<<362436069; z[0] = y[0]+x[0]; w[0] = z[0]^x[0]+y[0]; function next () { var t = new Uint32Array(1); t[0] = x[0]^(x<<11); x[0] = y[0]; y[0] = z[0]; z[0] = w[0]; w[0] = w[0]^(w[0]>>19)^(t[0]^(t[0]>>8)); return w[0]+y[0]; } // discard the first numbers as they are somewhat predictable for (var i=0, n=next(), j=next()%256; i<=j; next(), ++i); this.next = function (n, option) { if (typeof n == 'undefined' || typeof n !== 'number') var n = 1; var t; n |= 0; n = n>0 ? n : 1; if (typeof option == 'undefined' || typeof n !== 'number') var option = 0; // redundant code, but improved performance switch (option) { // nextHex case 1: t = []; for (var i=0, il=n; i= 0; --i, ++j) { j = (j == keyLength) ? 0 : j; t = (csprngs[j].next()[0]+key[j])%64; m = state[t]; state[t] = RXEncrypt(state[i], key[j], workingByte); state[i] = RXDecrypt(m, t, workingByte); } function next () { keyIndex = (++keyIndex == keyLength) ? 0 : keyIndex; stateIndex = (++stateIndex == 64) ? 0 : stateIndex; state[stateIndex] = RXEncrypt(state[stateIndex], key[keyIndex], workingByte); state[63 - stateIndex] = RXEncrypt(state[63 - stateIndex], state[stateIndex], workingByte); return state[stateIndex]; } this.get = function (nBytes) { nBytes = (nBytes|0 ? nBytes|0 : (parseInt(nBytes) ? parseInt(nBytes) : 1)); var k = new Uint8Array(nBytes); for (var o=0; o> 6), 0x80 | (charcode & 0x3f)); } else if (charcode < 0xd800 || charcode >= 0xe000) { utf8.push(0xe0 | (charcode >> 12), 0x80 | ((charcode>>6) & 0x3f), 0x80 | (charcode & 0x3f)); } else { // surrogate pair i++; // UTF-16 encodes 0x10000-0x10FFFF by // subtracting 0x10000 and splitting the // 20 bits of 0x0-0xFFFFF into two halves charcode = 0x10000 + (((charcode & 0x3ff)<<10) | (str.charCodeAt(i) & 0x3ff)); utf8.push(0xf0 | (charcode >>18), 0x80 | ((charcode>>12) & 0x3f), 0x80 | ((charcode>>6) & 0x3f), 0x80 | (charcode & 0x3f)); } } return new Uint8Array(utf8); } // convert from UTF-8 to JavaScript's internal UTF-16 strings // ripped from https://github.com/coolaj86/TextEncoderLite // credit: AJ ONeal (coolaj86) && Feross Aboukhadijeh (feross) function fromUTF8Array (arr) { var utf16 = '', tmp = ''; for (var i = 0, ij = arr.length; i < ij; i++) { if (arr[i] <= 0x7F) { utf16 += decodeUtf8Char(tmp) + String.fromCharCode(arr[i]); tmp = ''; } else { tmp += '%' + arr[i].toString(16); } }; return utf16 + decodeUtf8Char(tmp); } function decodeUtf8Char (str) { try { return decodeURIComponent(str); } catch (err) { return String.fromCharCode(0xFFFD); // UTF 8 invalid char } } function toHex (raw) { var t = []; for (var i=0, il=raw.length; i