includes/rijndael.php
changeset 1 fe660c52c48f
child 40 723bb7acf914
equal deleted inserted replaced
0:902822492a68 1:fe660c52c48f
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * Phijndael - an implementation of the AES encryption standard in PHP
       
     5  * Originally written by Fritz Schneider <fritz AT cd DOT ucsd DOT edu>
       
     6  * Ported to PHP by Dan Fuhry <dan AT enano DOT homelinux DOT org>
       
     7  * @package phijndael
       
     8  * @author Fritz Schneider
       
     9  * @author Dan Fuhry
       
    10  * @license BSD-style license
       
    11  */
       
    12 
       
    13 error_reporting(E_ALL);
       
    14 
       
    15 define ('ENC_HEX', 201);
       
    16 define ('ENC_BASE64', 202);
       
    17 define ('ENC_BINARY', 203);
       
    18 
       
    19 class AESCrypt {
       
    20   
       
    21   var $debug = false;
       
    22   var $mcrypt = false;
       
    23 
       
    24   // Rijndael parameters --  Valid values are 128, 192, or 256
       
    25   
       
    26   var $keySizeInBits = 128;
       
    27   var $blockSizeInBits = 128;
       
    28   
       
    29   ///////  You shouldn't have to modify anything below this line except for
       
    30   ///////  the function getRandomBytes().
       
    31   //
       
    32   // Note: in the following code the two dimensional arrays are indexed as
       
    33   //       you would probably expect, as array[row][column]. The state arrays
       
    34   //       are 2d arrays of the form state[4][Nb].
       
    35   
       
    36   
       
    37   // The number of rounds for the cipher, indexed by [Nk][Nb]
       
    38   var $roundsArray = Array(0,0,0,0,Array(0,0,0,0,10,0, 12,0, 14),0, 
       
    39                                Array(0,0,0,0,12,0, 12,0, 14),0, 
       
    40                                Array(0,0,0,0,14,0, 14,0, 14) );
       
    41   
       
    42   // The number of bytes to shift by in shiftRow, indexed by [Nb][row]
       
    43   var $shiftOffsets = Array(0,0,0,0,Array(0,1, 2, 3),0,Array(0,1, 2, 3),0,Array(0,1, 3, 4) );
       
    44   
       
    45   // The round constants used in subkey expansion
       
    46   var $Rcon = Array( 
       
    47   0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 
       
    48   0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 
       
    49   0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 
       
    50   0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 
       
    51   0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 );
       
    52   
       
    53   // Precomputed lookup table for the SBox
       
    54   var $SBox = Array(
       
    55    99, 124, 119, 123, 242, 107, 111, 197,  48,   1, 103,  43, 254, 215, 171, 
       
    56   118, 202, 130, 201, 125, 250,  89,  71, 240, 173, 212, 162, 175, 156, 164, 
       
    57   114, 192, 183, 253, 147,  38,  54,  63, 247, 204,  52, 165, 229, 241, 113, 
       
    58   216,  49,  21,   4, 199,  35, 195,  24, 150,   5, 154,   7,  18, 128, 226, 
       
    59   235,  39, 178, 117,   9, 131,  44,  26,  27, 110,  90, 160,  82,  59, 214, 
       
    60   179,  41, 227,  47, 132,  83, 209,   0, 237,  32, 252, 177,  91, 106, 203, 
       
    61   190,  57,  74,  76,  88, 207, 208, 239, 170, 251,  67,  77,  51, 133,  69, 
       
    62   249,   2, 127,  80,  60, 159, 168,  81, 163,  64, 143, 146, 157,  56, 245, 
       
    63   188, 182, 218,  33,  16, 255, 243, 210, 205,  12,  19, 236,  95, 151,  68,  
       
    64   23,  196, 167, 126,  61, 100,  93,  25, 115,  96, 129,  79, 220,  34,  42, 
       
    65   144, 136,  70, 238, 184,  20, 222,  94,  11, 219, 224,  50,  58,  10,  73,
       
    66     6,  36,  92, 194, 211, 172,  98, 145, 149, 228, 121, 231, 200,  55, 109, 
       
    67   141, 213,  78, 169, 108,  86, 244, 234, 101, 122, 174,   8, 186, 120,  37,  
       
    68    46,  28, 166, 180, 198, 232, 221, 116,  31,  75, 189, 139, 138, 112,  62, 
       
    69   181, 102,  72,   3, 246,  14,  97,  53,  87, 185, 134, 193,  29, 158, 225,
       
    70   248, 152,  17, 105, 217, 142, 148, 155,  30, 135, 233, 206,  85,  40, 223,
       
    71   140, 161, 137,  13, 191, 230,  66, 104,  65, 153,  45,  15, 176,  84, 187,  
       
    72    22 );
       
    73   
       
    74   // Precomputed lookup table for the inverse SBox
       
    75   var $SBoxInverse = Array(
       
    76    82,   9, 106, 213,  48,  54, 165,  56, 191,  64, 163, 158, 129, 243, 215, 
       
    77   251, 124, 227,  57, 130, 155,  47, 255, 135,  52, 142,  67,  68, 196, 222, 
       
    78   233, 203,  84, 123, 148,  50, 166, 194,  35,  61, 238,  76, 149,  11,  66, 
       
    79   250, 195,  78,   8,  46, 161, 102,  40, 217,  36, 178, 118,  91, 162,  73, 
       
    80   109, 139, 209,  37, 114, 248, 246, 100, 134, 104, 152,  22, 212, 164,  92, 
       
    81   204,  93, 101, 182, 146, 108, 112,  72,  80, 253, 237, 185, 218,  94,  21,  
       
    82    70,  87, 167, 141, 157, 132, 144, 216, 171,   0, 140, 188, 211,  10, 247, 
       
    83   228,  88,   5, 184, 179,  69,   6, 208,  44,  30, 143, 202,  63,  15,   2, 
       
    84   193, 175, 189,   3,   1,  19, 138, 107,  58, 145,  17,  65,  79, 103, 220, 
       
    85   234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116,  34, 231, 173,
       
    86    53, 133, 226, 249,  55, 232,  28, 117, 223, 110,  71, 241,  26, 113,  29, 
       
    87    41, 197, 137, 111, 183,  98,  14, 170,  24, 190,  27, 252,  86,  62,  75, 
       
    88   198, 210, 121,  32, 154, 219, 192, 254, 120, 205,  90, 244,  31, 221, 168,
       
    89    51, 136,   7, 199,  49, 177,  18,  16,  89,  39, 128, 236,  95,  96,  81,
       
    90   127, 169,  25, 181,  74,  13,  45, 229, 122, 159, 147, 201, 156, 239, 160,
       
    91   224,  59,  77, 174,  42, 245, 176, 200, 235, 187,  60, 131,  83, 153,  97, 
       
    92    23,  43,   4, 126, 186, 119, 214,  38, 225, 105,  20,  99,  85,  33,  12,
       
    93   125 );
       
    94   
       
    95   function AESCrypt($ks = 128, $bs = 128, $debug = false)
       
    96   {
       
    97     $this->__construct($ks, $bs, $debug);
       
    98   }
       
    99   
       
   100   function __construct($ks = 128, $bs = 128, $debug = false)
       
   101   {
       
   102     $this->keySizeInBits = $ks;
       
   103     $this->blockSizeInBits = $bs;
       
   104     
       
   105     // Use the Mcrypt library? This speeds things up dramatically.
       
   106     if(defined('MCRYPT_RIJNDAEL_' . $ks) && defined('MCRYPT_ACCEL'))
       
   107     {
       
   108       eval('$mcb = MCRYPT_RIJNDAEL_' . $ks.';');
       
   109       $bks = mcrypt_module_get_algo_block_size($mcb);
       
   110       $bks = $bks * 8;
       
   111       if ( $bks != $bs )
       
   112       {
       
   113         $mcb = false;
       
   114         echo (string)$bks;
       
   115       }
       
   116     }
       
   117     else
       
   118     {
       
   119       $mcb = false;
       
   120     }
       
   121       
       
   122     $this->mcrypt = $mcb;
       
   123     
       
   124     // Cipher parameters ... do not change these
       
   125     $this->Nk = $this->keySizeInBits / 32;
       
   126     $this->Nb = $this->blockSizeInBits / 32;
       
   127     $this->Nr = $this->roundsArray[$this->Nk][$this->Nb];
       
   128     $this->debug = $debug;
       
   129   }
       
   130   
       
   131   // Error handler
       
   132   
       
   133   function trigger_error($text, $level = E_USER_NOTICE)
       
   134   {
       
   135     $bt = debug_backtrace();
       
   136     $lastfunc =& $bt[1];
       
   137     switch($level)
       
   138     {
       
   139       case E_USER_NOTICE:
       
   140       default:
       
   141         $desc = 'Notice';
       
   142         break;
       
   143       case E_USER_WARNING:
       
   144         $desc = 'Warning';
       
   145         break;
       
   146       case E_USER_ERROR:
       
   147         $desc = 'Fatal';
       
   148         break;
       
   149     }
       
   150     ob_start();
       
   151     if($this->debug || $level == E_USER_ERROR) echo "AES encryption: <b>{$desc}:</b> $text in {$lastfunc['file']} on line {$lastfunc['line']} in function {$lastfunc['function']}<br />";
       
   152     if($this->debug)
       
   153     {
       
   154       //echo '<pre>'.enano_debug_print_backtrace(true).'</pre>';
       
   155     }
       
   156     ob_end_flush();
       
   157     if($level == E_USER_ERROR)
       
   158     {
       
   159       echo '<p><b>This can sometimes happen if you are upgrading Enano to a new version and did not log out first.</b> <a href="'.$_SERVER['PHP_SELF'].'?do=diag&amp;sub=cookie_destroy">Click here</a> to force cookies to clear and try again. You will be logged out.</p>';
       
   160       exit;
       
   161     }
       
   162   }
       
   163   
       
   164   function array_slice_js_compat($array, $start, $finish = 0)
       
   165   {
       
   166     $len = $finish - $start;
       
   167     if($len < 0) $len = 0 - $len;
       
   168     //if($this->debug) echo (string)$len . ' ';
       
   169     //if(count($array) < $start + $len)
       
   170     //  $this->trigger_error('Index out of range', E_USER_WARNING);
       
   171     return array_slice($array, $start, $len);
       
   172   }
       
   173   
       
   174   function concat($s1, $s2)
       
   175   {
       
   176     if(is_array($s1) && is_array($s2))
       
   177       return array_merge($s1, $s2);
       
   178     elseif( ( is_array($s1) && !is_array($s2) ) || ( !is_array($s1) && is_array($s2) ) )
       
   179     {
       
   180       $this->trigger_error('incompatible types - you can\'t combine a non-array with an array', E_USER_WARNING);
       
   181       return false;
       
   182     }
       
   183     else
       
   184       return $s1 . $s2;
       
   185   }
       
   186   
       
   187   // This method circularly shifts the array left by the number of elements
       
   188   // given in its parameter. It returns the resulting array and is used for 
       
   189   // the ShiftRow step. Note that shift() and push() could be used for a more 
       
   190   // elegant solution, but they require IE5.5+, so I chose to do it manually. 
       
   191   
       
   192   function cyclicShiftLeft($theArray, $positions) {
       
   193     if(!is_int($positions))
       
   194     {
       
   195       $this->trigger_error('$positions is not an integer! Backtrace:<br /><pre>'.print_r(debug_backtrace(), true).'</pre>', E_USER_WARNING);
       
   196       return false;
       
   197     }
       
   198     $second = array_slice($theArray, 0, $positions);
       
   199     $first = array_slice($theArray, $positions);
       
   200     $theArray = array_merge($first, $second);
       
   201     return $theArray;
       
   202   }
       
   203   
       
   204   // Multiplies the element "poly" of GF(2^8) by x. See the Rijndael spec.
       
   205   
       
   206   function xtime($poly) {
       
   207     $poly <<= 1;
       
   208     return (($poly & 0x100) ? ($poly ^ 0x11B) : ($poly));
       
   209   }
       
   210   
       
   211   // Multiplies the two elements of GF(2^8) together and returns the result.
       
   212   // See the Rijndael spec, but should be straightforward: for each power of
       
   213   // the indeterminant that has a 1 coefficient in x, add y times that power
       
   214   // to the result. x and y should be bytes representing elements of GF(2^8)
       
   215   
       
   216   function mult_GF256($x, $y) {
       
   217     $result = 0;
       
   218     
       
   219     for ($bit = 1; $bit < 256; $bit *= 2, $y = $this->xtime($y)) {
       
   220       if ($x & $bit) 
       
   221         $result ^= $y;
       
   222     }
       
   223     return $result;
       
   224   }
       
   225   
       
   226   // Performs the substitution step of the cipher. State is the 2d array of
       
   227   // state information (see spec) and direction is string indicating whether
       
   228   // we are performing the forward substitution ("encrypt") or inverse 
       
   229   // substitution (anything else)
       
   230   
       
   231   function byteSub(&$state, $direction) {
       
   232     //global $this->SBox, $this->SBoxInverse, $this->Nb;
       
   233     if ($direction == "encrypt")           // Point S to the SBox we're using
       
   234       $S =& $this->SBox;
       
   235     else
       
   236       $S =& $this->SBoxInverse;
       
   237     for ($i = 0; $i < 4; $i++)           // Substitute for every byte in state
       
   238       for ($j = 0; $j < $this->Nb; $j++)
       
   239          $state[$i][$j] = $S[$state[$i][$j]];
       
   240   }
       
   241   
       
   242   // Performs the row shifting step of the cipher.
       
   243   
       
   244   function shiftRow(&$state, $direction) {
       
   245     //global $this->Nb, $this->shiftOffsets;
       
   246     for ($i=1; $i<4; $i++)               // Row 0 never shifts
       
   247       if ($direction == "encrypt")
       
   248          $state[$i] = $this->cyclicShiftLeft($state[$i], $this->shiftOffsets[$this->Nb][$i]);
       
   249       else
       
   250          $state[$i] = $this->cyclicShiftLeft($state[$i], $this->Nb - $this->shiftOffsets[$this->Nb][$i]);
       
   251   
       
   252   }
       
   253   
       
   254   // Performs the column mixing step of the cipher. Most of these steps can
       
   255   // be combined into table lookups on 32bit values (at least for encryption)
       
   256   // to greatly increase the speed. 
       
   257   
       
   258   function mixColumn(&$state, $direction) {
       
   259     //global $this->Nb;
       
   260     $b = Array();                                  // Result of matrix multiplications
       
   261     for ($j = 0; $j < $this->Nb; $j++) {                 // Go through each column...
       
   262       for ($i = 0; $i < 4; $i++) {                 // and for each row in the column...
       
   263         if ($direction == "encrypt")
       
   264           $b[$i] = $this->mult_GF256($state[$i][$j], 2) ^ // perform mixing
       
   265                    $this->mult_GF256($state[($i+1)%4][$j], 3) ^ 
       
   266                    $state[($i+2)%4][$j] ^ 
       
   267                    $state[($i+3)%4][$j];
       
   268         else 
       
   269           $b[$i] = $this->mult_GF256($state[$i][$j], 0xE) ^ 
       
   270                    $this->mult_GF256($state[($i+1)%4][$j], 0xB) ^
       
   271                    $this->mult_GF256($state[($i+2)%4][$j], 0xD) ^
       
   272                    $this->mult_GF256($state[($i+3)%4][$j], 9);
       
   273       }
       
   274       for ($i = 0; $i < 4; $i++)          // Place result back into column
       
   275         $state[$i][$j] = $b[$i];
       
   276     }
       
   277   }
       
   278   
       
   279   // Adds the current round key to the state information. Straightforward.
       
   280   
       
   281   function addRoundKey(&$state, $roundKey) {
       
   282     //global $this->Nb;
       
   283     for ($j = 0; $j < $this->Nb; $j++) {                      // Step through columns...
       
   284       $state[0][$j] ^= ( $roundKey[$j] & 0xFF);         // and XOR
       
   285       $state[1][$j] ^= (($roundKey[$j]>>8) & 0xFF);
       
   286       $state[2][$j] ^= (($roundKey[$j]>>16) & 0xFF);
       
   287       $state[3][$j] ^= (($roundKey[$j]>>24) & 0xFF);
       
   288     }
       
   289   }
       
   290   
       
   291   // This function creates the expanded key from the input (128/192/256-bit)
       
   292   // key. The parameter key is an array of bytes holding the value of the key.
       
   293   // The returned value is an array whose elements are the 32-bit words that 
       
   294   // make up the expanded key.
       
   295   
       
   296   function keyExpansion($key) {
       
   297     //global $this->keySizeInBits, $this->blockSizeInBits, $this->roundsArray, $this->Nk, $this->Nb, $this->Nr, $this->Nk, $this->SBox, $this->Rcon;
       
   298     $expandedKey = Array();
       
   299   
       
   300     // in case the key size or parameters were changed...
       
   301     $this->Nk = $this->keySizeInBits / 32;                   
       
   302     $this->Nb = $this->blockSizeInBits / 32;
       
   303     $this->Nr = $this->roundsArray[$this->Nk][$this->Nb];
       
   304   
       
   305     for ($j=0; $j < $this->Nk; $j++)     // Fill in input key first
       
   306       $expandedKey[$j] = 
       
   307         ($key[4*$j]) | ($key[4*$j+1]<<8) | ($key[4*$j+2]<<16) | ($key[4*$j+3]<<24);
       
   308   
       
   309     // Now walk down the rest of the array filling in expanded key bytes as
       
   310     // per Rijndael's spec
       
   311     for ($j = $this->Nk; $j < $this->Nb * ($this->Nr + 1); $j++) {    // For each word of expanded key
       
   312       $temp = $expandedKey[$j - 1];
       
   313       if ($j % $this->Nk == 0) 
       
   314         $temp = ( ($this->SBox[($temp>>8) & 0xFF]) |
       
   315                   ($this->SBox[($temp>>16) & 0xFF]<<8) |
       
   316                   ($this->SBox[($temp>>24) & 0xFF]<<16) |
       
   317                   ($this->SBox[$temp & 0xFF]<<24) ) ^ $this->Rcon[floor($j / $this->Nk) - 1];
       
   318       elseif  ($this->Nk > 6 && $j % $this->Nk == 4)
       
   319         $temp = ($this->SBox[($temp>>24) & 0xFF]<<24) |
       
   320                ($this->SBox[($temp>>16) & 0xFF]<<16) |
       
   321                ($this->SBox[($temp>>8) & 0xFF]<<8) |
       
   322                ($this->SBox[ $temp & 0xFF]);
       
   323       $expandedKey[$j] = $expandedKey[$j-$this->Nk] ^ $temp;
       
   324     }
       
   325     return $expandedKey;
       
   326   }
       
   327   
       
   328   // Rijndael's round functions... 
       
   329   
       
   330   function RijndaelRound(&$state, $roundKey) {
       
   331     $this->byteSub($state, "encrypt");
       
   332     $this->shiftRow($state, "encrypt");
       
   333     $this->mixColumn($state, "encrypt");
       
   334     $this->addRoundKey($state, $roundKey);
       
   335   }
       
   336   
       
   337   function InverseRijndaelRound(&$state, $roundKey) {
       
   338     $this->addRoundKey($state, $roundKey);
       
   339     $this->mixColumn($state, "decrypt");
       
   340     $this->shiftRow($state, "decrypt");
       
   341     $this->byteSub($state, "decrypt");
       
   342   }
       
   343   
       
   344   function FinalRijndaelRound(&$state, $roundKey) {
       
   345     $this->byteSub($state, "encrypt");
       
   346     $this->shiftRow($state, "encrypt");
       
   347     $this->addRoundKey($state, $roundKey);
       
   348   }
       
   349   
       
   350   function InverseFinalRijndaelRound(&$state, $roundKey){
       
   351     $this->addRoundKey($state, $roundKey);
       
   352     $this->shiftRow($state, "decrypt");
       
   353     $this->byteSub($state, "decrypt");  
       
   354   }
       
   355   
       
   356   // encrypt is the basic encryption function. It takes parameters
       
   357   // block, an array of bytes representing a plaintext block, and expandedKey,
       
   358   // an array of words representing the expanded key previously returned by
       
   359   // keyExpansion(). The ciphertext block is returned as an array of bytes.
       
   360   
       
   361   function cryptBlock($block, $expandedKey) {
       
   362     //global $this->blockSizeInBits, $this->Nb, $this->Nr;
       
   363     $t=count($block)*8;
       
   364     if (!is_array($block) || count($block)*8 != $this->blockSizeInBits)
       
   365     {
       
   366       $this->trigger_error('block is bad or block size is wrong<pre>'.print_r($block, true).'</pre><p>Aiming for size '.$this->blockSizeInBits.', got '.$t.'.', E_USER_WARNING); 
       
   367       return false;
       
   368     }
       
   369     if (!$expandedKey)
       
   370       return;
       
   371   
       
   372     $block = $this->packBytes($block);
       
   373     $this->addRoundKey($block, $expandedKey);
       
   374     for ($i=1; $i<$this->Nr; $i++) 
       
   375       $this->RijndaelRound($block, $this->array_slice_js_compat($expandedKey, $this->Nb*$i, $this->Nb*($i+1)));
       
   376     $this->FinalRijndaelRound($block, $this->array_slice_js_compat($expandedKey, $this->Nb*$this->Nr));
       
   377     $ret = $this->unpackBytes($block);
       
   378     return $ret;
       
   379   }
       
   380   
       
   381   // decrypt is the basic decryption function. It takes parameters
       
   382   // block, an array of bytes representing a ciphertext block, and expandedKey,
       
   383   // an array of words representing the expanded key previously returned by
       
   384   // keyExpansion(). The decrypted block is returned as an array of bytes.
       
   385   
       
   386   function unCryptBlock($block, $expandedKey) {
       
   387     $t = count($block)*8;
       
   388     if (!is_array($block) || count($block)*8 != $this->blockSizeInBits)
       
   389     {
       
   390       $this->trigger_error('$block is not a valid rijndael-block array: '.$this->byteArrayToHex($block).'<pre>'.print_r($block, true).'</pre><p>Block size is '.$t.', should be '.$this->blockSizeInBits.'</p>', E_USER_WARNING);
       
   391       return false;
       
   392     }
       
   393     if (!$expandedKey)
       
   394     {
       
   395       $this->trigger_error('$expandedKey is invalid', E_USER_WARNING);
       
   396       return false;
       
   397     }
       
   398   
       
   399     $block = $this->packBytes($block);
       
   400     $this->InverseFinalRijndaelRound($block, $this->array_slice_js_compat($expandedKey, $this->Nb*$this->Nr)); 
       
   401     for ($i = $this->Nr - 1; $i>0; $i--) 
       
   402     {
       
   403       $this->InverseRijndaelRound($block, $this->array_slice_js_compat($expandedKey, $this->Nb*$i, $this->Nb*($i+1)));
       
   404     }
       
   405     $this->addRoundKey($block, $expandedKey);
       
   406     $ret = $this->unpackBytes($block);
       
   407     if(!is_array($ret))
       
   408     {
       
   409       $this->trigger_error('$ret is not an array', E_USER_WARNING);
       
   410     }
       
   411     return $ret;
       
   412   }
       
   413   
       
   414   // This method takes a byte array (byteArray) and converts it to a string by
       
   415   // applying String.fromCharCode() to each value and concatenating the result.
       
   416   // The resulting string is returned. Note that this function SKIPS zero bytes
       
   417   // under the assumption that they are padding added in formatPlaintext().
       
   418   // Obviously, do not invoke this method on raw data that can contain zero
       
   419   // bytes. It is really only appropriate for printable ASCII/Latin-1 
       
   420   // values. Roll your own function for more robust functionality :)
       
   421   
       
   422   function byteArrayToString($byteArray) {
       
   423     $result = "";
       
   424     for($i=0; $i<count($byteArray); $i++)
       
   425       if ($byteArray[$i] != 0) 
       
   426         $result .= chr($byteArray[$i]);
       
   427     return $result;
       
   428   }
       
   429   
       
   430   // This function takes an array of bytes (byteArray) and converts them
       
   431   // to a hexadecimal string. Array element 0 is found at the beginning of 
       
   432   // the resulting string, high nibble first. Consecutive elements follow
       
   433   // similarly, for example [16, 255] --> "10ff". The function returns a 
       
   434   // string.
       
   435   
       
   436   /*
       
   437   function byteArrayToHex($byteArray) {
       
   438     $result = "";
       
   439     if (!$byteArray)
       
   440       return;
       
   441     for ($i=0; $i<count($byteArray); $i++)
       
   442       $result .= (($byteArray[$i]<16) ? "0" : "") + toString($byteArray[$i]); // magic number here is 16, not sure how to handle this...
       
   443   
       
   444     return $result;
       
   445   }
       
   446   */
       
   447   function byteArrayToHex($arr)
       
   448   {
       
   449     $ret = '';
       
   450     foreach($arr as $a)
       
   451     {
       
   452       $nibble = (string)dechex(intval($a));
       
   453       if(strlen($nibble) == 1) $nibble = '0' . $nibble;
       
   454       $ret .= $nibble;
       
   455     }
       
   456     return $ret;
       
   457   }
       
   458   
       
   459   // PHP equivalent of Javascript's toString()
       
   460   function toString($bool)
       
   461   {
       
   462     if(is_bool($bool))
       
   463       return ($bool) ? 'true' : 'false';
       
   464     elseif(is_array($bool))
       
   465       return implode(',', $bool);
       
   466     else
       
   467       return (string)$bool;
       
   468   }
       
   469   
       
   470   // This function converts a string containing hexadecimal digits to an 
       
   471   // array of bytes. The resulting byte array is filled in the order the
       
   472   // values occur in the string, for example "10FF" --> [16, 255]. This
       
   473   // function returns an array. 
       
   474   
       
   475   /*
       
   476   function hexToByteArray($hexString) {
       
   477     $byteArray = Array();
       
   478     if (strlen($hexString) % 2)             // must have even length
       
   479       return;
       
   480     if (strstr($hexString, "0x") == $hexString || strstr($hexString, "0X") == $hexString)
       
   481       $hexString = substr($hexString, 2);
       
   482     for ($i = 0; $i<strlen($hexString); $i++,$i++) 
       
   483       $byteArray[floor($i/2)] = intval(substr($hexString, $i, 2)); // again, that strange magic number: 16
       
   484     return $byteArray;
       
   485   }
       
   486   */
       
   487   function hexToByteArray($str)
       
   488   {
       
   489     if(substr($str, 0, 2) == '0x' || substr($str, 0, 2) == '0X')
       
   490       $str = substr($str, 2);
       
   491     $arr = Array();
       
   492     $str = $this->enano_str_split($str, 2);
       
   493     foreach($str as $s)
       
   494     {
       
   495       $arr[] = intval(hexdec($s));
       
   496     }
       
   497     return $arr;
       
   498   }
       
   499   
       
   500   // This function packs an array of bytes into the four row form defined by
       
   501   // Rijndael. It assumes the length of the array of bytes is divisible by
       
   502   // four. Bytes are filled in according to the Rijndael spec (starting with
       
   503   // column 0, row 0 to 3). This function returns a 2d array.
       
   504   
       
   505   function packBytes($octets) {
       
   506     $state = Array();
       
   507     if (!$octets || count($octets) % 4)
       
   508       return;
       
   509   
       
   510     $state[0] = Array(); $state[1] = Array(); 
       
   511     $state[2] = Array(); $state[3] = Array();
       
   512     for ($j=0; $j<count($octets); $j = $j+4) {
       
   513        $state[0][$j/4] = $octets[$j];
       
   514        $state[1][$j/4] = $octets[$j+1];
       
   515        $state[2][$j/4] = $octets[$j+2];
       
   516        $state[3][$j/4] = $octets[$j+3];
       
   517     }
       
   518     return $state;
       
   519   }
       
   520   
       
   521   // This function unpacks an array of bytes from the four row format preferred
       
   522   // by Rijndael into a single 1d array of bytes. It assumes the input "packed"
       
   523   // is a packed array. Bytes are filled in according to the Rijndael spec. 
       
   524   // This function returns a 1d array of bytes.
       
   525   
       
   526   function unpackBytes($packed) {
       
   527     $result = Array();
       
   528     for ($j=0; $j<count($packed[0]); $j++) {
       
   529       $result[] = $packed[0][$j];
       
   530       $result[] = $packed[1][$j];
       
   531       $result[] = $packed[2][$j];
       
   532       $result[] = $packed[3][$j];
       
   533     }
       
   534     return $result;
       
   535   }
       
   536   
       
   537   function charCodeAt($str, $i)
       
   538   {
       
   539     return ord(substr($str, $i, 1));
       
   540   }
       
   541   
       
   542   function fromCharCode($str)
       
   543   {
       
   544     return chr($str);
       
   545   }
       
   546   
       
   547   // This function takes a prospective plaintext (string or array of bytes)
       
   548   // and pads it with zero bytes if its length is not a multiple of the block 
       
   549   // size. If plaintext is a string, it is converted to an array of bytes
       
   550   // in the process. The type checking can be made much nicer using the 
       
   551   // instanceof operator, but this operator is not available until IE5.0 so I 
       
   552   // chose to use the heuristic below. 
       
   553   
       
   554   function formatPlaintext($plaintext) {
       
   555     //global $this->blockSizeInBits;
       
   556     $bpb = $this->blockSizeInBits / 8;               // bytes per block
       
   557   
       
   558     // if primitive string or String instance
       
   559     if (is_string($plaintext)) {
       
   560       $plaintext = $this->enano_str_split($plaintext);
       
   561       // Unicode issues here (ignoring high byte)
       
   562       for ($i=0; $i<sizeof($plaintext); $i++)
       
   563         $plaintext[$i] = $this->charCodeAt($plaintext[$i], 0) & 0xFF;
       
   564     } 
       
   565   
       
   566     for ($i = $bpb - (sizeof($plaintext) % $bpb); $i > 0 && $i < $bpb; $i--) 
       
   567       $plaintext[] = 0;
       
   568     
       
   569     return $plaintext;
       
   570   }
       
   571   
       
   572   // Returns an array containing "howMany" random bytes. YOU SHOULD CHANGE THIS
       
   573   // TO RETURN HIGHER QUALITY RANDOM BYTES IF YOU ARE USING THIS FOR A "REAL"
       
   574   // APPLICATION. (edit: done, mt_rand() is relatively secure)
       
   575   
       
   576   function getRandomBytes($howMany) {
       
   577     $bytes = Array();
       
   578     for ($i=0; $i<$howMany; $i++)
       
   579       $bytes[$i] = mt_rand(0, 255);
       
   580     return $bytes;
       
   581   }
       
   582   
       
   583   // rijndaelEncrypt(plaintext, key, mode)
       
   584   // Encrypts the plaintext using the given key and in the given mode. 
       
   585   // The parameter "plaintext" can either be a string or an array of bytes. 
       
   586   // The parameter "key" must be an array of key bytes. If you have a hex 
       
   587   // string representing the key, invoke hexToByteArray() on it to convert it 
       
   588   // to an array of bytes. The third parameter "mode" is a string indicating
       
   589   // the encryption mode to use, either "ECB" or "CBC". If the parameter is
       
   590   // omitted, ECB is assumed.
       
   591   // 
       
   592   // An array of bytes representing the cihpertext is returned. To convert 
       
   593   // this array to hex, invoke byteArrayToHex() on it. If you are using this 
       
   594   // "for real" it is a good idea to change the function getRandomBytes() to 
       
   595   // something that returns truly random bits.
       
   596   
       
   597   function rijndaelEncrypt($plaintext, $key, $mode = 'ECB') {
       
   598     //global $this->blockSizeInBits, $this->keySizeInBits;
       
   599     $bpb = $this->blockSizeInBits / 8;          // bytes per block
       
   600     // var ct;                                 // ciphertext
       
   601   
       
   602     if($mode == 'CBC')
       
   603     {
       
   604       if (!is_string($plaintext) || !is_array($key))
       
   605       {
       
   606         $this->trigger_error('In CBC mode the first and second parameters should be strings', E_USER_WARNING);
       
   607         return false;
       
   608       }
       
   609     } else {
       
   610       if (!is_array($plaintext) || !is_array($key))
       
   611       {
       
   612         $this->trigger_error('In ECB mode the first and second parameters should be byte arrays', E_USER_WARNING);
       
   613         return false;
       
   614       }
       
   615     }
       
   616     if (sizeof($key)*8 != $this->keySizeInBits)
       
   617     {
       
   618       $this->trigger_error('The key needs to be '. ( $this->keySizeInBits / 8 ) .' bytes in length', E_USER_WARNING);
       
   619       return false;
       
   620     }
       
   621     if ($mode == "CBC")
       
   622       $ct = $this->getRandomBytes($bpb);             // get IV
       
   623     else {
       
   624       $mode = "ECB";
       
   625       $ct = Array();
       
   626     }
       
   627     
       
   628     // convert plaintext to byte array and pad with zeros if necessary. 
       
   629     $plaintext = $this->formatPlaintext($plaintext);
       
   630     
       
   631     $expandedKey = $this->keyExpansion($key);
       
   632     
       
   633     for ($block=0; $block<sizeof($plaintext) / $bpb; $block++) {
       
   634       $aBlock = $this->array_slice_js_compat($plaintext, $block*$bpb, ($block+1)*$bpb);
       
   635       if ($mode == "CBC")
       
   636       {
       
   637         for ($i=0; $i<$bpb; $i++)
       
   638         {
       
   639           $aBlock[$i] ^= $ct[$block*$bpb + $i];
       
   640         }
       
   641       }
       
   642       $cp = $this->cryptBlock($aBlock, $expandedKey);
       
   643       $ct = $this->concat($ct, $cp);
       
   644     }
       
   645   
       
   646     return $ct;
       
   647   }
       
   648   
       
   649   // rijndaelDecrypt(ciphertext, key, mode)
       
   650   // Decrypts the using the given key and mode. The parameter "ciphertext" 
       
   651   // must be an array of bytes. The parameter "key" must be an array of key 
       
   652   // bytes. If you have a hex string representing the ciphertext or key, 
       
   653   // invoke hexToByteArray() on it to convert it to an array of bytes. The
       
   654   // parameter "mode" is a string, either "CBC" or "ECB".
       
   655   // 
       
   656   // An array of bytes representing the plaintext is returned. To convert 
       
   657   // this array to a hex string, invoke byteArrayToHex() on it. To convert it 
       
   658   // to a string of characters, you can use byteArrayToString().
       
   659   
       
   660   function rijndaelDecrypt($ciphertext, $key, $mode = 'ECB') {
       
   661     //global $this->blockSizeInBits, $this->keySizeInBits;
       
   662     $bpb = $this->blockSizeInBits / 8;          // bytes per block
       
   663     $pt = Array();                   // plaintext array
       
   664     // $aBlock;                             // a decrypted block
       
   665     // $block;                              // current block number
       
   666   
       
   667     if (!$ciphertext)
       
   668     {
       
   669       $this->trigger_error('$ciphertext should be a byte array', E_USER_WARNING);
       
   670       return false;
       
   671     }
       
   672     if(  !is_array($key) )
       
   673     {
       
   674       $this->trigger_error('$key should be a byte array', E_USER_WARNING);
       
   675       return false;
       
   676     }
       
   677     if( is_string($ciphertext) )
       
   678     {
       
   679       $this->trigger_error('$ciphertext should be a byte array', E_USER_WARNING);
       
   680       return false;
       
   681     }
       
   682     if (sizeof($key)*8 != $this->keySizeInBits)
       
   683     {
       
   684       $this->trigger_error('Encryption key is the wrong length', E_USER_WARNING);
       
   685       return false;
       
   686     }
       
   687     if (!$mode)
       
   688       $mode = "ECB";                         // assume ECB if mode omitted
       
   689   
       
   690     $expandedKey = $this->keyExpansion($key);
       
   691    
       
   692     // work backwards to accomodate CBC mode 
       
   693     for ($block=(sizeof($ciphertext) / $bpb)-1; $block>0; $block--)
       
   694     {
       
   695       if( ( $block*$bpb ) + ( ($block+1)*$bpb ) > count($ciphertext) )
       
   696       {
       
   697         //$this->trigger_error('$ciphertext index out of bounds', E_USER_ERROR);
       
   698       }
       
   699       $current_block = $this->array_slice_js_compat($ciphertext, $block*$bpb, ($block+1)*$bpb);
       
   700       if(count($current_block) * 8 != $this->blockSizeInBits)
       
   701       {
       
   702         // $c=count($current_block)*8;
       
   703         // $this->trigger_error('We got a '.$c.'-bit block, instead of '.$this->blockSizeInBits.'', E_USER_ERROR);
       
   704       }
       
   705       $aBlock = $this->uncryptBlock($current_block, $expandedKey);
       
   706       if(!$aBlock)
       
   707       {
       
   708         $this->trigger_error('Shared block decryption routine returned false', E_USER_WARNING);
       
   709         return false;
       
   710       }
       
   711       if ($mode == "CBC")
       
   712         for ($i=0; $i<$bpb; $i++) 
       
   713           $pt[($block-1)*$bpb + $i] = $aBlock[$i] ^ $ciphertext[($block-1)*$bpb + $i];
       
   714       else
       
   715         $pt = $this->concat($aBlock, $pt);
       
   716     }
       
   717   
       
   718     // do last block if ECB (skips the IV in CBC)
       
   719     if ($mode == "ECB")
       
   720     {
       
   721       $x = $this->uncryptBlock($this->array_slice_js_compat($ciphertext, 0, $bpb), $expandedKey);
       
   722       if(!$x)
       
   723       {
       
   724         $this->trigger_error('ECB block decryption routine returned false', E_USER_WARNING);
       
   725         return false;
       
   726       }
       
   727       $pt = $this->concat($x, $pt);
       
   728       if(!$pt)
       
   729       {
       
   730         $this->trigger_error('ECB concatenation routine returned false', E_USER_WARNING);
       
   731         return false;
       
   732       }
       
   733     }
       
   734   
       
   735     return $pt;
       
   736   }
       
   737   
       
   738   /**
       
   739    * Wrapper for encryption.
       
   740    * @param string $text the text to encrypt
       
   741    * @param string $key the raw binary key to encrypt with
       
   742    * @param int $return_encoding optional - can be ENC_BINARY, ENC_HEX or ENC_BASE64
       
   743    */
       
   744    
       
   745   function encrypt($text, $key, $return_encoding = ENC_HEX)
       
   746   {
       
   747     if ( $this->mcrypt && $this->blockSizeInBits == mcrypt_module_get_algo_block_size(eval('return MCRYPT_RIJNDAEL_'.$this->keySizeInBits.';')) )
       
   748     {
       
   749       $iv_size = mcrypt_get_iv_size($this->mcrypt, MCRYPT_MODE_ECB);
       
   750       $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
       
   751       $cryptext = mcrypt_encrypt($this->mcrypt, $key, $text, MCRYPT_MODE_ECB, $iv);
       
   752       switch($return_encoding)
       
   753       {
       
   754         case ENC_HEX:
       
   755         default:
       
   756           $cryptext = $this->strtohex($cryptext);
       
   757           break;
       
   758         case ENC_BINARY:
       
   759           $cryptext = $cryptext;
       
   760           break;
       
   761         case ENC_BASE64:
       
   762           $cryptext = base64_encode($cryptext);
       
   763           break;
       
   764       }
       
   765     }
       
   766     else
       
   767     {
       
   768       $key = $this->prepare_string($key);
       
   769       $text = $this->prepare_string($text);
       
   770       $cryptext = $this->rijndaelEncrypt($text, $key, 'ECB');
       
   771       if(!is_array($cryptext))
       
   772       {
       
   773         echo 'Warning: encryption failed for string: '.$text.'<br />';
       
   774         return false;
       
   775       }
       
   776       switch($return_encoding)
       
   777       {
       
   778         case ENC_HEX:
       
   779         default:
       
   780           $cryptext = $this->byteArrayToHex($cryptext);
       
   781           break;
       
   782         case ENC_BINARY:
       
   783           $cryptext = $this->byteArrayToString($cryptext);
       
   784           break;
       
   785         case ENC_BASE64:
       
   786           $cryptext = base64_encode($this->byteArrayToString($cryptext));
       
   787           break;
       
   788       }
       
   789     }
       
   790     return $cryptext;
       
   791   }
       
   792   
       
   793   /**
       
   794    * Wrapper for decryption.
       
   795    * @param string $text the encrypted text
       
   796    * @param string $key the raw binary key used to encrypt the text
       
   797    * @param int $input_encoding the encoding used for the encrypted string. Can be ENC_BINARY, ENC_HEX, or ENC_BASE64.
       
   798    * @return string
       
   799    */
       
   800    
       
   801   function decrypt($text, $key, $input_encoding = ENC_HEX)
       
   802   {
       
   803     switch($input_encoding)
       
   804     {
       
   805       case ENC_BINARY:
       
   806       default:
       
   807         break;
       
   808       case ENC_HEX:
       
   809         $text = $this->hextostring($text);
       
   810         break;
       
   811       case ENC_BASE64:
       
   812         $text = base64_decode($text);
       
   813         break;
       
   814     }
       
   815     //$mod = strlen($text) % $this->blockSizeInBits;
       
   816     //if($mod != 96)
       
   817       //die('modulus check failed: '.$mod);
       
   818     if ( $this->mcrypt )
       
   819     {
       
   820       $iv_size = mcrypt_get_iv_size($this->mcrypt, MCRYPT_MODE_ECB);
       
   821       $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
       
   822       $dypt = mcrypt_decrypt($this->mcrypt, $key, $text, MCRYPT_MODE_ECB, $iv);
       
   823     }
       
   824     else
       
   825     {
       
   826       $etext = $this->prepare_string($text);
       
   827       $ekey  = $this->prepare_string($key);
       
   828       $mod = count($etext) % $this->blockSizeInBits;
       
   829       $dypt = $this->rijndaelDecrypt($etext, $ekey, 'ECB');
       
   830       if(!$dypt)
       
   831       {
       
   832         echo '<pre>'.print_r($dypt, true).'</pre>';
       
   833         $this->trigger_error('Rijndael main decryption routine failed', E_USER_ERROR);
       
   834       }
       
   835       $dypt = $this->byteArrayToString($dypt);
       
   836     }
       
   837     return $dypt;
       
   838   }
       
   839   
       
   840   /**
       
   841    * Enano-ese equivalent of str_split() which is only found in PHP5
       
   842    * @param $text string the text to split
       
   843    * @param $inc int size of each block
       
   844    * @return array
       
   845    */
       
   846    
       
   847   function enano_str_split($text, $inc = 1)
       
   848   {
       
   849     if($inc < 1) return false;
       
   850     if($inc >= strlen($text)) return Array($text);
       
   851     $len = ceil(strlen($text) / $inc);
       
   852     $ret = Array();
       
   853     for($i=0;$i<strlen($text);$i=$i+$inc)
       
   854     {
       
   855       $ret[] = substr($text, $i, $inc);
       
   856     }
       
   857     return $ret;
       
   858   }
       
   859   
       
   860   /**
       
   861    * Generates a random key suitable for encryption
       
   862    * @param int $len the length of the key, in bytes
       
   863    * @return string a BINARY key
       
   864    */
       
   865   
       
   866   function randkey($len = 32)
       
   867   {
       
   868     $key = '';
       
   869     for($i=0;$i<$len;$i++)
       
   870     {
       
   871       $key .= chr(mt_rand(0, 255));
       
   872     }
       
   873     return $key;
       
   874   }
       
   875   
       
   876   /*
       
   877   function byteArrayToString($arr)
       
   878   {
       
   879     if(!is_array($arr))
       
   880     {
       
   881       $this->trigger_error('First parameter should be an array', E_USER_WARNING);
       
   882       return false;
       
   883     }
       
   884     $ret = '';
       
   885     foreach($arr as $a)
       
   886     {
       
   887       if($a != 0) $ret .= chr($a);
       
   888     }
       
   889     return $ret;
       
   890   }
       
   891   */
       
   892   
       
   893   function strtohex($str)
       
   894   {
       
   895     $str = $this->enano_str_split($str);
       
   896     $ret = '';
       
   897     foreach($str as $s)
       
   898     {
       
   899       $chr = dechex(ord($s));
       
   900       if(strlen($chr) < 2) $chr = '0' . $chr;
       
   901       $ret .= $chr;
       
   902     }
       
   903     return $ret;
       
   904   }
       
   905   
       
   906   function gen_readymade_key()
       
   907   {
       
   908     $key = $this->strtohex($this->randkey($this->keySizeInBits / 8));
       
   909     return $key;
       
   910   }
       
   911   
       
   912   function prepare_string($text)
       
   913   {
       
   914     $ret = $this->hexToByteArray($this->strtohex($text));
       
   915     if(count($ret) != strlen($text))
       
   916       die('problem seems to be the hex conversion');
       
   917     return $ret;
       
   918   }
       
   919   
       
   920   /**
       
   921    * Decodes a hex string.
       
   922    * @param string $hex The hex code to decode
       
   923    * @return string
       
   924    */
       
   925   
       
   926   function hextostring($hex)
       
   927   {
       
   928     $hex = $this->enano_str_split($hex, 2);
       
   929     $bin_key = '';
       
   930     foreach($hex as $nibble)
       
   931     {
       
   932       $byte = chr(hexdec($nibble));
       
   933       $bin_key .= $byte;
       
   934     }
       
   935     return $bin_key;
       
   936   }
       
   937 }
       
   938 
       
   939 /**
       
   940  * XXTEA encryption arithmetic library.
       
   941  *
       
   942  * Copyright (C) 2006 Ma Bingyao <andot@ujn.edu.cn>
       
   943  * Version:      1.5
       
   944  * LastModified: Dec 5, 2006
       
   945  * This library is free.  You can redistribute it and/or modify it.
       
   946  * 
       
   947  * From dandaman32: I am treating this code as GPL, as implied by the license statement above.
       
   948  */
       
   949 class TEACrypt extends AESCrypt {
       
   950   function long2str($v, $w) {
       
   951       $len = count($v);
       
   952       $n = ($len - 1) << 2;
       
   953       if ($w) {
       
   954           $m = $v[$len - 1];
       
   955           if (($m < $n - 3) || ($m > $n)) return false;
       
   956           $n = $m;
       
   957       }
       
   958       $s = array();
       
   959       for ($i = 0; $i < $len; $i++) {
       
   960           $s[$i] = pack("V", $v[$i]);
       
   961       }
       
   962       if ($w) {
       
   963           return substr(join('', $s), 0, $n);
       
   964       }
       
   965       else {
       
   966           return join('', $s);
       
   967       }
       
   968   }
       
   969    
       
   970   function str2long($s, $w) {
       
   971       $v = unpack("V*", $s. str_repeat("\0", (4 - strlen($s) % 4) & 3));
       
   972       $v = array_values($v);
       
   973       if ($w) {
       
   974           $v[count($v)] = strlen($s);
       
   975       }
       
   976       return $v;
       
   977   }
       
   978    
       
   979   function int32($n) {
       
   980       while ($n >= 2147483648) $n -= 4294967296;
       
   981       while ($n <= -2147483649) $n += 4294967296;
       
   982       return (int)$n;
       
   983   }
       
   984    
       
   985   function encrypt($str, $key) {
       
   986       if ($str == "") {
       
   987           return "";
       
   988       }
       
   989       $v = $this->str2long($str, true);
       
   990       $k = $this->str2long($key, false);
       
   991       if (count($k) < 4) {
       
   992           for ($i = count($k); $i < 4; $i++) {
       
   993               $k[$i] = 0;
       
   994           }
       
   995       }
       
   996       $n = count($v) - 1;
       
   997    
       
   998       $z = $v[$n];
       
   999       $y = $v[0];
       
  1000       $delta = 0x9E3779B9;
       
  1001       $q = floor(6 + 52 / ($n + 1));
       
  1002       $sum = 0;
       
  1003       while (0 < $q--) {
       
  1004           $sum = $this->int32($sum + $delta);
       
  1005           $e = $sum >> 2 & 3;
       
  1006           for ($p = 0; $p < $n; $p++) {
       
  1007               $y = $v[$p + 1];
       
  1008               $mx = $this->int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ $this->int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
       
  1009               $z = $v[$p] = $this->int32($v[$p] + $mx);
       
  1010           }
       
  1011           $y = $v[0];
       
  1012           $mx = $this->int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ $this->int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
       
  1013           $z = $v[$n] = $this->int32($v[$n] + $mx);
       
  1014       }
       
  1015       return $this->long2str($v, false);
       
  1016   }
       
  1017    
       
  1018   function decrypt($str, $key) {
       
  1019       if ($str == "") {
       
  1020           return "";
       
  1021       }
       
  1022       $v = $this->str2long($str, false);
       
  1023       $k = $this->str2long($key, false);
       
  1024       if (count($k) < 4) {
       
  1025           for ($i = count($k); $i < 4; $i++) {
       
  1026               $k[$i] = 0;
       
  1027           }
       
  1028       }
       
  1029       $n = count($v) - 1;
       
  1030    
       
  1031       $z = $v[$n];
       
  1032       $y = $v[0];
       
  1033       $delta = 0x9E3779B9;
       
  1034       $q = floor(6 + 52 / ($n + 1));
       
  1035       $sum = $this->int32($q * $delta);
       
  1036       while ($sum != 0) {
       
  1037           $e = $sum >> 2 & 3;
       
  1038           for ($p = $n; $p > 0; $p--) {
       
  1039               $z = $v[$p - 1];
       
  1040               $mx = $this->int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ $this->int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
       
  1041               $y = $v[$p] = $this->int32($v[$p] - $mx);
       
  1042           }
       
  1043           $z = $v[$n];
       
  1044           $mx = $this->int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ $this->int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
       
  1045           $y = $v[0] = $this->int32($v[0] - $mx);
       
  1046           $sum = $this->int32($sum - $delta);
       
  1047       }
       
  1048       return $this->long2str($v, true);
       
  1049   }
       
  1050 }
       
  1051 
       
  1052 ?>