Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * XORShiftPlus "non-linear" PRNG
- * Author: k98kurz (@gmail)
- * License: MIT
- * Date: 2020-09-07
- * 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.
- */
- class XORShiftPlus {
- #booted = false;
- #x;
- #y;
- #z;
- #w;
- constructor (seed) {
- this.#x = new Uint32Array(1);
- this.#y = new Uint32Array(1);
- this.#z = new Uint32Array(1);
- this.#w = new Uint32Array(1);
- this.#x[0] = seed ? seed|0 : 317973455;
- this.#y[0] = this.#x[0]<<362436069;
- this.#z[0] = this.#y[0]+this.#x[0];
- this.#w[0] = this.#z[0]^this.#x[0]+this.#y[0];
- }
- #next () {
- let t = new Uint32Array(1);
- t[0] = this.#x[0]^(this.#x[0]<<11);
- this.#x[0] = this.#y[0];
- this.#y[0] = this.#z[0];
- this.#z[0] = this.#w[0];
- this.#w[0] = this.#w[0]^(this.#w[0]>>19)^(t[0]^(t[0]>>8));
- return this.#w[0]+this.#y[0];
- }
- #boot () {
- // if not booted, discard the first numbers as they are somewhat predictable
- if (this.#booted)
- return;
- for (let i=0, n=this.#next(), j=this.#next()%256; i<=j; this.#next(), ++i);
- this.#booted = true;
- }
- next (n, option) {
- this.#boot();
- n = (n === undefined || typeof n !== 'number') ? 1 : n;
- let t; n |= 0; n = n>0 ? n : 1;
- option = (option === undefined || typeof n !== 'number') ? 0 : option;
- // redundant code, but improved performance
- switch (option) {
- // nextHex
- case 1:
- t = [];
- for (let i=0, il=n; i<il; t[i++] = this.#next().toString(16));
- break;
- // bytes
- case 2:
- t = new Uint8Array(n);
- for (let i=0, il=n; i<il; t[i++] = this.#next());
- break;
- // bytesHex
- case 3:
- t = [];
- 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);
- break;
- // hex
- case 4:
- t = [];
- 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);
- t = t.join('');
- break;
- // next
- default:
- t = new Uint32Array(n);
- for (let i=0, il=n; i<il; t[i++] = this.#next());
- }
- return t;
- };
- nextHex (n) { return this.next(n, 1); };
- bytes (n) { return this.next(n, 2); };
- bytesHex (n) { return this.next(n, 3); };
- hex (n) { return this.next(n, 4); };
- }
- /**
- * KeyStream
- * Author: k98kurz
- * License: MIT
- * Date: 2020-09-07
- * Description: Takes a key and iv, mixes them, and uses the result to seed several XORShiftPlus PRNGs.
- * Dependency: XORShiftPlus
- * Methods:
- *
- */
- class KeyStream {
- #csprngs;
- #state;
- #keyLength;
- #keyIndex;
- #stateIndex;
- #workingByte = new Uint8Array(1);
- #key;
- #iv;
- constructor (key, iv) {
- if (typeof key == 'undefined')
- throw new Error('KeyStream: key required');
- if (typeof key == 'object' && key instanceof Array)
- key = (typeof key[0] == 'number') ? new Uint8Array(key) : key.join('');
- if (typeof key == 'string')
- key = KeyStream.toUTF8Array(key);
- if (typeof key !== 'object' && !(key instanceof Uint8Array))
- throw new Error('KeyStream: expected key of type String, Array, or Uint8Array');
- if (typeof iv == 'undefined')
- throw new Error('KeyStream: iv required');
- if (typeof iv == 'object' && iv instanceof Array)
- iv = (typeof iv[0] == 'number') ? new Uint8Array(iv) : iv.join('');
- if (typeof iv == 'string')
- iv = KeyStream.toUTF8Array(iv);
- if (!(iv instanceof Uint8Array) || iv.length == 0)
- throw new Error('KeyStream: expected iv of type String, Array or Uint8Array');
- // set the key and iv private properties
- this.#key = key;
- this.#iv = iv;
- // initialize some internal state
- this.#csprngs = []; this.#keyLength = this.#key.length; this.#keyIndex = 0; this.#state = new Uint8Array(64); this.#stateIndex = 0;
- for (let i = 0; i < this.#keyLength; i++) {
- this.#csprngs[i] = new XORShiftPlus(this.#key[i]);
- }
- for (let i = 0, j = 0, c = 0; c < this.#keyLength * 4; ++i, ++j, ++c) {
- j = (j == this.#keyLength) ? 0 : j;
- i = (i == this.#iv.length) ? 0 : i;
- this.#key[j] = this.#key[j] + this.#iv[i];
- }
- for (let i = 0, j = 0; i < 64; ++i, ++j) {
- j = (j == this.#keyLength) ? 0 : j;
- this.#state[i] = this.#csprngs[j].next()[0];
- }
- for (let i = 63, j = 0, t, m; i >= 0; --i, ++j) {
- j = (j == this.#keyLength) ? 0 : j;
- t = (this.#csprngs[j].next()[0] + this.#key[j]) % 64;
- m = this.#state[t];
- this.#state[t] = KeyStream.RXEncrypt(this.#state[i], this.#key[j], this.#workingByte);
- this.#state[i] = KeyStream.RXDecrypt(m, t, this.#workingByte);
- }
- }
- #next () {
- this.#keyIndex = (++this.#keyIndex == this.#keyLength) ? 0 : this.#keyIndex;
- this.#stateIndex = (++this.#stateIndex == 64) ? 0 : this.#stateIndex;
- this.#state[this.#stateIndex] = KeyStream.RXEncrypt(this.#state[this.#stateIndex], this.#key[this.#keyIndex], this.#workingByte);
- this.#state[63 - this.#stateIndex] = KeyStream.RXEncrypt(this.#state[63 - this.#stateIndex], this.#state[this.#stateIndex], this.#workingByte);
- return this.#state[this.#stateIndex];
- }
- getBytes (nBytes) {
- nBytes = (nBytes|0 ? nBytes|0 : (parseInt(nBytes) ? parseInt(nBytes) : 1));
- let k = new Uint8Array(nBytes);
- for (let o=0; o<nBytes; ++o) {
- k[o] = this.#next();
- }
- return k;
- }
- static RXEncrypt (t, k, workingByte) {
- workingByte[0] = (t+k);
- return workingByte[0]^k;
- }
- static RXDecrypt (c, k, workingByte) {
- workingByte[0] = (c^k) - k;
- return workingByte[0];
- }
- // convert from JavaScript's internal UTF-16 strings to UTF-8
- // ripped from http://stackoverflow.com/a/18729931
- // credit: Joni Salonen
- static toUTF8Array(str) {
- let utf8 = [], charcode;
- for (let i=0; i < str.length; i++) {
- charcode = str.charCodeAt(i);
- if (charcode < 0x80) utf8.push(charcode);
- else if (charcode < 0x800) {
- utf8.push(0xc0 | (charcode >> 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);
- }
- }
- /**
- * 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); 09/07/2020 (4.0)
- * Version: 4.0
- * Namespace: pastebin.com/u/k98kurz; github.com/k98kurz
- * License: ISC
- *
- * 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
- * Instance 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
- * Static Methods:
- * rxencrypt ( key, plaintext, optional iv )
- * creates ephemeral RXCipher instance, calls encrypt, and returns {iv}.{ciphertext}
- * rxdecrypt ( key, ciphertextString )
- * decrypts rxencrypt output
- *
- * 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
- * 4.0 (09/07/2020): converted to es6 class; added static helper methods
- */
- class RXCipher {
- #key;
- #iv;
- #stream;
- constructor (key, iv) {
- this.#key = (typeof key == 'object' && key instanceof Uint8Array) ? RXCipher.fromUTF8Array(key) : key;
- this.#iv = (typeof iv == 'object' && iv instanceof Uint8Array) ? RXCipher.fromUTF8Array(iv) : iv;
- this.#stream = new KeyStream(this.#key, this.#iv);
- }
- #encrypt (plaintext) {
- let ciphertext = new Uint8Array(plaintext.length);
- let key = this.#stream.getBytes(plaintext.length);
- for (let i = 0, ij = plaintext.length; i < ij; ++i) {
- ciphertext[i] = plaintext[i] ^ key[i];
- }
- return ciphertext;
- }
- #decrypt (ciphertext) {
- let plaintext = new Uint8Array(ciphertext.length);
- let key = this.#stream.getBytes(ciphertext.length);
- for (let i = 0, ij = ciphertext.length; i < ij; ++i) {
- plaintext[i] = ciphertext[i] ^ key[i];
- }
- return plaintext;
- }
- encrypt (plaintext) {
- let ciphertext = this.#encrypt(RXCipher.toUTF8Array(plaintext));
- return RXCipher.toHex(ciphertext);
- }
- decrypt (ciphertext) {
- let plaintext = this.#decrypt(RXCipher.fromHex(ciphertext));
- return RXCipher.fromUTF8Array(plaintext);
- }
- encryptRaw (plaintext) {
- return this.#encrypt(RXCipher.toUTF8Array(plaintext));
- }
- decryptRaw (ciphertext) {
- return RXCipher.fromUTF8Array(this.#decrypt(ciphertext));
- }
- static rxencrypt (key, plaintext, iv) {
- if (iv === undefined)
- iv = (new XORShiftPlus(Date.now())).bytes(8);
- let rxc = new RXCipher(key, iv);
- let ciphertext = rxc.encrypt(plaintext);
- return ciphertext + '.' + RXCipher.toHex(iv);
- }
- static rxdecrypt (key, ciphertextString) {
- let ctSplit = ciphertextString.split('.');
- let ciphertext = ctSplit[0];
- let iv = RXCipher.fromHex(ctSplit[1]);
- let rxc = new RXCipher(key, iv);
- return rxc.decrypt(ciphertext);
- }
- getKeyStream () {
- return new KeyStream(this.#key, this.#iv);
- }
- reset () {
- this.#stream = new KeyStream (this.#key, this.#iv);
- return this;
- }
- static RXEncrypt (t, k, workingByte) {
- workingByte[0] = (t+k);
- return workingByte[0]^k;
- }
- static RXDecrypt (c, k, workingByte) {
- workingByte[0] = (c^k) - k;
- return workingByte[0];
- }
- // convert from JavaScript's internal UTF-16 strings to UTF-8
- // ripped from http://stackoverflow.com/a/18729931
- // credit: Joni Salonen
- static toUTF8Array(str) {
- let utf8 = [], charcode;
- for (let i=0; i < str.length; i++) {
- charcode = str.charCodeAt(i);
- if (charcode < 0x80) utf8.push(charcode);
- else if (charcode < 0x800) {
- utf8.push(0xc0 | (charcode >> 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)
- static fromUTF8Array (arr) {
- let utf16 = '', tmp = '';
- for (let i = 0, ij = arr.length; i < ij; i++) {
- if (arr[i] <= 0x7F) {
- utf16 += RXCipher.decodeUtf8Char(tmp) + String.fromCharCode(arr[i]);
- tmp = '';
- } else {
- tmp += '%' + arr[i].toString(16);
- }
- }
- return utf16 + RXCipher.decodeUtf8Char(tmp);
- }
- static decodeUtf8Char (str) {
- try {
- return decodeURIComponent(str);
- } catch (err) {
- return String.fromCharCode(0xFFFD); // UTF 8 invalid char
- }
- }
- static toHex (raw) {
- let t = [];
- for (let i=0, il=raw.length; i<il; ++i) {
- t[i] = raw[i].toString(16); t[i] = t[i].length%2 ? '0'+t[i] : t[i];
- }
- return t.join('');
- }
- static fromHex (hex) {
- let t = new Uint8Array(hex.length/2);
- for (let i=0, j=0, il=hex.length; i<il; ++i, ++j) {
- t[j] = parseInt(hex[i] + hex[++i], 16);
- }
- return t;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement