XXTEA.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * PHP implementation of XXTEA encryption algorithm.
  5. *
  6. * XXTEA is a secure and fast encryption algorithm, suitable for web
  7. * development.
  8. *
  9. * PHP versions 4 and 5
  10. *
  11. * LICENSE: This library is free software; you can redistribute it
  12. * and/or modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation; either
  14. * version 2.1 of the License, or (at your option) any later version.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  24. * MA 02110-1301 USA.
  25. *
  26. * @category Encryption
  27. * @package Crypt_XXTEA
  28. * @author Wudi Liu <[email protected]>
  29. * @author Ma Bingyao <[email protected]>
  30. * @copyright 2005-2008 Coolcode.CN
  31. * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
  32. * @version CVS: $Id: XXTEA.php,v 1.3 2008/03/06 11:38:45 wudicgi Exp $
  33. * @link http://pear.php.net/package/Crypt_XXTEA
  34. */
  35. /**
  36. * Needed for error handling
  37. */
  38. require_once 'PEAR.php';
  39. // {{{ constants
  40. define('CRYPT_XXTEA_DELTA', 0x9E3779B9);
  41. // }}}
  42. /**
  43. * The main class
  44. *
  45. * @category Encryption
  46. * @package Crypt_XXTEA
  47. * @author Wudi Liu <[email protected]>
  48. * @author Ma Bingyao <[email protected]>
  49. * @copyright 2005-2008 Coolcode.CN
  50. * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
  51. * @version Release: 0.9.0
  52. * @link http://pear.php.net/package/Crypt_XXTEA
  53. */
  54. class Crypt_XXTEA {
  55. // {{{ properties
  56. /**
  57. * The long integer array of secret key
  58. *
  59. * @access private
  60. *
  61. * @var array
  62. */
  63. var $_key;
  64. // }}}
  65. // {{{ setKey()
  66. /**
  67. * Sets the secret key
  68. *
  69. * The key must be non-empty, and not more than 16 characters or 4 long values
  70. *
  71. * @access public
  72. *
  73. * @param mixed $key the secret key (string or long integer array)
  74. *
  75. * @return bool true on success, PEAR_Error on failure
  76. */
  77. function setKey($key) {
  78. if (is_string($key)) {
  79. $k = $this->_str2long($key, false);
  80. } elseif (is_array($key)) {
  81. $k = $key;
  82. } else {
  83. return PEAR::raiseError('The secret key must be a string or long integer array.');
  84. }
  85. if (count($k) > 4) {
  86. return PEAR::raiseError('The secret key cannot be more than 16 characters or 4 long values.');
  87. } elseif (count($k) == 0) {
  88. return PEAR::raiseError('The secret key cannot be empty.');
  89. } elseif (count($k) < 4) {
  90. for ($i = count($k); $i < 4; $i++) {
  91. $k[$i] = 0;
  92. }
  93. }
  94. $this->_key = $k;
  95. return true;
  96. }
  97. // }}}
  98. // {{{ encrypt()
  99. /**
  100. * Encrypts a plain text
  101. *
  102. * As the XXTEA encryption algorithm is designed for encrypting and decrypting
  103. * the long integer array type of data, there is not a standard that defines
  104. * how to convert between long integer array and text or binary data for it.
  105. * So this package provides the ability to encrypt and decrypt the long integer
  106. * arrays directly to satisfy the requirement for working with other
  107. * implementations. And at the same time, for convenience, it also provides
  108. * the ability to process strings, which uses its own method to group the text
  109. * into array.
  110. *
  111. * @access public
  112. *
  113. * @param mixed $plaintext the plain text (string or long integer array)
  114. *
  115. * @return mixed the cipher text as the same type as the parameter $plaintext
  116. * on success, PEAR_Error on failure
  117. */
  118. function encrypt($plaintext) {
  119. if ($this->_key == null) {
  120. return PEAR::raiseError('Secret key is undefined.');
  121. }
  122. if (is_string($plaintext)) {
  123. return $this->_encryptString($plaintext);
  124. } elseif (is_array($plaintext)) {
  125. return $this->_encryptArray($plaintext);
  126. } else {
  127. return PEAR::raiseError('The plain text must be a string or long integer array.');
  128. }
  129. }
  130. // }}}
  131. // {{{ decrypt()
  132. /**
  133. * Decrypts a cipher text
  134. *
  135. * @access public
  136. *
  137. * @param mixed $chipertext the cipher text (string or long integer array)
  138. *
  139. * @return mixed the plain text as the same type as the parameter $chipertext
  140. * on success, PEAR_Error on failure
  141. */
  142. function decrypt($chipertext) {
  143. if ($this->_key == null) {
  144. return PEAR::raiseError('Secret key is undefined.');
  145. }
  146. if (is_string($chipertext)) {
  147. return $this->_decryptString($chipertext);
  148. } elseif (is_array($chipertext)) {
  149. return $this->_decryptArray($chipertext);
  150. } else {
  151. return PEAR::raiseError('The chiper text must be a string or long integer array.');
  152. }
  153. }
  154. // }}}
  155. // {{{ _encryptString()
  156. /**
  157. * Encrypts a string
  158. *
  159. * @access private
  160. *
  161. * @param string $str the string to encrypt
  162. *
  163. * @return string the string type of the cipher text on success,
  164. * PEAR_Error on failure
  165. */
  166. function _encryptString($str) {
  167. if ($str == '') {
  168. return '';
  169. }
  170. $v = $this->_str2long($str, true);
  171. $v = $this->_encryptArray($v);
  172. return $this->_long2str($v, false);
  173. }
  174. // }}}
  175. // {{{ _encryptArray()
  176. /**
  177. * Encrypts a long integer array
  178. *
  179. * @access private
  180. *
  181. * @param array $v the long integer array to encrypt
  182. *
  183. * @return array the array type of the cipher text on success,
  184. * PEAR_Error on failure
  185. */
  186. function _encryptArray($v) {
  187. $n = count($v) - 1;
  188. $z = $v[$n];
  189. $y = $v[0];
  190. $q = floor(6 + 52 / ($n + 1));
  191. $sum = 0;
  192. while (0 < $q--) {
  193. $sum = $this->_int32($sum + CRYPT_XXTEA_DELTA);
  194. $e = $sum >> 2 & 3;
  195. for ($p = 0; $p < $n; $p++) {
  196. $y = $v[$p + 1];
  197. $mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
  198. $z = $v[$p] = $this->_int32($v[$p] + $mx);
  199. }
  200. $y = $v[0];
  201. $mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
  202. $z = $v[$n] = $this->_int32($v[$n] + $mx);
  203. }
  204. return $v;
  205. }
  206. // }}}
  207. // {{{ _decryptString()
  208. /**
  209. * Decrypts a string
  210. *
  211. * @access private
  212. *
  213. * @param string $str the string to decrypt
  214. *
  215. * @return string the string type of the plain text on success,
  216. * PEAR_Error on failure
  217. */
  218. function _decryptString($str) {
  219. if ($str == '') {
  220. return '';
  221. }
  222. $v = $this->_str2long($str, false);
  223. $v = $this->_decryptArray($v);
  224. return $this->_long2str($v, true);
  225. }
  226. // }}}
  227. // {{{ _encryptArray()
  228. /**
  229. * Decrypts a long integer array
  230. *
  231. * @access private
  232. *
  233. * @param array $v the long integer array to decrypt
  234. *
  235. * @return array the array type of the plain text on success,
  236. * PEAR_Error on failure
  237. */
  238. function _decryptArray($v) {
  239. $n = count($v) - 1;
  240. $z = $v[$n];
  241. $y = $v[0];
  242. $q = floor(6 + 52 / ($n + 1));
  243. $sum = $this->_int32($q * CRYPT_XXTEA_DELTA);
  244. while ($sum != 0) {
  245. $e = $sum >> 2 & 3;
  246. for ($p = $n; $p > 0; $p--) {
  247. $z = $v[$p - 1];
  248. $mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
  249. $y = $v[$p] = $this->_int32($v[$p] - $mx);
  250. }
  251. $z = $v[$n];
  252. $mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
  253. $y = $v[0] = $this->_int32($v[0] - $mx);
  254. $sum = $this->_int32($sum - CRYPT_XXTEA_DELTA);
  255. }
  256. return $v;
  257. }
  258. // }}}
  259. // {{{ _long2str()
  260. /**
  261. * Converts long integer array to string
  262. *
  263. * @access private
  264. *
  265. * @param array $v the long integer array
  266. * @param bool $w whether the given array contains the length of
  267. * original plain text
  268. *
  269. * @return string the string
  270. */
  271. function _long2str($v, $w) {
  272. $len = count($v);
  273. $s = '';
  274. for ($i = 0; $i < $len; $i++) {
  275. $s .= pack('V', $v[$i]);
  276. }
  277. if ($w) {
  278. return substr($s, 0, $v[$len - 1]);
  279. } else {
  280. return $s;
  281. }
  282. }
  283. // }}}
  284. // {{{ _str2long()
  285. /**
  286. * Converts string to long integer array
  287. *
  288. * @access private
  289. *
  290. * @param string $s the string
  291. * @param bool $w whether to append the length of string to array
  292. *
  293. * @return string the long integer array
  294. */
  295. function _str2long($s, $w) {
  296. $v = array_values(unpack('V*', $s.str_repeat("\0", (4-strlen($s)%4)&3)));
  297. if ($w) {
  298. $v[] = strlen($s);
  299. }
  300. return $v;
  301. }
  302. // }}}
  303. // {{{ _int32()
  304. /**
  305. * Corrects long integer value
  306. *
  307. * Because a number beyond the bounds of the integer type will be automatically
  308. * interpreted as a float, the simulation of integer overflow is needed.
  309. *
  310. * @access private
  311. *
  312. * @param int $n the integer
  313. *
  314. * @return int the correct integer
  315. */
  316. function _int32($n) {
  317. while ($n >= 2147483648) $n -= 4294967296;
  318. while ($n <= -2147483649) $n += 4294967296;
  319. return (int)$n;
  320. }
  321. // }}}
  322. }
  323. ?>