diff --git a/phpseclib/Crypt/AES.php b/phpseclib/Crypt/AES.php new file mode 100644 index 00000000..e1683f2a --- /dev/null +++ b/phpseclib/Crypt/AES.php @@ -0,0 +1,422 @@ + + * setKey('abcdefghijklmnop'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $aes->decrypt($aes->encrypt($plaintext)); + * ?> + * + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * @category Crypt + * @package Crypt_AES + * @author Jim Wigginton + * @copyright MMVIII Jim Wigginton + * @license http://www.gnu.org/licenses/lgpl.txt + * @version $Id: AES.php,v 1.1 2009-02-16 23:11:01 terrafrost Exp $ + * @link http://phpseclib.sourceforge.net + */ + +/** + * Include Crypt_Rijndael + */ +require_once 'Rijndael.php'; + +/**#@+ + * @access public + * @see Crypt_Rijndael::encrypt() + * @see Crypt_Rijndael::decrypt() + */ +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +define('CRYPT_AES_MODE_ECB', 1); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +define('CRYPT_AES_MODE_CBC', 2); +/**#@-*/ + +/**#@+ + * @access private + * @see Crypt_AES::Crypt_AES() + */ +/** + * Toggles the internal implementation + */ +define('CRYPT_AES_MODE_INTERNAL', 1); +/** + * Toggles the mcrypt implementation + */ +define('CRYPT_AES_MODE_MCRYPT', 2); +/**#@-*/ + +/** + * Pure-PHP implementation of AES. + * + * @author Jim Wigginton + * @version 0.1.0 + * @access public + * @package Crypt_AES + */ +class Crypt_AES extends Crypt_Rijndael { + /** + * MCrypt parameters + * + * @see Crypt_AES::setMCrypt() + * @var Array + * @access private + */ + var $mcrypt = array('', ''); + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be + * CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC. If not explictly set, CRYPT_AES_MODE_CBC will be used. + * + * @param optional Integer $mode + * @return Crypt_AES + * @access public + */ + function Crypt_AES($mode = CRYPT_MODE_AES_CBC) + { + if ( !defined('CRYPT_AES_MODE') ) { + switch (true) { + case extension_loaded('mcrypt'): + // i'd check to see if aes was supported, by doing in_array('des', mcrypt_list_algorithms('')), + // but since that can be changed after the object has been created, there doesn't seem to be + // a lot of point... + define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT); + break; + default: + define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL); + } + } + + switch ( CRYPT_AES_MODE ) { + case CRYPT_AES_MODE_MCRYPT: + switch ($mode) { + case CRYPT_AES_MODE_ECB: + $this->mode = MCRYPT_MODE_ECB; + break; + case CRYPT_AES_MODE_CBC: + default: + $this->mode = MCRYPT_MODE_CBC; + } + + break; + default: + switch ($mode) { + case CRYPT_AES_MODE_ECB: + $this->mode = CRYPT_RIJNDAEL_MODE_ECB; + break; + case CRYPT_AES_MODE_CBC: + default: + $this->mode = CRYPT_RIJNDAEL_MODE_CBC; + } + } + + if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) { + parent::Crypt_Rijndael($this->mode); + } + } + + /** + * Dummy function + * + * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything. + * + * @access public + * @param Integer $length + */ + function setBlockLength($length) + { + return; + } + + /** + * Encrypts a message. + * + * $plaintext will be padded with up to 16 additional bytes. Other AES implementations may or may not pad in the + * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following + * URL: + * + * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} + * + * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. + * strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that + * length. + * + * @see Crypt_AES::decrypt() + * @access public + * @param String $plaintext + */ + function encrypt($plaintext) + { + if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { + $this->_mcryptSetup(); + $this->_pad($plaintext); + + $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); + mcrypt_generic_init($td, $this->key, $this->encryptIV); + + $ciphertext = mcrypt_generic($td, $plaintext); + + mcrypt_generic_deinit($td); + mcrypt_module_close($td); + + if ($this->continuousBuffer) { + $this->encryptIV = substr($ciphertext, -16); + } + + return $ciphertext; + } + + return parent::encrypt($plaintext); + } + + /** + * Decrypts a message. + * + * If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is. + * + * @see Crypt_AES::encrypt() + * @access public + * @param String $ciphertext + */ + function decrypt($ciphertext) + { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0)); + + if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { + $this->_mcryptSetup(); + + $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); + mcrypt_generic_init($td, $this->key, $this->decryptIV); + + $plaintext = mdecrypt_generic($td, $ciphertext); + + mcrypt_generic_deinit($td); + mcrypt_module_close($td); + + if ($this->continuousBuffer) { + $this->decryptIV = substr($ciphertext, -16); + } + + return $this->_unpad($plaintext); + } + + return parent::decrypt($ciphertext); + } + + /** + * Sets MCrypt parameters. (optional) + * + * If MCrypt is being used, empty strings will be used, unless otherwise specified. + * + * @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open + * @access public + * @param optional Integer $algorithm_directory + * @param optional Integer $mode_directory + */ + function setMCrypt($algorithm_directory = '', $mode_directory = '') + { + $this->mcrypt = array($algorithm_directory, $mode_directory); + } + + /** + * Setup mcrypt + * + * Validates all the variables. + * + * @access private + */ + function _mcryptSetup() + { + if (!$this->changed) { + return; + } + + if (!$this->explicit_key_length) { + // this just copied from Crypt_Rijndael::_setup() + $length = strlen($this->key) >> 2; + if ($length > 8) { + $length = 8; + } else if ($length < 4) { + $length = 4; + } + $this->Nk = $length; + $this->key_size = $length << 2; + } + + switch ($this->Nk) { + case 4: // 128 + $this->key_size = 16; + break; + case 5: // 160 + case 6: // 192 + $this->key_size = 24; + break; + case 7: // 224 + case 8: // 256 + $this->key_size = 32; + } + + $this->key = substr($this->key, 0, $this->key_size); + $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0)); + + $this->changed = false; + } + + /** + * Encrypts a block + * + * Optimized over Crypt_Rijndael's implementation by means of loop unrolling. + * + * @see Crypt_Rijndael::_encryptBlock() + * @access private + * @param String $in + * @return String + */ + function _encryptBlock($in) + { + // unpack starts it's indices at 1 - not 0. + $state = unpack('N*', $in); + + // addRoundKey and reindex $state + $state = array( + $state[1] ^ $this->w[0][0], + $state[2] ^ $this->w[0][1], + $state[3] ^ $this->w[0][2], + $state[4] ^ $this->w[0][3] + ); + + // shiftRows + subWord + mixColumns + addRoundKey + // we could loop unroll this and use if statements to do more rounds as necessary, but, in my tests, that yields + // only a marginal improvement. since that also, imho, hinders the readability of the code, i've opted not to do it. + for ($round = 1; $round < $this->Nr; $round++) { + $state = array( + $this->t0[$state[0] & 0xFF000000] ^ $this->t1[$state[1] & 0x00FF0000] ^ $this->t2[$state[2] & 0x0000FF00] ^ $this->t3[$state[3] & 0x000000FF] ^ $this->w[$round][0], + $this->t0[$state[1] & 0xFF000000] ^ $this->t1[$state[2] & 0x00FF0000] ^ $this->t2[$state[3] & 0x0000FF00] ^ $this->t3[$state[0] & 0x000000FF] ^ $this->w[$round][1], + $this->t0[$state[2] & 0xFF000000] ^ $this->t1[$state[3] & 0x00FF0000] ^ $this->t2[$state[0] & 0x0000FF00] ^ $this->t3[$state[1] & 0x000000FF] ^ $this->w[$round][2], + $this->t0[$state[3] & 0xFF000000] ^ $this->t1[$state[0] & 0x00FF0000] ^ $this->t2[$state[1] & 0x0000FF00] ^ $this->t3[$state[2] & 0x000000FF] ^ $this->w[$round][3] + ); + + } + + // subWord + $state = array( + $this->_subWord($state[0]), + $this->_subWord($state[1]), + $this->_subWord($state[2]), + $this->_subWord($state[3]) + ); + + // shiftRows + addRoundKey + $state = array( + ($state[0] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[3] & 0x000000FF) ^ $this->w[$this->Nr][0], + ($state[1] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[0] & 0x000000FF) ^ $this->w[$this->Nr][1], + ($state[2] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[1] & 0x000000FF) ^ $this->w[$this->Nr][2], + ($state[3] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[2] & 0x000000FF) ^ $this->w[$this->Nr][3] + ); + + return pack('N*', $state[0], $state[1], $state[2], $state[3]); + } + + /** + * Decrypts a block + * + * Optimized over Crypt_Rijndael's implementation by means of loop unrolling. + * + * @see Crypt_Rijndael::_decryptBlock() + * @access private + * @param String $in + * @return String + */ + function _decryptBlock($in) + { + // unpack starts it's indices at 1 - not 0. + $state = unpack('N*', $in); + + // addRoundKey and reindex $state + $state = array( + $state[1] ^ $this->dw[$this->Nr][0], + $state[2] ^ $this->dw[$this->Nr][1], + $state[3] ^ $this->dw[$this->Nr][2], + $state[4] ^ $this->dw[$this->Nr][3] + ); + + // invShiftRows + invSubBytes + invMixColumns + addRoundKey + for ($round = $this->Nr - 1; $round > 0; $round--) { + $state = array( + $this->dt0[$state[0] & 0xFF000000] ^ $this->dt1[$state[3] & 0x00FF0000] ^ $this->dt2[$state[2] & 0x0000FF00] ^ $this->dt3[$state[1] & 0x000000FF] ^ $this->dw[$round][0], + $this->dt0[$state[1] & 0xFF000000] ^ $this->dt1[$state[0] & 0x00FF0000] ^ $this->dt2[$state[3] & 0x0000FF00] ^ $this->dt3[$state[2] & 0x000000FF] ^ $this->dw[$round][1], + $this->dt0[$state[2] & 0xFF000000] ^ $this->dt1[$state[1] & 0x00FF0000] ^ $this->dt2[$state[0] & 0x0000FF00] ^ $this->dt3[$state[3] & 0x000000FF] ^ $this->dw[$round][2], + $this->dt0[$state[3] & 0xFF000000] ^ $this->dt1[$state[2] & 0x00FF0000] ^ $this->dt2[$state[1] & 0x0000FF00] ^ $this->dt3[$state[0] & 0x000000FF] ^ $this->dw[$round][3] + ); + } + + // invShiftRows + invSubWord + addRoundKey + $state = array( + $this->_invSubWord(($state[0] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[1] & 0x000000FF)) ^ $this->dw[0][0], + $this->_invSubWord(($state[1] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[2] & 0x000000FF)) ^ $this->dw[0][1], + $this->_invSubWord(($state[2] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[3] & 0x000000FF)) ^ $this->dw[0][2], + $this->_invSubWord(($state[3] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[0] & 0x000000FF)) ^ $this->dw[0][3] + ); + + return pack('N*', $state[0], $state[1], $state[2], $state[3]); + } +} + +// vim: ts=4:sw=4:et: +// vim6: fdl=1: \ No newline at end of file diff --git a/phpseclib/Math/BigInteger.php b/phpseclib/Math/BigInteger.php index a54de1d0..78c6b56a 100644 --- a/phpseclib/Math/BigInteger.php +++ b/phpseclib/Math/BigInteger.php @@ -69,7 +69,7 @@ * @author Jim Wigginton * @copyright MMVI Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: BigInteger.php,v 1.4 2008-05-25 07:28:57 terrafrost Exp $ + * @version $Id: BigInteger.php,v 1.5 2009-02-16 23:11:01 terrafrost Exp $ * @link http://pear.php.net/package/Math_BigInteger */ @@ -523,6 +523,20 @@ class Math_BigInteger { return $result; } + /** + * __toString() magic method + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * toString(). + * + * @access public + * @internal Implemented per a suggestion by Techie-Michael - thanks! + */ + function __toString() + { + return $this->toString(); + } + /** * Adds two BigIntegers. * @@ -1412,7 +1426,7 @@ class Math_BigInteger { * * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85} * - * As for why we do all the bitmasking... strange things can happen when converting from flots to ints. For + * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that