View difference between Paste ID: EdSbT7WN and tkfNM030
SHOW: | | - or go back to the newest paste.
1
/**
2-
 * Title:		RXCipher
2+
 *  XORShiftPlus "non-linear" PRNG
3-
 * Description:	Simple and relatively quick stream cipher algorithm - JavaScript implementation
3+
 *  Author: k98kurz (@gmail)
4-
 * Author:		Jonathan Voss
4+
 *  License: MIT
5-
 * Date:		8/7/2012 (v1.0); 12/16/2014 (v2.0); 11/19/2015 (v3.0)
5+
 *  Date: 2020-09-07
6-
 * Version:		3.2
6+
 *  Description: Based upon the xorshift128+ generator, one of the fastest generators passing BigCrush.
7-
 * Namespace:	github.com/k98kurz
7+
 *  Methods:
8-
 * License:		MIT
8+
 *      next(n)         returns Uint32Array of n random numbers
9
 *      nextHex(n)      returns Array of n hexidecimal numbers
10
 *      bytes(n)        returns Uint8Array of n random bytes
11
 *      bytesHex(n)     returns Array of n random bytes in hexidecimal
12-
 *		function enc (t, k) { return (t+k>255 ? t+k-256 : t+k)^k; }
12+
 *      hex(n)          returns hexidecimal string of n bytes
13-
 *		function dec (c, k) { return (c^k)-k<0 ? (c^k)-k+256 : (c^k)-k; }
13+
 *  Note: This generates with higher periodicity and better random distribution than the linear XORShift.
14
 */
15
class XORShiftPlus {
16
  #booted = false;
17-
 * Methods:
17+
  #x;
18-
 *		encrypt ( plaintext )
18+
  #y;
19-
 *			encrypts plaintext with key stream derived from skey
19+
  #z;
20-
 *		decrypt ( ciphertext)
20+
  #w;
21-
 *			decrypts ciphertext with key stream derived from skey
21+
22-
 *		getKeyStream ()
22+
  constructor (seed) {
23-
 *			returns a KeyStream for PRNG generation
23+
    this.#x = new Uint32Array(1);
24-
 *		reset ()
24+
    this.#y = new Uint32Array(1);
25-
 *			resets the internal KeyStream
25+
    this.#z = new Uint32Array(1);
26
    this.#w = new Uint32Array(1);
27
    this.#x[0] = seed ? seed|0 : 317973455;
28-
 *	2.0 (12/16/2014): added pseudo-random key stream; dropped automatic hex conversion of ciphertext
28+
    this.#y[0] = this.#x[0]<<362436069;
29-
 *	2.1 (12/29/2014): changed default seed value for XORShiftPlus PRNG
29+
    this.#z[0] = this.#y[0]+this.#x[0];
30-
 *	2.2 (11/18/2015): converts to/from UTF-8 encoding; new keystream uses multiple csprngs and XORs the key with csprng values
30+
    this.#w[0] = this.#z[0]^this.#x[0]+this.#y[0];
31-
 *	2.3 (11/18/2015): added explicit IV support; made base RXEncrypt & RXDecrypt use a Uint8 byte
31+
  }
32-
 *	3.0 (11/19/2015): changed KeyStream to use an internally mixed 64 byte state; improved IV handling; improved performance
32+
33-
 *	3.1 (02/01/2016): updated KeyStream to use improved/fixed XORShiftPlus class; encrypt/decrypt use hex; encryptRaw/decryptRaw use Uint8Array
33+
  #next () {
34-
 *	3.2 (03/08/2016): bug fix: getKeyStream now returns actual KeyStream; reset method returns this
34+
    let t = new Uint32Array(1);
35
    t[0] = this.#x[0]^(this.#x[0]<<11);
36
    this.#x[0] = this.#y[0];
37-
function RXCipher (key, iv) {
37+
    this.#y[0] = this.#z[0];
38-
	var skey, siv = iv, stream;
38+
    this.#z[0] = this.#w[0];
39-
	skey = (typeof key == 'object' && key instanceof Uint8Array) ? fromUTF8Array(key) : key;
39+
    this.#w[0] = this.#w[0]^(this.#w[0]>>19)^(t[0]^(t[0]>>8));
40-
	siv = (typeof iv == 'object' && iv instanceof Uint8Array) ? fromUTF8Array(iv) : iv;
40+
    return this.#w[0]+this.#y[0];
41-
	stream = new KeyStream(skey, siv);
41+
  }
42
43-
	function encrypt (plaintext) {
43+
  #boot () {
44-
		var ciphertext = new Uint8Array(plaintext.length), g, t, k, key;
44+
    // if not booted, discard the first numbers as they are somewhat predictable
45-
		key = stream.get(plaintext.length);
45+
    if (this.#booted)
46-
		for (var i = 0, ij = plaintext.length; i < ij; ++i) {
46+
      return;
47-
			ciphertext[i] = plaintext[i] ^ key[i];
47+
    for (let i=0, n=this.#next(), j=this.#next()%256; i<=j; this.#next(), ++i);
48-
		}
48+
    this.#booted = true;
49-
		return ciphertext;
49+
  }
50-
	}
50+
51
  next (n, option) {
52-
	function decrypt (ciphertext) {
52+
    this.#boot();
53-
		var plaintext = new Uint8Array(ciphertext.length), g, c, k, key;
53+
    n = (n === undefined || typeof n !== 'number') ? 1 : n;
54-
		key = stream.get(ciphertext.length);
54+
    let t; n |= 0; n = n>0 ? n : 1;
55-
		for (var i = 0, ij = ciphertext.length; i < ij; ++i) {
55+
    option = (option === undefined || typeof n !== 'number') ? 0 : option;
56-
			plaintext[i] = ciphertext[i] ^ key[i];
56+
57-
		}
57+
    // redundant code, but improved performance
58-
		return plaintext;
58+
    switch (option) {
59-
	}
59+
      // nextHex
60
      case 1:
61-
	this.encrypt = function (plaintext) {
61+
        t = [];
62-
		var ciphertext = encrypt(toUTF8Array(plaintext));
62+
        for (let i=0, il=n; i<il; t[i++] = this.#next().toString(16));
63-
		return toHex(ciphertext);
63+
        break;
64-
	};
64+
      // bytes
65
      case 2:
66-
	this.decrypt = function (ciphertext) {
66+
        t = new Uint8Array(n);
67-
		var plaintext = decrypt(fromHex(ciphertext));
67+
        for (let i=0, il=n; i<il; t[i++] = this.#next());
68-
		return fromUTF8Array(plaintext);
68+
        break;
69-
	};
69+
      // bytesHex
70
      case 3:
71-
	this.encryptRaw = function (plaintext) {
71+
        t = [];
72-
		return encrypt(toUTF8Array(plaintext));
72+
        for (let i=0, il=n; i<il; t[i] = (this.#next()%256).toString(16), t[i] = t[i].length%2 ? '0'+t[i] : t[i], ++i);
73-
	};
73+
        break;
74
      // hex
75-
	this.decryptRaw = function (ciphertext) {
75+
      case 4:
76-
		return fromUTF8Array(decrypt(ciphertext));
76+
        t = [];
77-
	};
77+
        for (let i=0, il=n; i<il; t[i] = (this.#next()%256).toString(16), t[i] = t[i].length%2 ? '0'+t[i] : t[i], ++i);
78
        t = t.join('');
79-
	this.getKeyStream = function () {
79+
        break;
80-
		return new KeyStream(skey, siv);
80+
      // next
81-
	};
81+
      default:
82
        t = new Uint32Array(n);
83-
	this.reset = function () {
83+
        for (let i=0, il=n; i<il; t[i++] = this.#next());
84-
		stream = new KeyStream (skey, siv);
84+
    }
85-
		return this;
85+
    return t;
86-
	};
86+
  };
87
88-
	function RXEncrypt (t, k, workingByte) {
88+
  nextHex (n) { return this.next(n, 1); };
89-
		workingByte[0] = (t+k);
89+
90-
		return workingByte[0]^k;
90+
  bytes (n) { return this.next(n, 2); };
91-
	}
91+
92
  bytesHex (n) { return this.next(n, 3); };
93-
	function RXDecrypt (c, k, workingByte) {
93+
94-
		workingByte[0] = (c^k) - k;
94+
  hex (n) { return this.next(n, 4); };
95-
		return workingByte[0];
95+
}
96-
	}
96+
97
/**
98-
	/**
98+
 *  KeyStream
99-
	 *  Class: XORShiftPlus: "non-linear" PRNG
99+
 *  Author: k98kurz
100-
	 *  Description: Based upon the xorshift128+ generator, one of the fastest generators passing BigCrush.
100+
 *  License: MIT
101-
	 *	Methods:
101+
 *  Date: 2020-09-07
102-
	 *		next(n)			returns Uint32Array of n random numbers
102+
 *  Description: Takes a key and iv, mixes them, and uses the result to seed several XORShiftPlus PRNGs.
103-
	 *		nextHex(n)		returns Array of n hexidecimal numbers
103+
 *  Dependency: XORShiftPlus
104-
	 *		bytes(n)		returns Uint8Array of n random bytes
104+
 *  Methods:
105-
	 *		bytesHex(n)		returns Array of n random bytes in hexidecimal
105+
106-
	 *		hex(n)			returns hexidecimal string of n bytes
106+
 */
107-
	 *	Note: This generates with higher periodicity and better random distribution than the linear XORShift
107+
class KeyStream {
108-
	 */
108+
  #csprngs;
109-
	function XORShiftPlus (seed) {
109+
  #state;
110-
		var x = new Uint32Array(1), y = new Uint32Array(1), z = new Uint32Array(1), w = new Uint32Array(1);
110+
  #keyLength;
111-
		x[0] = seed ? seed|0 : 317973455;
111+
  #keyIndex;
112-
		y[0] = x[0]<<362436069;
112+
  #stateIndex;
113-
		z[0] = y[0]+x[0];
113+
  #workingByte = new Uint8Array(1);
114-
		w[0] = z[0]^x[0]+y[0];
114+
  #key;
115
  #iv;
116-
		function next () {
116+
117-
			var t = new Uint32Array(1);
117+
  constructor (key, iv) {
118-
			t[0] = x[0]^(x<<11);
118+
    if (typeof key == 'undefined')
119-
			x[0] = y[0]; y[0] = z[0]; z[0] = w[0];
119+
      throw new Error('KeyStream: key required');
120-
			w[0] = w[0]^(w[0]>>19)^(t[0]^(t[0]>>8));
120+
    if (typeof key == 'object' && key instanceof Array)
121-
			return w[0]+y[0];
121+
      key = (typeof key[0] == 'number') ? new Uint8Array(key) : key.join('');
122-
		}
122+
    if (typeof key == 'string')
123
      key = KeyStream.toUTF8Array(key);
124-
		// discard the first numbers as they are somewhat predictable
124+
    if (typeof key !== 'object' && !(key instanceof Uint8Array))
125-
		for (var i=0, n=next(), j=next()%256; i<=j; next(), ++i);
125+
      throw new Error('KeyStream: expected key of type String, Array, or Uint8Array');
126
127-
		this.next = function (n, option) {
127+
    if (typeof iv == 'undefined')
128-
			if (typeof n == 'undefined' || typeof n !== 'number')
128+
      throw new Error('KeyStream: iv required');
129-
				var n = 1;
129+
    if (typeof iv == 'object' && iv instanceof Array)
130-
			var t; n |= 0; n = n>0 ? n : 1;
130+
      iv = (typeof iv[0] == 'number') ? new Uint8Array(iv) : iv.join('');
131-
			if (typeof option == 'undefined' || typeof n !== 'number')
131+
    if (typeof iv == 'string')
132-
				var option = 0;
132+
      iv = KeyStream.toUTF8Array(iv);
133
    if (!(iv instanceof Uint8Array) || iv.length == 0)
134-
			// redundant code, but improved performance
134+
      throw new Error('KeyStream: expected iv of type String, Array or Uint8Array');
135-
			switch (option) {
135+
136-
				// nextHex
136+
    // set the key and iv private properties
137-
				case 1:
137+
    this.#key = key;
138-
					t = [];
138+
    this.#iv = iv;
139-
					for (var i=0, il=n; i<il; t[i++] = next().toString(16));
139+
140-
					break;
140+
    // initialize some internal state
141-
				// bytes
141+
    this.#csprngs = []; this.#keyLength = this.#key.length; this.#keyIndex = 0; this.#state = new Uint8Array(64); this.#stateIndex = 0;
142-
				case 2:
142+
    for (let i = 0; i < this.#keyLength; i++) {
143-
					t = new Uint8Array(n);
143+
      this.#csprngs[i] = new XORShiftPlus(this.#key[i]);
144-
					for (var i=0, il=n; i<il; t[i++] = next());
144+
    }
145-
					break;
145+
    for (let i = 0, j = 0, c = 0; c < this.#keyLength * 4; ++i, ++j, ++c) {
146-
				// bytesHex
146+
      j = (j == this.#keyLength) ? 0 : j;
147-
				case 3:
147+
      i = (i == this.#iv.length) ? 0 : i;
148-
					t = [];
148+
      this.#key[j] = this.#key[j] + this.#iv[i];
149-
					for (var i=0, il=n; i<il; t[i] = (next()%256).toString(16), t[i] = t[i].length%2 ? '0'+t[i] : t[i], ++i);
149+
    }
150-
					break;
150+
    for (let i = 0, j = 0; i < 64; ++i, ++j) {
151-
				// hex
151+
      j = (j == this.#keyLength) ? 0 : j;
152-
				case 4:
152+
      this.#state[i] = this.#csprngs[j].next()[0];
153-
					t = [];
153+
    }
154-
					for (var i=0, il=n; i<il; t[i] = (next()%256).toString(16), t[i] = t[i].length%2 ? '0'+t[i] : t[i], ++i);
154+
    for (let i = 63, j = 0, t, m; i >= 0; --i, ++j) {
155-
					t = t.join('');
155+
      j = (j == this.#keyLength) ? 0 : j;
156-
					break;
156+
      t = (this.#csprngs[j].next()[0] + this.#key[j]) % 64;
157-
				// next
157+
      m = this.#state[t];
158-
				default:
158+
      this.#state[t] = KeyStream.RXEncrypt(this.#state[i], this.#key[j], this.#workingByte);
159-
					t = new Uint32Array(n);
159+
      this.#state[i] = KeyStream.RXDecrypt(m, t, this.#workingByte);
160-
					for (var i=0, il=n; i<il; t[i++] = next());
160+
    }
161-
			}
161+
  }
162-
			return t;
162+
163-
		};
163+
  #next () {
164
    this.#keyIndex = (++this.#keyIndex == this.#keyLength) ? 0 : this.#keyIndex;
165-
		this.nextHex = function (n) { return this.next(n, 1); };
165+
    this.#stateIndex = (++this.#stateIndex == 64) ? 0 : this.#stateIndex;
166
    this.#state[this.#stateIndex] = KeyStream.RXEncrypt(this.#state[this.#stateIndex], this.#key[this.#keyIndex], this.#workingByte);
167-
		this.bytes = function (n) { return this.next(n, 2); };
167+
    this.#state[63 - this.#stateIndex] = KeyStream.RXEncrypt(this.#state[63 - this.#stateIndex], this.#state[this.#stateIndex], this.#workingByte);
168
    return this.#state[this.#stateIndex];
169-
		this.bytesHex = function (n) { return this.next(n, 3); };
169+
  }
170
171-
		this.hex = function (n) { return this.next(n, 4); };
171+
  getBytes (nBytes) {
172-
	}
172+
    nBytes = (nBytes|0 ? nBytes|0 : (parseInt(nBytes) ? parseInt(nBytes) : 1));
173
    let k = new Uint8Array(nBytes);
174-
	function KeyStream (key, iv) {
174+
    for (let o=0; o<nBytes; ++o) {
175-
		var csprngs, state, keyLength, keyIndex, stateIndex, workingByte = new Uint8Array(1), t, j;
175+
      k[o] = this.#next();
176
    }
177-
		if (typeof key == 'undefined')
177+
    return k;
178-
			throw new Error('RXCipher@KeyStream: key required');
178+
  }
179-
		if (typeof key == 'object' && key instanceof Array)
179+
180-
			key = (typeof key[0] == 'number') ? new Uint8Array(key) : key.join('');
180+
  static RXEncrypt (t, k, workingByte) {
181-
		if (typeof key == 'string')
181+
    workingByte[0] = (t+k);
182-
			key = toUTF8Array(key);
182+
    return workingByte[0]^k;
183-
		if (typeof key !== 'object' && !(key instanceof Uint8Array))
183+
  }
184-
			throw new Error('RXCipher@KeyStream: expected key of type String, Array, or Uint8Array');
184+
185
  static RXDecrypt (c, k, workingByte) {
186-
		if (typeof iv == 'undefined')
186+
    workingByte[0] = (c^k) - k;
187-
			iv = '0123456';
187+
    return workingByte[0];
188-
		if (typeof iv == 'object' && iv instanceof Array)
188+
  }
189-
			iv = (typeof iv[0] == 'number') ? new Uint8Array(iv) : iv.join('');
189+
190-
		if (typeof iv == 'string')
190+
  // convert from JavaScript's internal UTF-16 strings to UTF-8
191-
			iv = toUTF8Array(iv);
191+
  // ripped from http://stackoverflow.com/a/18729931
192-
		if (!(iv instanceof Uint8Array) || iv.length == 0)
192+
  // credit: Joni Salonen
193-
			throw new Error('RXCipher@KeyStream: expected iv of type String, Array or Uint8Array, or undefined');
193+
  static toUTF8Array(str) {
194
    let utf8 = [], charcode;
195-
		// initialize some internal state
195+
    for (let i=0; i < str.length; i++) {
196-
		csprngs = []; keyLength = key.length; keyIndex = 0; state = new Uint8Array(64); stateIndex = 0;
196+
      charcode = str.charCodeAt(i);
197-
		for (var i = 0; i < keyLength; i++) {
197+
      if (charcode < 0x80) utf8.push(charcode);
198-
			csprngs[i] = new XORShiftPlus(key[i]);
198+
      else if (charcode < 0x800) {
199-
		}
199+
        utf8.push(0xc0 | (charcode >> 6),
200-
		for (var i = 0, j = 0, c = 0; c < keyLength * 4; ++i, ++j, ++c) {
200+
            0x80 | (charcode & 0x3f));
201-
			j = (j == keyLength) ? 0 : j;
201+
      } else if (charcode < 0xd800 || charcode >= 0xe000) {
202-
			i = (i == iv.length) ? 0 : i;
202+
        utf8.push(0xe0 | (charcode >> 12),
203-
			key[j] = key[j] + iv[i];
203+
            0x80 | ((charcode>>6) & 0x3f),
204-
		}
204+
            0x80 | (charcode & 0x3f));
205-
		for (var i = 0, j = 0; i < 64; ++i, ++j) {
205+
      } else { // surrogate pair
206-
			j = (j == keyLength) ? 0 : j;
206+
        i++;
207-
			state[i] = csprngs[j].next()[0];
207+
        // UTF-16 encodes 0x10000-0x10FFFF by
208-
		}
208+
        // subtracting 0x10000 and splitting the
209-
		for (var i = 63, j = 0, t, m; i >= 0; --i, ++j) {
209+
        // 20 bits of 0x0-0xFFFFF into two halves
210-
			j = (j == keyLength) ? 0 : j;
210+
        charcode = 0x10000 + (((charcode & 0x3ff)<<10)
211-
			t = (csprngs[j].next()[0]+key[j])%64;
211+
            | (str.charCodeAt(i) & 0x3ff));
212-
			m = state[t];
212+
        utf8.push(0xf0 | (charcode >>18),
213-
			state[t] = RXEncrypt(state[i], key[j], workingByte);
213+
            0x80 | ((charcode>>12) & 0x3f),
214-
			state[i] = RXDecrypt(m, t, workingByte);
214+
            0x80 | ((charcode>>6) & 0x3f),
215-
		}
215+
            0x80 | (charcode & 0x3f));
216
      }
217-
		function next () {
217+
    }
218-
			keyIndex = (++keyIndex == keyLength) ? 0 : keyIndex;
218+
    return new Uint8Array(utf8);
219-
			stateIndex = (++stateIndex == 64) ? 0 : stateIndex;
219+
  }
220-
			state[stateIndex] = RXEncrypt(state[stateIndex], key[keyIndex], workingByte);
220+
}
221-
			state[63 - stateIndex] = RXEncrypt(state[63 - stateIndex], state[stateIndex], workingByte);
221+
222-
			return state[stateIndex];
222+
223-
		}
223+
 * Title:    RXCipher
224
 * Description:    Simple and relatively quick stream cipher algorithm - JavaScript implementation
225-
		this.get = function (nBytes) {
225+
 * Author:    Jonathan Voss
226-
			nBytes = (nBytes|0 ? nBytes|0 : (parseInt(nBytes) ? parseInt(nBytes) : 1));
226+
 * Date:    8/7/2012 (v1.0); 12/16/2014 (v2.0); 11/19/2015 (v3.0); 09/07/2020 (4.0)
227-
			var k = new Uint8Array(nBytes);
227+
 * Version:    4.0
228-
			for (var o=0; o<nBytes; ++o) {
228+
 * Namespace:    pastebin.com/u/k98kurz; github.com/k98kurz
229-
				k[o] = next();
229+
 * License:    ISC
230-
			}
230+
231-
			return k;
231+
232-
		};
232+
233-
	}
233+
 *    function enc (t, k) { return (t+k>255 ? t+k-256 : t+k)^k; }
234
 *    function dec (c, k) { return (c^k)-k<0 ? (c^k)-k+256 : (c^k)-k; }
235-
	// convert from JavaScript's internal UTF-16 strings to UTF-8
235+
236-
	// ripped from http://stackoverflow.com/a/18729931
236+
237-
	// credit: Joni Salonen
237+
238-
	function toUTF8Array(str) {
238+
 * Instance Methods:
239-
		var utf8 = [], charcode;
239+
 *    encrypt ( plaintext )
240-
		for (var i=0; i < str.length; i++) {
240+
 *      encrypts plaintext with key stream derived from skey
241-
			charcode = str.charCodeAt(i);
241+
 *    decrypt ( ciphertext)
242-
			if (charcode < 0x80) utf8.push(charcode);
242+
 *      decrypts ciphertext with key stream derived from skey
243-
			else if (charcode < 0x800) {
243+
 *    getKeyStream ()
244-
				utf8.push(0xc0 | (charcode >> 6),
244+
 *      returns a KeyStream for PRNG generation
245-
						0x80 | (charcode & 0x3f));
245+
 *    reset ()
246-
			} else if (charcode < 0xd800 || charcode >= 0xe000) {
246+
 *      resets the internal KeyStream
247-
				utf8.push(0xe0 | (charcode >> 12),
247+
 * Static Methods:
248-
						0x80 | ((charcode>>6) & 0x3f),
248+
 *    rxencrypt ( key, plaintext, optional iv )
249-
						0x80 | (charcode & 0x3f));
249+
 *      creates ephemeral RXCipher instance, calls encrypt, and returns {iv}.{ciphertext}
250-
			} else { // surrogate pair
250+
 *    rxdecrypt ( key, ciphertextString )
251-
				i++;
251+
 *      decrypts rxencrypt output
252-
				// UTF-16 encodes 0x10000-0x10FFFF by
252+
253-
				// subtracting 0x10000 and splitting the
253+
254-
				// 20 bits of 0x0-0xFFFFF into two halves
254+
 *  2.0 (12/16/2014): added pseudo-random key stream; dropped automatic hex conversion of ciphertext
255-
				charcode = 0x10000 + (((charcode & 0x3ff)<<10)
255+
 *  2.1 (12/29/2014): changed default seed value for XORShiftPlus PRNG
256-
						| (str.charCodeAt(i) & 0x3ff));
256+
 *  2.2 (11/18/2015): converts to/from UTF-8 encoding; new keystream uses multiple csprngs and XORs the key with csprng values
257-
				utf8.push(0xf0 | (charcode >>18),
257+
 *  2.3 (11/18/2015): added explicit IV support; made base RXEncrypt & RXDecrypt use a Uint8 byte
258-
						0x80 | ((charcode>>12) & 0x3f),
258+
 *  3.0 (11/19/2015): changed KeyStream to use an internally mixed 64 byte state; improved IV handling; improved performance
259-
						0x80 | ((charcode>>6) & 0x3f),
259+
 *  3.1 (02/01/2016): updated KeyStream to use improved/fixed XORShiftPlus class; encrypt/decrypt use hex; encryptRaw/decryptRaw use Uint8Array
260-
						0x80 | (charcode & 0x3f));
260+
 *  3.2 (03/08/2016): bug fix: getKeyStream now returns actual KeyStream; reset method returns this
261-
			}
261+
 *  4.0 (09/07/2020): converted to es6 class; added static helper methods
262-
		}
262+
263-
		return new Uint8Array(utf8);
263+
class RXCipher {
264-
	}
264+
  #key;
265
  #iv;
266-
	// convert from UTF-8 to JavaScript's internal UTF-16 strings
266+
  #stream;
267-
	// ripped from https://github.com/coolaj86/TextEncoderLite
267+
268-
	// credit: AJ ONeal (coolaj86) && Feross Aboukhadijeh (feross)
268+
  constructor (key, iv) {
269-
	function fromUTF8Array (arr) {
269+
    this.#key = (typeof key == 'object' && key instanceof Uint8Array) ? RXCipher.fromUTF8Array(key) : key;
270-
		var utf16 = '', tmp = '';
270+
    this.#iv = (typeof iv == 'object' && iv instanceof Uint8Array) ? RXCipher.fromUTF8Array(iv) : iv;
271-
		for (var i = 0, ij = arr.length; i < ij; i++) {
271+
    this.#stream = new KeyStream(this.#key, this.#iv);
272-
			if (arr[i] <= 0x7F) {
272+
  }
273-
				utf16 += decodeUtf8Char(tmp) + String.fromCharCode(arr[i]);
273+
274-
				tmp = '';
274+
  #encrypt (plaintext) {
275-
			} else {
275+
    let ciphertext = new Uint8Array(plaintext.length);
276-
				tmp += '%' + arr[i].toString(16);
276+
    let key = this.#stream.getBytes(plaintext.length);
277-
			}
277+
    for (let i = 0, ij = plaintext.length; i < ij; ++i) {
278-
		};
278+
      ciphertext[i] = plaintext[i] ^ key[i];
279
    }
280-
		return utf16 + decodeUtf8Char(tmp);
280+
    return ciphertext;
281-
	}
281+
  }
282
283-
	function decodeUtf8Char (str) {
283+
  #decrypt (ciphertext) {
284-
		try {
284+
    let plaintext = new Uint8Array(ciphertext.length);
285-
			return decodeURIComponent(str);
285+
    let key = this.#stream.getBytes(ciphertext.length);
286-
		} catch (err) {
286+
    for (let i = 0, ij = ciphertext.length; i < ij; ++i) {
287-
			return String.fromCharCode(0xFFFD); // UTF 8 invalid char
287+
      plaintext[i] = ciphertext[i] ^ key[i];
288-
		}
288+
    }
289-
	}
289+
    return plaintext;
290
  }
291-
	function toHex (raw) {
291+
292-
		var t = [];
292+
  encrypt (plaintext) {
293-
		for (var i=0, il=raw.length; i<il; ++i) {
293+
    let ciphertext = this.#encrypt(RXCipher.toUTF8Array(plaintext));
294-
		    t[i] = raw[i].toString(16); t[i] = t[i].length%2 ? '0'+t[i] : t[i];
294+
    return RXCipher.toHex(ciphertext);
295-
		}
295+
  }
296-
		return t.join('');
296+
297-
	}
297+
  decrypt (ciphertext) {
298
    let plaintext = this.#decrypt(RXCipher.fromHex(ciphertext));
299-
	function fromHex (hex) {
299+
    return RXCipher.fromUTF8Array(plaintext);
300-
		var t = new Uint8Array(hex.length/2);
300+
  }
301-
		for (var i=0, j=0, il=hex.length; i<il; ++i, ++j) {
301+
302-
		    t[j] = parseInt(hex[i] + hex[++i], 16);
302+
  encryptRaw (plaintext) {
303-
		}
303+
    return this.#encrypt(RXCipher.toUTF8Array(plaintext));
304-
		return t;
304+
  }
305-
	}
305+
306-
};
306+
  decryptRaw (ciphertext) {
307
    return RXCipher.fromUTF8Array(this.#decrypt(ciphertext));
308
  }
309
310
  static rxencrypt (key, plaintext, iv) {
311
    if (iv === undefined)
312
      iv = (new XORShiftPlus(Date.now())).bytes(8);
313
314
    let rxc = new RXCipher(key, iv);
315
    let ciphertext = rxc.encrypt(plaintext);
316
317
    return ciphertext + '.' + RXCipher.toHex(iv);
318
  }
319
320
  static rxdecrypt (key, ciphertextString) {
321
    let ctSplit = ciphertextString.split('.');
322
    let ciphertext = ctSplit[0];
323
    let iv = RXCipher.fromHex(ctSplit[1]);
324
    let rxc = new RXCipher(key, iv);
325
326
    return rxc.decrypt(ciphertext);
327
  }
328
329
  getKeyStream () {
330
    return new KeyStream(this.#key, this.#iv);
331
  }
332
333
  reset () {
334
    this.#stream = new KeyStream (this.#key, this.#iv);
335
    return this;
336
  }
337
338
  static RXEncrypt (t, k, workingByte) {
339
    workingByte[0] = (t+k);
340
    return workingByte[0]^k;
341
  }
342
343
  static RXDecrypt (c, k, workingByte) {
344
    workingByte[0] = (c^k) - k;
345
    return workingByte[0];
346
  }
347
348
  // convert from JavaScript's internal UTF-16 strings to UTF-8
349
  // ripped from http://stackoverflow.com/a/18729931
350
  // credit: Joni Salonen
351
  static toUTF8Array(str) {
352
    let utf8 = [], charcode;
353
    for (let i=0; i < str.length; i++) {
354
      charcode = str.charCodeAt(i);
355
      if (charcode < 0x80) utf8.push(charcode);
356
      else if (charcode < 0x800) {
357
        utf8.push(0xc0 | (charcode >> 6),
358
            0x80 | (charcode & 0x3f));
359
      } else if (charcode < 0xd800 || charcode >= 0xe000) {
360
        utf8.push(0xe0 | (charcode >> 12),
361
            0x80 | ((charcode>>6) & 0x3f),
362
            0x80 | (charcode & 0x3f));
363
      } else { // surrogate pair
364
        i++;
365
        // UTF-16 encodes 0x10000-0x10FFFF by
366
        // subtracting 0x10000 and splitting the
367
        // 20 bits of 0x0-0xFFFFF into two halves
368
        charcode = 0x10000 + (((charcode & 0x3ff)<<10)
369
            | (str.charCodeAt(i) & 0x3ff));
370
        utf8.push(0xf0 | (charcode >>18),
371
            0x80 | ((charcode>>12) & 0x3f),
372
            0x80 | ((charcode>>6) & 0x3f),
373
            0x80 | (charcode & 0x3f));
374
      }
375
    }
376
    return new Uint8Array(utf8);
377
  }
378
379
  // convert from UTF-8 to JavaScript's internal UTF-16 strings
380
  // ripped from https://github.com/coolaj86/TextEncoderLite
381
  // credit: AJ ONeal (coolaj86) && Feross Aboukhadijeh (feross)
382
  static fromUTF8Array (arr) {
383
    let utf16 = '', tmp = '';
384
    for (let i = 0, ij = arr.length; i < ij; i++) {
385
      if (arr[i] <= 0x7F) {
386
        utf16 += RXCipher.decodeUtf8Char(tmp) + String.fromCharCode(arr[i]);
387
        tmp = '';
388
      } else {
389
        tmp += '%' + arr[i].toString(16);
390
      }
391
    }
392
393
    return utf16 + RXCipher.decodeUtf8Char(tmp);
394
  }
395
396
  static decodeUtf8Char (str) {
397
    try {
398
      return decodeURIComponent(str);
399
    } catch (err) {
400
      return String.fromCharCode(0xFFFD); // UTF 8 invalid char
401
    }
402
  }
403
404
  static toHex (raw) {
405
    let t = [];
406
    for (let i=0, il=raw.length; i<il; ++i) {
407
        t[i] = raw[i].toString(16); t[i] = t[i].length%2 ? '0'+t[i] : t[i];
408
    }
409
    return t.join('');
410
  }
411
412
  static fromHex (hex) {
413
    let t = new Uint8Array(hex.length/2);
414
    for (let i=0, j=0, il=hex.length; i<il; ++i, ++j) {
415
        t[j] = parseInt(hex[i] + hex[++i], 16);
416
    }
417
    return t;
418
  }
419
}
420