/*  ******************************************************************
 **  Soubor  :  /ariadne/javascript/tea.js
 **
 **  Modul   :  Šifrovací funkce
 **  Verze   :  0.1.0
 **
 **  Autor   :  Michal Hromádko      michal.hromadko@lifeweb.cz
 **  Revize  :  Martin Jonáš         martin.jonas@lifeweb.cz
 **
 **  Popis   :  Funkce pro šifrování pomocí XXTEA.
 **             http://www.movable-type.co.uk/scripts/xxtea.pdf
 ** ***************************************************************** */

/**
 * Funkce šifruje data XXTEA algoritmem
 * Binární výstup je převeden do Base64
 * @param plaintext Řetězec určený k zašifrování
 * @param password Klíč
 * @return Vrací zašifrovaná data převedená do Base64
 */
function TEAencrypt(plaintext, password) {

  // Není co šifrovat
  if (plaintext.length == 0) {
    return('');
  }

  // Řetězec je převeden do Base64. Zabrání se tak případným chybám v UTF-8 řetězcích
  var asciitext = encode(plaintext, true);

  // Data jsou převedena na bloky po 32 bitech
  var v = strToLongs(asciitext);

  // Algoritmus potřebuje minimálně 2 bloky
  if (v.length <= 1) v[1] = 0;

  // Klíč je převeden na bloky po 32 bitech
  var k = strToLongs(password);

  // Klíč musí mít délku 128 bitů
  k = prepareKey(k);

  // Vlastní algoritmus
  var n = v.length;
  var z = v[n-1], y = v[0], delta = 0x9E3779B9;
  var mx, e, q = Math.floor(6 + 52/n), sum = 0;

  while (q-- > 0) {
    sum = int32(sum + delta);
    e = sum>>>2 & 3;

    for (var p = 0; p < n-1; p++) {
      y = v[p+1];
      mx = int32((z>>>5 ^ y<<2) + (y>>>3 ^ z<<4)) ^ int32((sum^y) + (k[p&3 ^ e] ^ z));
      z = v[p] = int32(v[p] + mx);
    }
    y = v[0];
    mx = int32((z>>>5 ^ y<<2) + (y>>>3 ^ z<<4)) ^ int32((sum^y) + (k[p&3 ^ e] ^ z));
    z = v[n-1] = int32(v[n-1] + mx);
  }

  // Převod bloků dat na binární řetězec
  var ciphertext = longsToStr(v);

  // Převod do Base64
  return encode(ciphertext, false);
}

/**
 * Funkce dešifruje data XXTEA algoritmem
 * @param ciphertext Šifrovaná data v Base64
 * @param password Klíč
 * @return Vrací dešifrovaný řetězec
 */
function TEAdecrypt(ciphertext, password) {

  // Není co dešifrovat
  if (ciphertext.length == 0) {
    return('');
  }

  // Data jsou z Base64 převedena na bloky po 32 bitech
  var v = strToLongs(decode(ciphertext), false);

  // Klíč je převeden na bloky po 32 bitech
  var k = strToLongs(password);

  // Klíč musí mít délku 128 bitů
  k = prepareKey(k);

  // Vlastní algoritmus
  var n = v.length;
  var z = v[n-1], y = v[0], delta = 0x9E3779B9;
  var mx, e, q = Math.floor(6 + 52/n), sum = q*delta;

  while (sum != 0) {
      e = sum>>>2 & 3;
      for (var p = n-1; p > 0; p--) {
          z = v[p-1];
          mx = int32((z>>>5 ^ y<<2) + (y>>>3 ^ z<<4)) ^ int32((sum^y) + (k[p&3 ^ e] ^ z));
          y = v[p] = int32(v[p] - mx);
      }
      z = v[n - 1];
      mx = int32((z>>>5 ^ y<<2) + (y>>>3 ^ z<<4)) ^ int32((sum^y) + (k[p&3 ^ e] ^ z));
      y = v[0] = int32(v[0] - mx);
      sum = int32(sum - delta);
  }

  // Bloky dat jsou převedeny na řetězec
  var plaintext = longsToStr(v);

  // Odstranění nulových znaků na konci řetězce. Tyto znaky vznikají při převodu na 32 bitové bloky
  plaintext = plaintext.replace(/\0+$/,'');

  // Převod z Base64 na klasický řetězec
  return decode(plaintext, true);
}

/**
 * Funkce převede řetězec na bloky dat po 32 bitech
 * @param s Řetězec
 * @return Vrací pole 32 bitových čísel
 */
function strToLongs(s) {

  var l = new Array(Math.ceil(s.length/4));
  for (var i=0; i<l.length; i++) {
    l[i] = s.charCodeAt(i*4) + (s.charCodeAt(i*4+1)<<8) + (s.charCodeAt(i*4+2)<<16) + (s.charCodeAt(i*4+3)<<24);
  }
  return l;
}

/**
 * Funkce převede bloky dat po 32 bitech na řetězec
 * @param l Pole 32 bitových čísel
 * @return Vrací řetězec
 */
function longsToStr(l) {

  var a = new Array(l.length);
  for (var i=0; i<l.length; i++) {
    a[i] = String.fromCharCode(l[i] & 0xFF, l[i]>>>8 & 0xFF, l[i]>>>16 & 0xFF, l[i]>>>24 & 0xFF);
  }
  return a.join('');
}

/**
 * Funkce simuluje přetečení 32 bitového čísla
 * @param n Číslo
 * @return Vrací 32 bitové číslo
 */
function int32(n) {
  while (n >= 2147483648) {
    n -= 4294967296;
  }
  while (n <= -2147483649) {
    n += 4294967296;
  }
  return n;
}

/**
 * Funkce převede řetězec na Base64
 * @param s Řetězec
 * @param utf8 Určuje, zda jde o binární řetězec nebo o text v UTF-8
 * @return Vrací vrací řetězec v Base64
 */
function encode(s, utf8) {
  return Base64.encode(s, utf8);
}

/**
 * Funkce převede Base64 na řetězec
 * @param s Řetězec v Base64
 * @param utf8 Určuje, zda jde o binární řetězec nebo o text v UTF-8
 * @return Vrací řetězec
 */
function decode(s, utf8) {
  return Base64.decode(s, utf8);
}


/**
 * Funkce upraví délku klíče na 128 bitů
 * @param k Klíč v binární podobě
 * @return Vrací upravený klíč
 */
function prepareKey(k) {
  if (k.length < 4) {
    for (var i = k.length; i < 4; i++) {
      k[i] = 0;
    }
  }
  k = k.slice(0, 4);

  return k;
}