363 lines
10 KiB
PHP
363 lines
10 KiB
PHP
<?php
|
|
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
|
|
|
/**
|
|
* PHP implementation of XXTEA encryption algorithm.
|
|
*
|
|
* XXTEA is a secure and fast encryption algorithm, suitable for web
|
|
* development.
|
|
*
|
|
* PHP versions 4 and 5
|
|
*
|
|
* LICENSE: This library is free software; you can redistribute it
|
|
* and/or modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA.
|
|
*
|
|
* @category Encryption
|
|
* @package Crypt_XXTEA
|
|
* @author Wudi Liu <wudicgi@gmail.com>
|
|
* @author Ma Bingyao <andot@ujn.edu.cn>
|
|
* @copyright 2005-2008 Coolcode.CN
|
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
|
* @version CVS: $Id: XXTEA.php,v 1.3 2008/03/06 11:38:45 wudicgi Exp $
|
|
* @link http://pear.php.net/package/Crypt_XXTEA
|
|
*/
|
|
|
|
/**
|
|
* Needed for error handling
|
|
*/
|
|
require_once 'PEAR.php';
|
|
|
|
// {{{ constants
|
|
|
|
define('CRYPT_XXTEA_DELTA', 0x9E3779B9);
|
|
|
|
// }}}
|
|
|
|
/**
|
|
* The main class
|
|
*
|
|
* @category Encryption
|
|
* @package Crypt_XXTEA
|
|
* @author Wudi Liu <wudicgi@gmail.com>
|
|
* @author Ma Bingyao <andot@ujn.edu.cn>
|
|
* @copyright 2005-2008 Coolcode.CN
|
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
|
* @version Release: 0.9.0
|
|
* @link http://pear.php.net/package/Crypt_XXTEA
|
|
*/
|
|
class Crypt_XXTEA {
|
|
// {{{ properties
|
|
|
|
/**
|
|
* The long integer array of secret key
|
|
*
|
|
* @access private
|
|
*
|
|
* @var array
|
|
*/
|
|
var $_key;
|
|
|
|
// }}}
|
|
|
|
// {{{ setKey()
|
|
|
|
/**
|
|
* Sets the secret key
|
|
*
|
|
* The key must be non-empty, and not more than 16 characters or 4 long values
|
|
*
|
|
* @access public
|
|
*
|
|
* @param mixed $key the secret key (string or long integer array)
|
|
*
|
|
* @return bool true on success, PEAR_Error on failure
|
|
*/
|
|
function setKey($key) {
|
|
if (is_string($key)) {
|
|
$k = $this->_str2long($key, false);
|
|
} elseif (is_array($key)) {
|
|
$k = $key;
|
|
} else {
|
|
return PEAR::raiseError('The secret key must be a string or long integer array.');
|
|
}
|
|
if (count($k) > 4) {
|
|
return PEAR::raiseError('The secret key cannot be more than 16 characters or 4 long values.');
|
|
} elseif (count($k) == 0) {
|
|
return PEAR::raiseError('The secret key cannot be empty.');
|
|
} elseif (count($k) < 4) {
|
|
for ($i = count($k); $i < 4; $i++) {
|
|
$k[$i] = 0;
|
|
}
|
|
}
|
|
$this->_key = $k;
|
|
return true;
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ encrypt()
|
|
|
|
/**
|
|
* Encrypts a plain text
|
|
*
|
|
* As the XXTEA encryption algorithm is designed for encrypting and decrypting
|
|
* the long integer array type of data, there is not a standard that defines
|
|
* how to convert between long integer array and text or binary data for it.
|
|
* So this package provides the ability to encrypt and decrypt the long integer
|
|
* arrays directly to satisfy the requirement for working with other
|
|
* implementations. And at the same time, for convenience, it also provides
|
|
* the ability to process strings, which uses its own method to group the text
|
|
* into array.
|
|
*
|
|
* @access public
|
|
*
|
|
* @param mixed $plaintext the plain text (string or long integer array)
|
|
*
|
|
* @return mixed the cipher text as the same type as the parameter $plaintext
|
|
* on success, PEAR_Error on failure
|
|
*/
|
|
function encrypt($plaintext) {
|
|
if ($this->_key == null) {
|
|
return PEAR::raiseError('Secret key is undefined.');
|
|
}
|
|
if (is_string($plaintext)) {
|
|
return $this->_encryptString($plaintext);
|
|
} elseif (is_array($plaintext)) {
|
|
return $this->_encryptArray($plaintext);
|
|
} else {
|
|
return PEAR::raiseError('The plain text must be a string or long integer array.');
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ decrypt()
|
|
|
|
/**
|
|
* Decrypts a cipher text
|
|
*
|
|
* @access public
|
|
*
|
|
* @param mixed $chipertext the cipher text (string or long integer array)
|
|
*
|
|
* @return mixed the plain text as the same type as the parameter $chipertext
|
|
* on success, PEAR_Error on failure
|
|
*/
|
|
function decrypt($chipertext) {
|
|
if ($this->_key == null) {
|
|
return PEAR::raiseError('Secret key is undefined.');
|
|
}
|
|
if (is_string($chipertext)) {
|
|
return $this->_decryptString($chipertext);
|
|
} elseif (is_array($chipertext)) {
|
|
return $this->_decryptArray($chipertext);
|
|
} else {
|
|
return PEAR::raiseError('The chiper text must be a string or long integer array.');
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ _encryptString()
|
|
|
|
/**
|
|
* Encrypts a string
|
|
*
|
|
* @access private
|
|
*
|
|
* @param string $str the string to encrypt
|
|
*
|
|
* @return string the string type of the cipher text on success,
|
|
* PEAR_Error on failure
|
|
*/
|
|
function _encryptString($str) {
|
|
if ($str == '') {
|
|
return '';
|
|
}
|
|
$v = $this->_str2long($str, true);
|
|
$v = $this->_encryptArray($v);
|
|
return $this->_long2str($v, false);
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ _encryptArray()
|
|
|
|
/**
|
|
* Encrypts a long integer array
|
|
*
|
|
* @access private
|
|
*
|
|
* @param array $v the long integer array to encrypt
|
|
*
|
|
* @return array the array type of the cipher text on success,
|
|
* PEAR_Error on failure
|
|
*/
|
|
function _encryptArray($v) {
|
|
$n = count($v) - 1;
|
|
$z = $v[$n];
|
|
$y = $v[0];
|
|
$q = floor(6 + 52 / ($n + 1));
|
|
$sum = 0;
|
|
while (0 < $q--) {
|
|
$sum = $this->_int32($sum + CRYPT_XXTEA_DELTA);
|
|
$e = $sum >> 2 & 3;
|
|
for ($p = 0; $p < $n; $p++) {
|
|
$y = $v[$p + 1];
|
|
$mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
|
|
$z = $v[$p] = $this->_int32($v[$p] + $mx);
|
|
}
|
|
$y = $v[0];
|
|
$mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
|
|
$z = $v[$n] = $this->_int32($v[$n] + $mx);
|
|
}
|
|
return $v;
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ _decryptString()
|
|
|
|
/**
|
|
* Decrypts a string
|
|
*
|
|
* @access private
|
|
*
|
|
* @param string $str the string to decrypt
|
|
*
|
|
* @return string the string type of the plain text on success,
|
|
* PEAR_Error on failure
|
|
*/
|
|
function _decryptString($str) {
|
|
if ($str == '') {
|
|
return '';
|
|
}
|
|
$v = $this->_str2long($str, false);
|
|
$v = $this->_decryptArray($v);
|
|
return $this->_long2str($v, true);
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ _encryptArray()
|
|
|
|
/**
|
|
* Decrypts a long integer array
|
|
*
|
|
* @access private
|
|
*
|
|
* @param array $v the long integer array to decrypt
|
|
*
|
|
* @return array the array type of the plain text on success,
|
|
* PEAR_Error on failure
|
|
*/
|
|
function _decryptArray($v) {
|
|
$n = count($v) - 1;
|
|
$z = $v[$n];
|
|
$y = $v[0];
|
|
$q = floor(6 + 52 / ($n + 1));
|
|
$sum = $this->_int32($q * CRYPT_XXTEA_DELTA);
|
|
while ($sum != 0) {
|
|
$e = $sum >> 2 & 3;
|
|
for ($p = $n; $p > 0; $p--) {
|
|
$z = $v[$p - 1];
|
|
$mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
|
|
$y = $v[$p] = $this->_int32($v[$p] - $mx);
|
|
}
|
|
$z = $v[$n];
|
|
$mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
|
|
$y = $v[0] = $this->_int32($v[0] - $mx);
|
|
$sum = $this->_int32($sum - CRYPT_XXTEA_DELTA);
|
|
}
|
|
return $v;
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ _long2str()
|
|
|
|
/**
|
|
* Converts long integer array to string
|
|
*
|
|
* @access private
|
|
*
|
|
* @param array $v the long integer array
|
|
* @param bool $w whether the given array contains the length of
|
|
* original plain text
|
|
*
|
|
* @return string the string
|
|
*/
|
|
function _long2str($v, $w) {
|
|
$len = count($v);
|
|
$s = '';
|
|
for ($i = 0; $i < $len; $i++) {
|
|
$s .= pack('V', $v[$i]);
|
|
}
|
|
if ($w) {
|
|
return substr($s, 0, $v[$len - 1]);
|
|
} else {
|
|
return $s;
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ _str2long()
|
|
|
|
/**
|
|
* Converts string to long integer array
|
|
*
|
|
* @access private
|
|
*
|
|
* @param string $s the string
|
|
* @param bool $w whether to append the length of string to array
|
|
*
|
|
* @return string the long integer array
|
|
*/
|
|
function _str2long($s, $w) {
|
|
$v = array_values(unpack('V*', $s.str_repeat("\0", (4-strlen($s)%4)&3)));
|
|
if ($w) {
|
|
$v[] = strlen($s);
|
|
}
|
|
return $v;
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ _int32()
|
|
|
|
/**
|
|
* Corrects long integer value
|
|
*
|
|
* Because a number beyond the bounds of the integer type will be automatically
|
|
* interpreted as a float, the simulation of integer overflow is needed.
|
|
*
|
|
* @access private
|
|
*
|
|
* @param int $n the integer
|
|
*
|
|
* @return int the correct integer
|
|
*/
|
|
function _int32($n) {
|
|
while ($n >= 2147483648) $n -= 4294967296;
|
|
while ($n <= -2147483649) $n += 4294967296;
|
|
return (int)$n;
|
|
}
|
|
|
|
// }}}
|
|
|
|
}
|
|
|
|
?>
|