Advertisement
k98kurz

rxcipher.3.2.js

Nov 19th, 2015 (edited)
302
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * Title:       RXCipher
  3.  * Description: Simple and relatively quick stream cipher algorithm - JavaScript implementation
  4.  * Author:      Jonathan Voss
  5.  * Date:        8/7/2012 (v1.0); 12/16/2014 (v2.0); 11/19/2015 (v3.0)
  6.  * Version:     3.2
  7.  * Namespace:   github.com/k98kurz
  8.  * License:     MIT
  9.  *
  10.  * Basic byte-level algorithm
  11.  * t = plaintext byte, c = ciphertext byte, k = encryption keystream byte
  12.  *      function enc (t, k) { return (t+k>255 ? t+k-256 : t+k)^k; }
  13.  *      function dec (c, k) { return (c^k)-k<0 ? (c^k)-k+256 : (c^k)-k; }
  14.  *
  15.  *
  16.  * Class implementation
  17.  * Methods:
  18.  *      encrypt ( plaintext )
  19.  *          encrypts plaintext with key stream derived from skey
  20.  *      decrypt ( ciphertext)
  21.  *          decrypts ciphertext with key stream derived from skey
  22.  *      getKeyStream ()
  23.  *          returns a KeyStream for PRNG generation
  24.  *      reset ()
  25.  *          resets the internal KeyStream
  26.  *
  27.  * Changes:
  28.  *  2.0 (12/16/2014): added pseudo-random key stream; dropped automatic hex conversion of ciphertext
  29.  *  2.1 (12/29/2014): changed default seed value for XORShiftPlus PRNG
  30.  *  2.2 (11/18/2015): converts to/from UTF-8 encoding; new keystream uses multiple csprngs and XORs the key with csprng values
  31.  *  2.3 (11/18/2015): added explicit IV support; made base RXEncrypt & RXDecrypt use a Uint8 byte
  32.  *  3.0 (11/19/2015): changed KeyStream to use an internally mixed 64 byte state; improved IV handling; improved performance
  33.  *  3.1 (02/01/2016): updated KeyStream to use improved/fixed XORShiftPlus class; encrypt/decrypt use hex; encryptRaw/decryptRaw use Uint8Array
  34.  *  3.2 (03/08/2016): bug fix: getKeyStream now returns actual KeyStream; reset method returns this
  35. */
  36.  
  37. function RXCipher (key, iv) {
  38.     var skey, siv = iv, stream;
  39.     skey = (typeof key == 'object' && key instanceof Uint8Array) ? fromUTF8Array(key) : key;
  40.     siv = (typeof iv == 'object' && iv instanceof Uint8Array) ? fromUTF8Array(iv) : iv;
  41.     stream = new KeyStream(skey, siv);
  42.  
  43.     function encrypt (plaintext) {
  44.         var ciphertext = new Uint8Array(plaintext.length), g, t, k, key;
  45.         key = stream.get(plaintext.length);
  46.         for (var i = 0, ij = plaintext.length; i < ij; ++i) {
  47.             ciphertext[i] = plaintext[i] ^ key[i];
  48.         }
  49.         return ciphertext;
  50.     }
  51.  
  52.     function decrypt (ciphertext) {
  53.         var plaintext = new Uint8Array(ciphertext.length), g, c, k, key;
  54.         key = stream.get(ciphertext.length);
  55.         for (var i = 0, ij = ciphertext.length; i < ij; ++i) {
  56.             plaintext[i] = ciphertext[i] ^ key[i];
  57.         }
  58.         return plaintext;
  59.     }
  60.  
  61.     this.encrypt = function (plaintext) {
  62.         var ciphertext = encrypt(toUTF8Array(plaintext));
  63.         return toHex(ciphertext);
  64.     };
  65.  
  66.     this.decrypt = function (ciphertext) {
  67.         var plaintext = decrypt(fromHex(ciphertext));
  68.         return fromUTF8Array(plaintext);
  69.     };
  70.  
  71.     this.encryptRaw = function (plaintext) {
  72.         return encrypt(toUTF8Array(plaintext));
  73.     };
  74.  
  75.     this.decryptRaw = function (ciphertext) {
  76.         return fromUTF8Array(decrypt(ciphertext));
  77.     };
  78.  
  79.     this.getKeyStream = function () {
  80.         return new KeyStream(skey, siv);
  81.     };
  82.  
  83.     this.reset = function () {
  84.         stream = new KeyStream (skey, siv);
  85.         return this;
  86.     };
  87.  
  88.     function RXEncrypt (t, k, workingByte) {
  89.         workingByte[0] = (t+k);
  90.         return workingByte[0]^k;
  91.     }
  92.  
  93.     function RXDecrypt (c, k, workingByte) {
  94.         workingByte[0] = (c^k) - k;
  95.         return workingByte[0];
  96.     }
  97.  
  98.     /**
  99.      *  Class: XORShiftPlus: "non-linear" PRNG
  100.      *  Description: Based upon the xorshift128+ generator, one of the fastest generators passing BigCrush.
  101.      *  Methods:
  102.      *      next(n)         returns Uint32Array of n random numbers
  103.      *      nextHex(n)      returns Array of n hexidecimal numbers
  104.      *      bytes(n)        returns Uint8Array of n random bytes
  105.      *      bytesHex(n)     returns Array of n random bytes in hexidecimal
  106.      *      hex(n)          returns hexidecimal string of n bytes
  107.      *  Note: This generates with higher periodicity and better random distribution than the linear XORShift
  108.      */
  109.     function XORShiftPlus (seed) {
  110.         var x = new Uint32Array(1), y = new Uint32Array(1), z = new Uint32Array(1), w = new Uint32Array(1);
  111.         x[0] = seed ? seed|0 : 317973455;
  112.         y[0] = x[0]<<362436069;
  113.         z[0] = y[0]+x[0];
  114.         w[0] = z[0]^x[0]+y[0];
  115.  
  116.         function next () {
  117.             var t = new Uint32Array(1);
  118.             t[0] = x[0]^(x<<11);
  119.             x[0] = y[0]; y[0] = z[0]; z[0] = w[0];
  120.             w[0] = w[0]^(w[0]>>19)^(t[0]^(t[0]>>8));
  121.             return w[0]+y[0];
  122.         }
  123.  
  124.         // discard the first numbers as they are somewhat predictable
  125.         for (var i=0, n=next(), j=next()%256; i<=j; next(), ++i);
  126.  
  127.         this.next = function (n, option) {
  128.             if (typeof n == 'undefined' || typeof n !== 'number')
  129.                 var n = 1;
  130.             var t; n |= 0; n = n>0 ? n : 1;
  131.             if (typeof option == 'undefined' || typeof n !== 'number')
  132.                 var option = 0;
  133.  
  134.             // redundant code, but improved performance
  135.             switch (option) {
  136.                 // nextHex
  137.                 case 1:
  138.                     t = [];
  139.                     for (var i=0, il=n; i<il; t[i++] = next().toString(16));
  140.                     break;
  141.                 // bytes
  142.                 case 2:
  143.                     t = new Uint8Array(n);
  144.                     for (var i=0, il=n; i<il; t[i++] = next());
  145.                     break;
  146.                 // bytesHex
  147.                 case 3:
  148.                     t = [];
  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);
  150.                     break;
  151.                 // hex
  152.                 case 4:
  153.                     t = [];
  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);
  155.                     t = t.join('');
  156.                     break;
  157.                 // next
  158.                 default:
  159.                     t = new Uint32Array(n);
  160.                     for (var i=0, il=n; i<il; t[i++] = next());
  161.             }
  162.             return t;
  163.         };
  164.  
  165.         this.nextHex = function (n) { return this.next(n, 1); };
  166.  
  167.         this.bytes = function (n) { return this.next(n, 2); };
  168.  
  169.         this.bytesHex = function (n) { return this.next(n, 3); };
  170.  
  171.         this.hex = function (n) { return this.next(n, 4); };
  172.     }
  173.  
  174.     function KeyStream (key, iv) {
  175.         var csprngs, state, keyLength, keyIndex, stateIndex, workingByte = new Uint8Array(1), t, j;
  176.  
  177.         if (typeof key == 'undefined')
  178.             throw new Error('RXCipher@KeyStream: key required');
  179.         if (typeof key == 'object' && key instanceof Array)
  180.             key = (typeof key[0] == 'number') ? new Uint8Array(key) : key.join('');
  181.         if (typeof key == 'string')
  182.             key = toUTF8Array(key);
  183.         if (typeof key !== 'object' && !(key instanceof Uint8Array))
  184.             throw new Error('RXCipher@KeyStream: expected key of type String, Array, or Uint8Array');
  185.  
  186.         if (typeof iv == 'undefined')
  187.             iv = '0123456';
  188.         if (typeof iv == 'object' && iv instanceof Array)
  189.             iv = (typeof iv[0] == 'number') ? new Uint8Array(iv) : iv.join('');
  190.         if (typeof iv == 'string')
  191.             iv = toUTF8Array(iv);
  192.         if (!(iv instanceof Uint8Array) || iv.length == 0)
  193.             throw new Error('RXCipher@KeyStream: expected iv of type String, Array or Uint8Array, or undefined');
  194.  
  195.         // initialize some internal state
  196.         csprngs = []; keyLength = key.length; keyIndex = 0; state = new Uint8Array(64); stateIndex = 0;
  197.         for (var i = 0; i < keyLength; i++) {
  198.             csprngs[i] = new XORShiftPlus(key[i]);
  199.         }
  200.         for (var i = 0, j = 0, c = 0; c < keyLength * 4; ++i, ++j, ++c) {
  201.             j = (j == keyLength) ? 0 : j;
  202.             i = (i == iv.length) ? 0 : i;
  203.             key[j] = key[j] + iv[i];
  204.         }
  205.         for (var i = 0, j = 0; i < 64; ++i, ++j) {
  206.             j = (j == keyLength) ? 0 : j;
  207.             state[i] = csprngs[j].next()[0];
  208.         }
  209.         for (var i = 63, j = 0, t, m; i >= 0; --i, ++j) {
  210.             j = (j == keyLength) ? 0 : j;
  211.             t = (csprngs[j].next()[0]+key[j])%64;
  212.             m = state[t];
  213.             state[t] = RXEncrypt(state[i], key[j], workingByte);
  214.             state[i] = RXDecrypt(m, t, workingByte);
  215.         }
  216.  
  217.         function next () {
  218.             keyIndex = (++keyIndex == keyLength) ? 0 : keyIndex;
  219.             stateIndex = (++stateIndex == 64) ? 0 : stateIndex;
  220.             state[stateIndex] = RXEncrypt(state[stateIndex], key[keyIndex], workingByte);
  221.             state[63 - stateIndex] = RXEncrypt(state[63 - stateIndex], state[stateIndex], workingByte);
  222.             return state[stateIndex];
  223.         }
  224.  
  225.         this.get = function (nBytes) {
  226.             nBytes = (nBytes|0 ? nBytes|0 : (parseInt(nBytes) ? parseInt(nBytes) : 1));
  227.             var k = new Uint8Array(nBytes);
  228.             for (var o=0; o<nBytes; ++o) {
  229.                 k[o] = next();
  230.             }
  231.             return k;
  232.         };
  233.     }
  234.  
  235.     // convert from JavaScript's internal UTF-16 strings to UTF-8
  236.     // ripped from http://stackoverflow.com/a/18729931
  237.     // credit: Joni Salonen
  238.     function toUTF8Array(str) {
  239.         var utf8 = [], charcode;
  240.         for (var i=0; i < str.length; i++) {
  241.             charcode = str.charCodeAt(i);
  242.             if (charcode < 0x80) utf8.push(charcode);
  243.             else if (charcode < 0x800) {
  244.                 utf8.push(0xc0 | (charcode >> 6),
  245.                         0x80 | (charcode & 0x3f));
  246.             } else if (charcode < 0xd800 || charcode >= 0xe000) {
  247.                 utf8.push(0xe0 | (charcode >> 12),
  248.                         0x80 | ((charcode>>6) & 0x3f),
  249.                         0x80 | (charcode & 0x3f));
  250.             } else { // surrogate pair
  251.                 i++;
  252.                 // UTF-16 encodes 0x10000-0x10FFFF by
  253.                 // subtracting 0x10000 and splitting the
  254.                 // 20 bits of 0x0-0xFFFFF into two halves
  255.                 charcode = 0x10000 + (((charcode & 0x3ff)<<10)
  256.                         | (str.charCodeAt(i) & 0x3ff));
  257.                 utf8.push(0xf0 | (charcode >>18),
  258.                         0x80 | ((charcode>>12) & 0x3f),
  259.                         0x80 | ((charcode>>6) & 0x3f),
  260.                         0x80 | (charcode & 0x3f));
  261.             }
  262.         }
  263.         return new Uint8Array(utf8);
  264.     }
  265.  
  266.     // convert from UTF-8 to JavaScript's internal UTF-16 strings
  267.     // ripped from https://github.com/coolaj86/TextEncoderLite
  268.     // credit: AJ ONeal (coolaj86) && Feross Aboukhadijeh (feross)
  269.     function fromUTF8Array (arr) {
  270.         var utf16 = '', tmp = '';
  271.         for (var i = 0, ij = arr.length; i < ij; i++) {
  272.             if (arr[i] <= 0x7F) {
  273.                 utf16 += decodeUtf8Char(tmp) + String.fromCharCode(arr[i]);
  274.                 tmp = '';
  275.             } else {
  276.                 tmp += '%' + arr[i].toString(16);
  277.             }
  278.         };
  279.  
  280.         return utf16 + decodeUtf8Char(tmp);
  281.     }
  282.  
  283.     function decodeUtf8Char (str) {
  284.         try {
  285.             return decodeURIComponent(str);
  286.         } catch (err) {
  287.             return String.fromCharCode(0xFFFD); // UTF 8 invalid char
  288.         }
  289.     }
  290.  
  291.     function toHex (raw) {
  292.         var t = [];
  293.         for (var i=0, il=raw.length; i<il; ++i) {
  294.             t[i] = raw[i].toString(16); t[i] = t[i].length%2 ? '0'+t[i] : t[i];
  295.         }
  296.         return t.join('');
  297.     }
  298.  
  299.     function fromHex (hex) {
  300.         var t = new Uint8Array(hex.length/2);
  301.         for (var i=0, j=0, il=hex.length; i<il; ++i, ++j) {
  302.             t[j] = parseInt(hex[i] + hex[++i], 16);
  303.         }
  304.         return t;
  305.     }
  306. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement