diff --git a/phpseclib/Crypt/AES.php b/phpseclib/Crypt/AES.php index 7e90013c..aac0fdd0 100644 --- a/phpseclib/Crypt/AES.php +++ b/phpseclib/Crypt/AES.php @@ -56,7 +56,7 @@ * @author Jim Wigginton * @copyright MMVIII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: AES.php,v 1.4 2009-10-07 20:53:19 terrafrost Exp $ + * @version $Id: AES.php,v 1.5 2009-11-23 19:06:06 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -330,15 +330,14 @@ class Crypt_AES extends Crypt_Rijndael { */ function _encryptBlock($in) { - // unpack starts it's indices at 1 - not 0. - $state = unpack('N*', $in); + $state = unpack('N*word', $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] + $state['word1'] ^ $this->w[0][0], + $state['word2'] ^ $this->w[0][1], + $state['word3'] ^ $this->w[0][2], + $state['word4'] ^ $this->w[0][3] ); // shiftRows + subWord + mixColumns + addRoundKey @@ -351,6 +350,7 @@ class Crypt_AES extends Crypt_Rijndael { $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 @@ -384,17 +384,17 @@ class Crypt_AES extends Crypt_Rijndael { */ function _decryptBlock($in) { - // unpack starts it's indices at 1 - not 0. - $state = unpack('N*', $in); + $state = unpack('N*word', $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] + $state['word1'] ^ $this->dw[$this->Nr][0], + $state['word2'] ^ $this->dw[$this->Nr][1], + $state['word3'] ^ $this->dw[$this->Nr][2], + $state['word4'] ^ $this->dw[$this->Nr][3] ); + // invShiftRows + invSubBytes + invMixColumns + addRoundKey for ($round = $this->Nr - 1; $round > 0; $round--) { $state = array( diff --git a/phpseclib/Crypt/DES.php b/phpseclib/Crypt/DES.php index a8d28f7e..2ea4bbce 100644 --- a/phpseclib/Crypt/DES.php +++ b/phpseclib/Crypt/DES.php @@ -53,7 +53,7 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: DES.php,v 1.8 2009-05-27 16:15:23 terrafrost Exp $ + * @version $Id: DES.php,v 1.9 2009-11-23 19:06:06 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -523,7 +523,7 @@ class Crypt_DES { /** * Unpads a string * - * If padding is enabled and the reported padding length exceeds the block size, padding will be, hence forth, disabled. + * If padding is enabled and the reported padding length is invalid, padding will be, hence forth, disabled. * * @see Crypt_DES::_pad() * @access private @@ -536,8 +536,8 @@ class Crypt_DES { $length = ord($text[strlen($text) - 1]); - if ($length > 8) { - user_error("The number of bytes reported as being padded ($length) exceeds the block size (8)", E_USER_NOTICE); + if (!$length || $length > 8) { + user_error("The number of bytes reported as being padded ($length) is invalid (block size = 8)", E_USER_NOTICE); $this->padding = false; return $text; } diff --git a/phpseclib/Crypt/Hash.php b/phpseclib/Crypt/Hash.php index c0fa8870..91eb0fbf 100644 --- a/phpseclib/Crypt/Hash.php +++ b/phpseclib/Crypt/Hash.php @@ -4,9 +4,12 @@ /** * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. * - * Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports md5, md5-96, sha1, and - * sha1-96. If {@link Crypt_Hash::setKey() setKey()} is called, {@link Crypt_Hash::hash() hash()} will return the HMAC as - * as opposed to the hash. If no valid algorithm is provided, sha1 will be used. + * Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports the following: + * + * md2, md5, md5-96, sha1, sha1-96, sha256, sha384, and sha512 + * + * If {@link Crypt_Hash::setKey() setKey()} is called, {@link Crypt_Hash::hash() hash()} will return the HMAC as opposed to + * the hash. If no valid algorithm is provided, sha1 will be used. * * PHP versions 4 and 5 * @@ -46,7 +49,7 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: Hash.php,v 1.3 2009-05-27 16:15:23 terrafrost Exp $ + * @version $Id: Hash.php,v 1.4 2009-11-23 19:06:07 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -93,7 +96,7 @@ class Crypt_Hash { * @var Integer * @access private */ - var $l; + var $l = false; /** * Hash Algorithm @@ -182,20 +185,45 @@ class Crypt_Hash { case 'sha1-96': $this->l = 12; // 96 / 8 = 12 break; + case 'md2': case 'md5': $this->l = 16; break; case 'sha1': $this->l = 20; + break; + case 'sha256': + $this->l = 32; + break; + case 'sha384': + $this->l = 48; + break; + case 'sha512': + $this->l = 64; } - switch ( CRYPT_HASH_MODE ) { + switch ($hash) { + case 'md2': + $mode = CRYPT_HASH_MODE_INTERNAL; + break; + case 'sha384': + case 'sha512': + $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_MHASH ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE; + break; + default: + $mode = CRYPT_HASH_MODE; + } + + switch ( $mode ) { case CRYPT_HASH_MODE_MHASH: switch ($hash) { case 'md5': case 'md5-96': $this->hash = MHASH_MD5; break; + case 'sha256': + $this->hash = MHASH_SHA256; + break; case 'sha1': case 'sha1-96': default: @@ -208,6 +236,11 @@ class Crypt_Hash { case 'md5-96': $this->hash = 'md5'; return; + case 'sha256': + case 'sha384': + case 'sha512': + $this->hash = $hash; + return; case 'sha1': case 'sha1-96': default: @@ -217,16 +250,28 @@ class Crypt_Hash { } switch ($hash) { + case 'md2': + $this->b = 16; + $this->hash = array($this, '_md2'); case 'md5': case 'md5-96': $this->b = 64; - $this->hash = 'md5'; + $this->hash = array($this, '_md5'); + break; + case 'sha256': + $this->b = 64; + $this->hash = array($this, '_sha256'); + break; + case 'sha384': + case 'sha512': + $this->b = 128; + $this->hash = array($this, '_sha512'); break; case 'sha1': case 'sha1-96': default: $this->b = 64; - $this->hash = 'sha1'; + $this->hash = array($this, '_sha1'); } $this->ipad = str_repeat(chr(0x36), $this->b); @@ -238,11 +283,14 @@ class Crypt_Hash { * * @access public * @param String $text + * @return String */ function hash($text) { + $mode = is_array($this->hash) ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE; + if (!empty($this->key)) { - switch ( CRYPT_HASH_MODE ) { + switch ( $mode ) { case CRYPT_HASH_MODE_MHASH: $output = mhash($this->hash, $text, $this->key); break; @@ -250,35 +298,518 @@ class Crypt_Hash { $output = hash_hmac($this->hash, $text, $this->key, true); break; case CRYPT_HASH_MODE_INTERNAL: - $hash = $this->hash; /* "Applications that use keys longer than B bytes will first hash the key using H and then use the resultant L byte string as the actual key to HMAC." -- http://tools.ietf.org/html/rfc2104#section-2 */ - $key = strlen($this->key) > $this->b ? $hash($this->key) : $this->key; + $key = strlen($this->key) > $this->b ? call_user_func($this->$hash, $this->key) : $this->key; - $key = str_pad($key, $this->b, chr(0));// step 1 - $temp = $this->ipad ^ $key; // step 2 - $temp .= $text; // step 3 - $temp = pack('H*', $hash($temp)); // step 4 - $output = $this->opad ^ $key; // step 5 - $output.= $temp; // step 6 - $output = pack('H*', $hash($output)); // step 7 + $key = str_pad($key, $this->b, chr(0)); // step 1 + $temp = $this->ipad ^ $key; // step 2 + $temp .= $text; // step 3 + $temp = call_user_func($this->hash, $temp); // step 4 + $output = $this->opad ^ $key; // step 5 + $output.= $temp; // step 6 + $output = call_user_func($this->hash, $output); // step 7 } } else { - switch ( CRYPT_HASH_MODE ) { + switch ( $mode ) { case CRYPT_HASH_MODE_MHASH: $output = mhash($this->hash, $text); break; - case CRYPT_HASH_MODE_MHASH: + case CRYPT_HASH_MODE_HASH: $output = hash($this->hash, $text, true); break; case CRYPT_HASH_MODE_INTERNAL: - $hash = $this->hash; - $output = pack('H*', $hash($output)); + $output = call_user_func($this->hash, $text); } } return substr($output, 0, $this->l); } + + /** + * Returns the hash length (in bytes) + * + * @access private + * @return Integer + */ + function getLength() + { + return $this->l; + } + + /** + * Wrapper for MD5 + * + * @access private + * @param String $text + */ + function _md5($m) + { + return pack('H*', md5($m)); + } + + /** + * Wrapper for SHA1 + * + * @access private + * @param String $text + */ + function _sha1($m) + { + return pack('H*', sha1($m)); + } + + /** + * Pure-PHP implementation of MD2 + * + * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}. + * + * @access private + * @param String $text + */ + function _md2($m) + { + static $s = array( + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, + 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, + 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, + 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, + 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, + 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, + 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, + 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, + 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, + 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, + 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, + 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, + 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, + 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, + 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, + 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, + 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, + 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 + ); + + // Step 1. Append Padding Bytes + $pad = 16 - (strlen($m) & 0xF); + $m.= str_repeat(chr($pad), $pad); + + $length = strlen($m); + + // Step 2. Append Checksum + $c = str_repeat(chr(0), 16); + $l = chr(0); + for ($i = 0; $i < $length; $i+= 16) { + for ($j = 0; $j < 16; $j++) { + $c[$j] = chr($s[ord($m[$i + $j] ^ $l)]); + $l = $c[$j]; + } + } + $m.= $c; + + $length+= 16; + + // Step 3. Initialize MD Buffer + $x = str_repeat(chr(0), 48); + + // Step 4. Process Message in 16-Byte Blocks + for ($i = 0; $i < $length; $i+= 16) { + for ($j = 0; $j < 16; $j++) { + $x[$j + 16] = $m[$i + $j]; + $x[$j + 32] = $x[$j + 16] ^ $x[$j]; + } + $t = chr(0); + for ($j = 0; $j < 18; $j++) { + for ($k = 0; $k < 48; $k++) { + $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]); + //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]); + } + $t = chr(ord($t) + $j); + } + } + + // Step 5. Output + return bin2hex(substr($x, 0, 16)); + } + + /** + * Pure-PHP implementation of SHA256 + * + * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}. + * + * @access private + * @param String $text + */ + function _sha256($m) + { + if (extension_loaded('suhosin')) { + return pack('H*', sha256($m)); + } + + // Initialize variables + $hash = array( + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + ); + // Initialize table of round constants + // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311) + static $k = array( + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ); + + // Pre-processing + $length = strlen($m); + // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64 + $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F)); + $m[$length] = chr(0x80); + // we don't support hashing strings 512MB long + $m.= pack('N2', 0, $length << 3); + + // Process the message in successive 512-bit chunks + $chunks = str_split($m, 64); + foreach ($chunks as $chunk) { + $w = array(); + for ($i = 0; $i < 16; $i++) { + extract(unpack('Ntemp', $this->_string_shift($chunk, 4))); + $w[] = $temp; + } + + // Extend the sixteen 32-bit words into sixty-four 32-bit words + for ($i = 16; $i < 64; $i++) { + $s0 = $this->_rightRotate($w[$i - 15], 7) ^ + $this->_rightRotate($w[$i - 15], 18) ^ + $this->_rightShift( $w[$i - 15], 3); + $s1 = $this->_rightRotate($w[$i - 2], 17) ^ + $this->_rightRotate($w[$i - 2], 19) ^ + $this->_rightShift( $w[$i - 2], 10); + $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1); + + } + + // Initialize hash value for this chunk + list($a, $b, $c, $d, $e, $f, $g, $h) = $hash; + + // Main loop + for ($i = 0; $i < 64; $i++) { + $s0 = $this->_rightRotate($a, 2) ^ + $this->_rightRotate($a, 13) ^ + $this->_rightRotate($a, 22); + $maj = ($a & $b) ^ + ($a & $c) ^ + ($b & $c); + $t2 = $this->_add($s0, $maj); + + $s1 = $this->_rightRotate($e, 6) ^ + $this->_rightRotate($e, 11) ^ + $this->_rightRotate($e, 25); + $ch = ($e & $f) ^ + ($this->_not($e) & $g); + $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]); + + $h = $g; + $g = $f; + $f = $e; + $e = $this->_add($d, $t1); + $d = $c; + $c = $b; + $b = $a; + $a = $this->_add($t1, $t2); + } + + // Add this chunk's hash to result so far + $hash = array( + $this->_add($hash[0], $a), + $this->_add($hash[1], $b), + $this->_add($hash[2], $c), + $this->_add($hash[3], $d), + $this->_add($hash[4], $e), + $this->_add($hash[5], $f), + $this->_add($hash[6], $g), + $this->_add($hash[7], $h) + ); + } + + // Produce the final hash value (big-endian) + return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]); + } + + /** + * Pure-PHP implementation of SHA384 and SHA512 + * + * @access private + * @param String $text + */ + function _sha512($m) + { + if (!class_exists('Math_BigInteger')) { + require_once('Math/BigInteger.php'); + } + + static $init384, $init512, $k; + + if (!isset($k)) { + // Initialize variables + $init384 = array( // initial values for SHA384 + 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939', + '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4' + ); + $init512 = array( // initial values for SHA512 + '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1', + '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179' + ); + + for ($i = 0; $i < 8; $i++) { + $init384[$i] = new Math_BigInteger($init384[$i], 16); + $init384[$i]->setPrecision(64); + $init512[$i] = new Math_BigInteger($init512[$i], 16); + $init512[$i]->setPrecision(64); + } + + // Initialize table of round constants + // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409) + $k = array( + '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc', + '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118', + 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2', + '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694', + 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65', + '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5', + '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4', + 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70', + '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df', + '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b', + 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30', + 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8', + '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8', + '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3', + '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec', + '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b', + 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178', + '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b', + '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c', + '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817' + ); + + for ($i = 0; $i < 80; $i++) { + $k[$i] = new Math_BigInteger($k[$i], 16); + } + } + + $hash = $this->l == 48 ? $init384 : $init512; + + // Pre-processing + $length = strlen($m); + // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128 + $m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F)); + $m[$length] = chr(0x80); + // we don't support hashing strings 512MB long + $m.= pack('N4', 0, 0, 0, $length << 3); + + // Process the message in successive 1024-bit chunks + $chunks = str_split($m, 128); + foreach ($chunks as $chunk) { + $w = array(); + for ($i = 0; $i < 16; $i++) { + $temp = new Math_BigInteger($this->_string_shift($chunk, 8), 256); + $temp->setPrecision(64); + $w[] = $temp; + } + + // Extend the sixteen 32-bit words into eighty 32-bit words + for ($i = 16; $i < 80; $i++) { + $temp = array( + $w[$i - 15]->bitwise_rightRotate(1), + $w[$i - 15]->bitwise_rightRotate(8), + $w[$i - 15]->bitwise_rightShift(7) + ); + $s0 = $temp[0]->bitwise_xor($temp[1]); + $s0 = $s0->bitwise_xor($temp[2]); + $temp = array( + $w[$i - 2]->bitwise_rightRotate(19), + $w[$i - 2]->bitwise_rightRotate(61), + $w[$i - 2]->bitwise_rightShift(6) + ); + $s1 = $temp[0]->bitwise_xor($temp[1]); + $s1 = $s1->bitwise_xor($temp[2]); + $w[$i] = $w[$i - 16]->copy(); + $w[$i] = $w[$i]->add($s0); + $w[$i] = $w[$i]->add($w[$i - 7]); + $w[$i] = $w[$i]->add($s1); + } + + // Initialize hash value for this chunk + $a = $hash[0]->copy(); + $b = $hash[1]->copy(); + $c = $hash[2]->copy(); + $d = $hash[3]->copy(); + $e = $hash[4]->copy(); + $f = $hash[5]->copy(); + $g = $hash[6]->copy(); + $h = $hash[7]->copy(); + + // Main loop + for ($i = 0; $i < 80; $i++) { + $temp = array( + $a->bitwise_rightRotate(28), + $a->bitwise_rightRotate(34), + $a->bitwise_rightRotate(39) + ); + $s0 = $temp[0]->bitwise_xor($temp[1]); + $s0 = $s0->bitwise_xor($temp[2]); + $temp = array( + $a->bitwise_and($b), + $a->bitwise_and($c), + $b->bitwise_and($c) + ); + $maj = $temp[0]->bitwise_xor($temp[1]); + $maj = $maj->bitwise_xor($temp[2]); + $t2 = $s0->add($maj); + + $temp = array( + $e->bitwise_rightRotate(14), + $e->bitwise_rightRotate(18), + $e->bitwise_rightRotate(41) + ); + $s1 = $temp[0]->bitwise_xor($temp[1]); + $s1 = $s1->bitwise_xor($temp[2]); + $temp = array( + $e->bitwise_and($f), + $g->bitwise_and($e->bitwise_not()) + ); + $ch = $temp[0]->bitwise_xor($temp[1]); + $t1 = $h->add($s1); + $t1 = $t1->add($ch); + $t1 = $t1->add($k[$i]); + $t1 = $t1->add($w[$i]); + + $h = $g->copy(); + $g = $f->copy(); + $f = $e->copy(); + $e = $d->add($t1); + $d = $c->copy(); + $c = $b->copy(); + $b = $a->copy(); + $a = $t1->add($t2); + } + + // Add this chunk's hash to result so far + $hash = array( + $hash[0]->add($a), + $hash[1]->add($b), + $hash[2]->add($c), + $hash[3]->add($d), + $hash[4]->add($e), + $hash[5]->add($f), + $hash[6]->add($g), + $hash[7]->add($h) + ); + } + + // Produce the final hash value (big-endian) + // (Crypt_Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here) + $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() . + $hash[4]->toBytes() . $hash[5]->toBytes(); + if ($this->l != 48) { + $temp.= $hash[6]->toBytes() . $hash[7]->toBytes(); + } + + return $temp; + } + + /** + * Right Rotate + * + * @access private + * @param Integer $int + * @param Integer $amt + * @see _sha256() + * @return Integer + */ + function _rightRotate($int, $amt) + { + $invamt = 32 - $amt; + $mask = (1 << $invamt) - 1; + return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask); + } + + /** + * Right Shift + * + * @access private + * @param Integer $int + * @param Integer $amt + * @see _sha256() + * @return Integer + */ + function _rightShift($int, $amt) + { + $mask = (1 << (32 - $amt)) - 1; + return ($int >> $amt) & $mask; + } + + /** + * Not + * + * @access private + * @param Integer $int + * @see _sha256() + * @return Integer + */ + function _not($int) + { + return ~$int & 0xFFFFFFFF; + } + + /** + * Add + * + * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the + * possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster. + * + * @param String $string + * @param optional Integer $index + * @return String + * @see _sha256() + * @access private + */ + function _add() + { + static $mod; + if (!isset($mod)) { + $mod = pow(2, 32); + } + + $result = 0; + $arguments = func_get_args(); + foreach ($arguments as $argument) { + $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument; + } + + return fmod($result, $mod); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param String $string + * @param optional Integer $index + * @return String + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } } \ No newline at end of file diff --git a/phpseclib/Crypt/Rijndael.php b/phpseclib/Crypt/Rijndael.php index c7e1d4fd..cc9e8c04 100644 --- a/phpseclib/Crypt/Rijndael.php +++ b/phpseclib/Crypt/Rijndael.php @@ -64,7 +64,7 @@ * @author Jim Wigginton * @copyright MMVIII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: Rijndael.php,v 1.7 2009-11-03 22:03:43 terrafrost Exp $ + * @version $Id: Rijndael.php,v 1.8 2009-11-23 19:06:07 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -626,14 +626,13 @@ class Crypt_Rijndael { */ function _encryptBlock($in) { - // unpack starts it's indices at 1 - not 0. - $state = unpack('N*', $in); + $state = array(); + $words = unpack('N*word', $in); - // addRoundKey and reindex $state - for ($i = 0; $i < $this->Nb; $i++) { - $state[$i] = $state[$i + 1] ^ $this->w[0][$i]; + // addRoundKey + foreach ($words as $word) { + $state[] = $word ^ $this->w[0][count($state)]; } - unset($state[$i]); // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components - // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding @@ -704,14 +703,13 @@ class Crypt_Rijndael { */ function _decryptBlock($in) { - // unpack starts it's indices at 1 - not 0. - $state = unpack('N*', $in); + $state = array(); + $words = unpack('N*word', $in); - // addRoundKey and reindex $state - for ($i = 0; $i < $this->Nb; $i++) { - $state[$i] = $state[$i + 1] ^ $this->dw[$this->Nr][$i]; + // addRoundKey + foreach ($words as $word) { + $state[] = $word ^ $this->dw[0][count($state)]; } - unset($state[$i]); $temp = array(); for ($round = $this->Nr - 1; $round > 0; $round--) { @@ -824,13 +822,10 @@ class Crypt_Rijndael { $key = $this->key; - $w = array(); - for ($i = 0; $i < $this->Nk; $i++) { - list(, $w[$i]) = unpack('N', $this->_string_shift($key, 4)); - } + $w = array_values(unpack('N*words', $key)); $length = $this->Nb * ($this->Nr + 1); - for (; $i < $length; $i++) { + for ($i = $this->Nk; $i < $length; $i++) { $temp = $w[$i - 1]; if ($i % $this->Nk == 0) { // according to , "the size of an integer is platform-dependent". @@ -1039,7 +1034,7 @@ class Crypt_Rijndael { /** * Unpads a string. * - * If padding is enabled and the reported padding length exceeds the block size, padding will be, hence forth, disabled. + * If padding is enabled and the reported padding length is invalid, padding will be, hence forth, disabled. * * @see Crypt_Rijndael::_pad() * @access private @@ -1052,8 +1047,8 @@ class Crypt_Rijndael { $length = ord($text[strlen($text) - 1]); - if ($length > $this->block_size) { - user_error("The number of bytes reported as being padded ($length) exceeds the block size ({$this->block_size})", E_USER_NOTICE); + if (!$length || $length > $this->block_size) { + user_error("The number of bytes reported as being padded ($length) is invalid (block size = {$this->block_size})", E_USER_NOTICE); $this->padding = false; return $text; } diff --git a/phpseclib/Crypt/TripleDES.php b/phpseclib/Crypt/TripleDES.php index f3ede108..92bad483 100644 --- a/phpseclib/Crypt/TripleDES.php +++ b/phpseclib/Crypt/TripleDES.php @@ -47,7 +47,7 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: TripleDES.php,v 1.8 2009-06-09 04:00:38 terrafrost Exp $ + * @version $Id: TripleDES.php,v 1.9 2009-11-23 19:06:07 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -576,7 +576,7 @@ class Crypt_TripleDES { /** * Unpads a string * - * If padding is enabled and the reported padding length exceeds the block size, padding will be, hence forth, disabled. + * If padding is enabled and the reported padding length is invalid, padding will be, hence forth, disabled. * * @see Crypt_TripleDES::_pad() * @access private @@ -589,8 +589,8 @@ class Crypt_TripleDES { $length = ord($text[strlen($text) - 1]); - if ($length > 8) { - user_error("The number of bytes reported as being padded ($length) exceeds the block size (8)", E_USER_NOTICE); + if (!$length || $length > 8) { + user_error("The number of bytes reported as being padded ($length) is invalid (block size = 8)", E_USER_NOTICE); $this->padding = false; return $text; } diff --git a/phpseclib/Math/BigInteger.php b/phpseclib/Math/BigInteger.php index aa21ff03..d0d52c97 100644 --- a/phpseclib/Math/BigInteger.php +++ b/phpseclib/Math/BigInteger.php @@ -35,7 +35,7 @@ * * {@link http://www.everything2.com/index.pl?node_id=1736418}}} * - * Here's a quick 'n dirty example of how to use this library: + * Here's an example of how to use this library: * * * @copyright MMVI Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: BigInteger.php,v 1.15 2009-11-04 17:23:58 terrafrost Exp $ + * @version $Id: BigInteger.php,v 1.16 2009-11-23 19:06:07 terrafrost Exp $ * @link http://pear.php.net/package/Math_BigInteger */ -/** - * Include PHP_Compat module bcpowmod since that function does not exist in PHP4: - * {@link http://pear.php.net/package/PHP_Compat/} - * {@link http://php.net/function.bcpowmod} - */ -require_once 'PHP/Compat/Function/bcpowmod.php'; -/** - * Include PHP_Compat module array_fill since that function requires PHP4.2.0+: - * {@link http://pear.php.net/package/PHP_Compat/} - * {@link http://php.net/function.array_fill} - */ -require_once 'PHP/Compat/Function/array_fill.php'; - /**#@+ * @access private * @see Math_BigInteger::_slidingWindow() @@ -203,13 +190,29 @@ class Math_BigInteger { */ var $generator = 'mt_rand'; + /** + * Precision + * + * @see setPrecision() + * @access private + */ + var $precision = -1; + + /** + * Precision Bitmask + * + * @see setPrecision() + * @access private + */ + var $bitmask = false; + /** * Converts base-2, base-10, base-16, and binary strings (eg. base-256) to BigIntegers. * * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using * two's compliment. The sole exception to this is -10, which is treated the same as 10 is. * - * Here's a quick 'n dirty example: + * Here's an example: * * value = $x; + return; + } $this->value = gmp_init(0); break; case MATH_BIGINTEGER_MODE_BCMATH: @@ -264,9 +271,8 @@ class Math_BigInteger { case 256: switch ( MATH_BIGINTEGER_MODE ) { case MATH_BIGINTEGER_MODE_GMP: - $temp = unpack('H*hex', $x); $sign = $this->is_negative ? '-' : ''; - $this->value = gmp_init($sign . '0x' . $temp['hex']); + $this->value = gmp_init($sign . '0x' . bin2hex($x)); break; case MATH_BIGINTEGER_MODE_BCMATH: // round $len to the nearest 4 (thanks, DavidMJ!) @@ -410,7 +416,7 @@ class Math_BigInteger { * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're * saved as two's compliment. * - * Here's a quick 'n dirty example: + * Here's an example: * * compare(new Math_BigInteger()); if ($comparison == 0) { - return ''; + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $temp = $comparison < 0 ? $this->add(new Math_BigInteger(1)) : $this->copy(); @@ -451,16 +457,19 @@ class Math_BigInteger { switch ( MATH_BIGINTEGER_MODE ) { case MATH_BIGINTEGER_MODE_GMP: if (gmp_cmp($this->value, gmp_init(0)) == 0) { - return ''; + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $temp = gmp_strval(gmp_abs($this->value), 16); $temp = ( strlen($temp) & 1 ) ? '0' . $temp : $temp; + $temp = pack('H*', $temp); - return ltrim(pack('H*', $temp), chr(0)); + return $this->precision > 0 ? + substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : + ltrim($temp, chr(0)); case MATH_BIGINTEGER_MODE_BCMATH: if ($this->value === '0') { - return ''; + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $value = ''; @@ -478,11 +487,13 @@ class Math_BigInteger { $current = bcdiv($current, 0x1000000); } - return ltrim($value, chr(0)); + return $this->precision > 0 ? + substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : + ltrim($value, chr(0)); } if (!count($this->value)) { - return ''; + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $result = $this->_int2bytes($this->value[count($this->value) - 1]); @@ -493,7 +504,9 @@ class Math_BigInteger { $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); } - return $result; + return $this->precision > 0 ? + substr(str_pad($result, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : + $result; } /** @@ -502,7 +515,7 @@ class Math_BigInteger { * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're * saved as two's compliment. * - * Here's a quick 'n dirty example: + * Here's an example: * * toBytes($twos_compliment)); - return $temp; + return bin2hex($this->toBytes($twos_compliment)); + } + + /** + * Converts a BigInteger to a bit string (eg. base-2). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * Here's an example: + * + * toBits(); // outputs '1000001' + * ?> + * + * + * @param Boolean $twos_compliment + * @return String + * @access public + * @internal Converts a base-2**26 number to base-2**2 + */ + function toBits($twos_compliment = false) + { + $hex = $this->toHex($twos_compliment); + $bits = ''; + for ($i = 0; $i < strlen($hex); $i+=8) { + $bits.= str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT); + } + return $this->precision > 0 ? $bits : ltrim($bits, '0'); } /** * Converts a BigInteger to a base-10 number. * - * Here's a quick 'n dirty example: + * Here's an example: * * value = $this->value; $temp->is_negative = $this->is_negative; $temp->generator = $this->generator; + $temp->precision = $this->precision; + $temp->bitmask = $this->bitmask; return $temp; } @@ -633,7 +679,7 @@ class Math_BigInteger { /** * Adds two BigIntegers. * - * Here's a quick 'n dirty example: + * Here's an example: * * value = gmp_add($this->value, $y->value); - return $temp; + return $this->_normalize($temp); case MATH_BIGINTEGER_MODE_BCMATH: $temp = new Math_BigInteger(); $temp->value = bcadd($this->value, $y->value); - return $temp; + return $this->_normalize($temp); } $this_size = count($this->value); @@ -687,14 +733,15 @@ class Math_BigInteger { $diff = $temp->compare($y); if ( !$diff ) { - return new Math_BigInteger(); + $temp = new Math_BigInteger(); + return $this->_normalize($temp); } $temp = $temp->subtract($y); $temp->is_negative = ($diff > 0) ? !$y_negative : $y_negative; - return $temp; + return $this->_normalize($temp); } $result = new Math_BigInteger(); @@ -723,13 +770,13 @@ class Math_BigInteger { $result->is_negative = $this->is_negative; - return $result->_normalize(); + return $this->_normalize($result); } /** * Subtracts two BigIntegers. * - * Here's a quick 'n dirty example: + * Here's an example: * * value = gmp_sub($this->value, $y->value); - return $temp; + return $this->_normalize($temp); case MATH_BIGINTEGER_MODE_BCMATH: $temp = new Math_BigInteger(); $temp->value = bcsub($this->value, $y->value); - return $temp; + return $this->_normalize($temp); } $this_size = count($this->value); @@ -786,13 +833,14 @@ class Math_BigInteger { $temp->is_negative = $is_negative; - return $temp; + return $this->_normalize($temp); } $diff = $this->compare($y); if ( !$diff ) { - return new Math_BigInteger(); + $temp = new Math_BigInteger(); + return $this->_normalize($temp); } // switch $this and $y around, if appropriate. @@ -806,7 +854,7 @@ class Math_BigInteger { $temp = $y->subtract($temp); $temp->is_negative = !$is_negative; - return $temp; + return $this->_normalize($temp); } $result = new Math_BigInteger(); @@ -834,13 +882,13 @@ class Math_BigInteger { $result->is_negative = $this->is_negative; - return $result->_normalize(); + return $this->_normalize($result); } /** * Multiplies two BigIntegers * - * Here's a quick 'n dirty example: + * Here's an example: * * value = gmp_mul($this->value, $x->value); - return $temp; + return $this->_normalize($temp); case MATH_BIGINTEGER_MODE_BCMATH: $temp = new Math_BigInteger(); $temp->value = bcmul($this->value, $x->value); - return $temp; + return $this->_normalize($temp); } static $cutoff = false; @@ -886,14 +934,15 @@ class Math_BigInteger { $x_length = count($x->value); if ( !$this_length || !$x_length ) { // a 0 is being multiplied - return new Math_BigInteger(); + $temp = new Math_BigInteger(); + return $this->_normalize($temp); } $product = min($this_length, $x_length) < $cutoff ? $this->_multiply($x) : $this->_karatsuba($x); $product->is_negative = $this->is_negative != $x->is_negative; - return $product; + return $this->_normalize($product); } /** @@ -959,7 +1008,7 @@ class Math_BigInteger { $product->is_negative = $this->is_negative != $x->is_negative; - return $product->_normalize(); + return $product; } /** @@ -1020,7 +1069,7 @@ class Math_BigInteger { $product->is_negative = $this->is_negative != $x->is_negative; - return $product->_normalize(); + return $this->_normalize($product); } /** @@ -1123,7 +1172,7 @@ class Math_BigInteger { $square->value[$i + $max_index + 1] = $carry; } - return $square->_normalize(); + return $square; } /** @@ -1174,7 +1223,7 @@ class Math_BigInteger { * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder * and the divisor (basically, the "common residue" is the first positive modulo). * - * Here's a quick 'n dirty example: + * Here's an example: * * value = gmp_add($remainder->value, gmp_abs($y->value)); } - return array($quotient, $remainder); + return array($this->_normalize($quotient), $this->_normalize($remainder)); case MATH_BIGINTEGER_MODE_BCMATH: $quotient = new Math_BigInteger(); $remainder = new Math_BigInteger(); @@ -1220,7 +1269,7 @@ class Math_BigInteger { $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value); } - return array($quotient, $remainder); + return array($this->_normalize($quotient), $this->_normalize($remainder)); } static $zero; @@ -1242,7 +1291,7 @@ class Math_BigInteger { $temp = new Math_BigInteger(); $temp->value = array(1); $temp->is_negative = $x_sign != $y_sign; - return array($temp, new Math_BigInteger()); + return array($this->_normalize($temp), $this->_normalize(new Math_BigInteger())); } if ( $diff < 0 ) { @@ -1250,7 +1299,7 @@ class Math_BigInteger { if ( $x_sign ) { $x = $y->subtract($x); } - return array(new Math_BigInteger(), $x); + return array($this->_normalize(new Math_BigInteger()), $this->_normalize($x)); } // normalize $x and $y as described in HAC 14.23 / 14.24 @@ -1347,13 +1396,13 @@ class Math_BigInteger { $x = $y->subtract($x); } - return array($quotient->_normalize(), $x); + return array($this->_normalize($quotient), $this->_normalize($x)); } /** * Performs modular exponentiation. * - * Here's a quick 'n dirty example: + * Here's an example: * * abs(); + $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); + if ($e->compare(new Math_BigInteger()) < 0) { $e = $e->abs(); @@ -1403,7 +1453,7 @@ class Math_BigInteger { return false; } - return $temp->modPow($e, $n); + return $this->_normalize($temp->modPow($e, $n)); } switch ( MATH_BIGINTEGER_MODE ) { @@ -1411,36 +1461,36 @@ class Math_BigInteger { $temp = new Math_BigInteger(); $temp->value = gmp_powm($this->value, $e->value, $n->value); - return $temp; + return $this->_normalize($temp); case MATH_BIGINTEGER_MODE_BCMATH: $temp = new Math_BigInteger(); $temp->value = bcpowmod($this->value, $e->value, $n->value); - return $temp; + return $this->_normalize($temp); } if ( empty($e->value) ) { $temp = new Math_BigInteger(); $temp->value = array(1); - return $temp; + return $this->_normalize($temp); } if ( $e->value == array(1) ) { list(, $temp) = $this->divide($n); - return $temp; + return $this->_normalize($temp); } if ( $e->value == array(2) ) { $temp = $this->_square(); list(, $temp) = $temp->divide($n); - return $temp; + return $this->_normalize($temp); } - return $this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT); + return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT)); // is the modulo odd? if ( $n->value[0] & 1 ) { - return $this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY); + return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY)); } // if it's not, it's even @@ -1476,7 +1526,7 @@ class Math_BigInteger { $result = $result->add($temp); list(, $result) = $result->divide($n); - return $result; + return $this->_normalize($result); } /** @@ -1594,7 +1644,7 @@ class Math_BigInteger { $result = $result->$reduce($n); - return $result->_normalize(); + return $result; } /** @@ -1669,7 +1719,7 @@ class Math_BigInteger { $temp = new Math_BigInteger(); $temp->value = array_slice($this->value, $n_length - 1); - $temp = $temp->_multiply($cache[MATH_BIGINTEGER_DATA][$key]); + $temp = $temp->multiply($cache[MATH_BIGINTEGER_DATA][$key]); $temp->value = array_slice($temp->value, $n_length + 1); $result = new Math_BigInteger(); @@ -1802,7 +1852,9 @@ class Math_BigInteger { /** * Calculates modular inverses. * - * Here's a quick 'n dirty example: + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * + * Here's an example: * * modInverse($b); - * * echo $c->toString(); // outputs 4 + * + * echo "\r\n"; + * + * $d = $a->multiply($c); + * list(, $d) = $d->divide($b); + * echo $d; // outputs 1 (as per the definition of modular inverse) * ?> * * * @param Math_BigInteger $n * @return mixed false, if no modular inverse exists, Math_BigInteger, otherwise. * @access public - * @internal Calculates the modular inverse of $this mod $n using the binary xGCD algorithim described in - * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes, - * the more traditional algorithim requires "relatively costly multiple-precision divisions". See - * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information. + * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information. */ function modInverse($n) { @@ -1831,103 +1885,138 @@ class Math_BigInteger { $temp = new Math_BigInteger(); $temp->value = gmp_invert($this->value, $n->value); - return ( $temp->value === false ) ? false : $temp; + return ( $temp->value === false ) ? false : $this->_normalize($temp); + } + + static $zero, $one; + if (!isset($zero)) { + $zero = new Math_BigInteger(); + $one = new Math_BigInteger(1); + } + + // $x mod $n == $x mod -$n. + $n = $n->abs(); + + if ($this->compare($zero) < 0) { + $temp = $this->abs(); + $temp = $temp->modInverse($n); + return $negated === false ? false : $this->_normalize($n->subtract($temp)); + } + + extract($this->extendedGCD($n)); + + if (!$gcd->equals($one)) { + return false; + } + + $x = $x->compare($zero) < 0 ? $x->add($n) : $x; + + return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x); + } + + /** + * Calculates the greatest common divisor and Bézout's identity. + * + * Say you have 693 and 609. The GCD is 21. Bézout's identity states that there exist integers x and y such that + * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which + * combination is returned is dependant upon which mode is in use. See + * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bézout's identity - Wikipedia} for more information. + * + * Here's an example: + * + * extendedGCD($b)); + * + * echo $gcd->toString() . "\r\n"; // outputs 21 + * echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21 + * ?> + * + * + * @param Math_BigInteger $n + * @return Math_BigInteger + * @access public + * @internal Calculates the GCD using the binary xGCD algorithim described in + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes, + * the more traditional algorithim requires "relatively costly multiple-precision divisions". + */ + function extendedGCD($n) { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + extract(gmp_gcdext($this->value, $n->value)); + + return array( + 'gcd' => $this->_normalize(new Math_BigInteger($g)), + 'x' => $this->_normalize(new Math_BigInteger($s)), + 'y' => $this->_normalize(new Math_BigInteger($t)) + ); case MATH_BIGINTEGER_MODE_BCMATH: // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is, // the basic extended euclidean algorithim is what we're using. - // if $x is less than 0, the first character of $x is a '-', so we'll remove it. we can do this because - // $x mod $n == $x mod -$n. - $n = (bccomp($n->value, '0') < 0) ? substr($n->value, 1) : $n->value; - - if (bccomp($this->value, '0') < 0) { - $negated_this = new Math_BigInteger(); - $negated_this->value = substr($this->value, 1); - - $temp = $negated_this->modInverse(new Math_BigInteger($n)); - - if ($temp === false) { - return false; - } - - $temp->value = bcsub($n, $temp->value); - - return $temp; - } - $u = $this->value; - $v = $n; + $v = $n->value; $a = '1'; + $b = '0'; $c = '0'; + $d = '1'; - while (true) { + while (bccomp($v, '0') != 0) { $q = bcdiv($u, $v); + $temp = $u; $u = $v; $v = bcsub($temp, bcmul($v, $q)); - if (bccomp($v, '0') == 0) { - break; - } - $temp = $a; $a = $c; - $c = bcsub($temp, bcmul($c, $q)); + $c = bcsub($temp, bcmul($a, $q)); + + $temp = $b; + $b = $d; + $d = bcsub($temp, bcmul($b, $q)); } - $temp = new Math_BigInteger(); - $temp->value = (bccomp($c, '0') < 0) ? bcadd($c, $n) : $c; - - // $u contains the gcd of $this and $n - return (bccomp($u, '1') == 0) ? $temp : false; + return array( + 'gcd' => $this->_normalize(new Math_BigInteger($u)), + 'x' => $this->_normalize(new Math_BigInteger($a)), + 'y' => $this->_normalize(new Math_BigInteger($b)) + ); } - // if $this and $n are even, return false. - if ( !($this->value[0]&1) && !($n->value[0]&1) ) { - return false; + $y = $n->copy(); + $x = $this->copy(); + $g = new Math_BigInteger(); + $g->value = array(1); + + while ( !(($x->value[0] & 1)|| ($y->value[0] & 1)) ) { + $x->_rshift(1); + $y->_rshift(1); + $g->_lshift(1); } - $n = $n->copy(); - $n->is_negative = false; - - if ($this->compare(new Math_BigInteger()) < 0) { - // is_negative is currently true. since we need it to be false, we'll just set it to false, temporarily, - // and reset it as true, later. - $this->is_negative = false; - - $temp = $this->modInverse($n); - - if ($temp === false) { - return false; - } - - $temp = $n->subtract($temp); - - $this->is_negative = true; - - return $temp; - } - - $u = $n->copy(); - $x = $this; - //list(, $x) = $this->divide($n); - $v = $x->copy(); + $u = $x->copy(); + $v = $y->copy(); $a = new Math_BigInteger(); $b = new Math_BigInteger(); $c = new Math_BigInteger(); $d = new Math_BigInteger(); - $a->value = $d->value = array(1); + $a->value = $d->value = $g->value = array(1); while ( !empty($u->value) ) { while ( !($u->value[0] & 1) ) { $u->_rshift(1); if ( ($a->value[0] & 1) || ($b->value[0] & 1) ) { - $a = $a->add($x); - $b = $b->subtract($n); + $a = $a->add($y); + $b = $b->subtract($x); } $a->_rshift(1); $b->_rshift(1); @@ -1936,8 +2025,8 @@ class Math_BigInteger { while ( !($v->value[0] & 1) ) { $v->_rshift(1); if ( ($c->value[0] & 1) || ($d->value[0] & 1) ) { - $c = $c->add($x); - $d = $d->subtract($n); + $c = $c->add($y); + $d = $d->subtract($x); } $c->_rshift(1); $d->_rshift(1); @@ -1952,18 +2041,42 @@ class Math_BigInteger { $c = $c->subtract($a); $d = $d->subtract($b); } - - $u->_normalize(); } - // at this point, $v == gcd($this, $n). if it's not equal to 1, no modular inverse exists. - if ( $v->value != array(1) ) { - return false; - } + return array( + 'gcd' => $this->_normalize($g->multiply($v)), + 'x' => $this->_normalize($c), + 'y' => $this->_normalize($d) + ); + } - $d = ($d->compare(new Math_BigInteger()) < 0) ? $d->add($n) : $d; - - return ($this->is_negative) ? $n->subtract($d) : $d; + /** + * Calculates the greatest common divisor + * + * Say you have 693 and 609. The GCD is 21. + * + * Here's an example: + * + * extendedGCD($b); + * + * echo $gcd->toString() . "\r\n"; // outputs 21 + * ?> + * + * + * @param Math_BigInteger $n + * @return Math_BigInteger + * @access public + */ + function gcd($n) + { + extract($this->extendedGCD($n)); + return $gcd; } /** @@ -2008,31 +2121,31 @@ class Math_BigInteger { * @see equals() * @internal Could return $this->sub($x), but that's not as fast as what we do do. */ - function compare($x) + function compare($y) { switch ( MATH_BIGINTEGER_MODE ) { case MATH_BIGINTEGER_MODE_GMP: - return gmp_cmp($this->value, $x->value); + return gmp_cmp($this->value, $y->value); case MATH_BIGINTEGER_MODE_BCMATH: - return bccomp($this->value, $x->value); + return bccomp($this->value, $y->value); } - $this->_normalize(); - $x->_normalize(); + $x = $this->_normalize($this->copy()); + $y = $this->_normalize($y); - if ( $this->is_negative != $x->is_negative ) { - return ( !$this->is_negative && $x->is_negative ) ? 1 : -1; + if ( $x->is_negative != $y->is_negative ) { + return ( !$x->is_negative && $y->is_negative ) ? 1 : -1; } - $result = $this->is_negative ? -1 : 1; + $result = $x->is_negative ? -1 : 1; - if ( count($this->value) != count($x->value) ) { - return ( count($this->value) > count($x->value) ) ? $result : -$result; + if ( count($x->value) != count($y->value) ) { + return ( count($x->value) > count($y->value) ) ? $result : -$result; } - for ($i = count($this->value) - 1; $i >= 0; $i--) { - if ($this->value[$i] != $x->value[$i]) { - return ( $this->value[$i] > $x->value[$i] ) ? $result : -$result; + for ($i = count($x->value) - 1; $i >= 0; $i--) { + if ($x->value[$i] != $y->value[$i]) { + return ( $x->value[$i] > $y->value[$i] ) ? $result : -$result; } } @@ -2057,6 +2170,26 @@ class Math_BigInteger { default: return $this->value == $x->value && $this->is_negative == $x->is_negative; } + } + + /** + * Set Precision + * + * Some bitwise operations give different results depending on the precision being used. Examples include left + * shift, not, and rotates. + * + * @param Math_BigInteger $x + * @access public + * @return Math_BigInteger + */ + function setPrecision($bits) + { + $this->precision = $bits; + if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ) { + $this->bitmask = new Math_BigInteger(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); + } else { + $this->bitmask = new Math_BigInteger(bcpow('2', $bits)); + } } /** @@ -2074,19 +2207,30 @@ class Math_BigInteger { $temp = new Math_BigInteger(); $temp->value = gmp_and($this->value, $x->value); - return $temp; + return $this->_normalize($temp); case MATH_BIGINTEGER_MODE_BCMATH: - return new Math_BigInteger($this->toBytes() & $x->toBytes(), 256); + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new Math_BigInteger($left & $right, 256)); } - $result = new Math_BigInteger(); + $result = $this->copy(); - $x_length = count($x->value); - for ($i = 0; $i < $x_length; $i++) { - $result->value[] = $this->value[$i] & $x->value[$i]; + $length = min(count($x->value), count($this->value)); + + $result->value = array_slice($result->value, 0, $length); + + for ($i = 0; $i < $length; $i++) { + $result->value[$i] = $result->value[$i] & $x->value[$i]; } - return $result->_normalize(); + return $this->_normalize($result); } /** @@ -2104,19 +2248,29 @@ class Math_BigInteger { $temp = new Math_BigInteger(); $temp->value = gmp_or($this->value, $x->value); - return $temp; + return $this->_normalize($temp); case MATH_BIGINTEGER_MODE_BCMATH: - return new Math_BigInteger($this->toBytes() | $x->toBytes(), 256); + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new Math_BigInteger($left | $right, 256)); } + $length = max(count($this->value), count($x->value)); $result = $this->copy(); + $result->value = array_pad($result->value, 0, $length); + $x->value = array_pad($x->value, 0, $length); - $x_length = count($x->value); - for ($i = 0; $i < $x_length; $i++) { + for ($i = 0; $i < $length; $i++) { $result->value[$i] = $this->value[$i] | $x->value[$i]; } - return $result->_normalize(); + return $this->_normalize($result); } /** @@ -2134,67 +2288,65 @@ class Math_BigInteger { $temp = new Math_BigInteger(); $temp->value = gmp_xor($this->value, $x->value); - return $temp; + return $this->_normalize($temp); case MATH_BIGINTEGER_MODE_BCMATH: - return new Math_BigInteger($this->toBytes() ^ $x->toBytes(), 256); + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new Math_BigInteger($left ^ $right, 256)); } + $length = max(count($this->value), count($x->value)); $result = $this->copy(); + $result->value = array_pad($result->value, 0, $length); + $x->value = array_pad($x->value, 0, $length); - $x_length = count($x->value); - for ($i = 0; $i < $x_length; $i++) { + for ($i = 0; $i < $length; $i++) { $result->value[$i] = $this->value[$i] ^ $x->value[$i]; } - return $result->_normalize(); + return $this->_normalize($result); } /** * Logical Not * - * Although integers can be converted to and from various bases with relative ease, there is one piece - * of information that is lost during such conversions. The number of leading zeros that number had - * or should have in any given base. Per that, if you convert 1 from decimal to binary, there's no - * way to know just how many leading zero's there should be. In truth, there could be any number. - * - * Normally, the number of leading zero's is unimportant. When doing "not", however, it is. The "not" - * of 1 on an 8-bit representation of 1 is 1111 1110. The "not" of 1 on a 16-bit representation of 1 is - * 1111 1111 1111 1110. When doing it on a number that's preceeded by an infinite number of zero's, it's - * infinite. - * - * This function assumes that there are no leading zero's - that the bit-representation being used is - * equal to the minimum number of required bits, unless otherwise specified in the optional parameter, - * where the optional parameter represents the bit-representation being used. If the specified - * bit-representation is smaller than the minimum number of bits required to represent the number, the - * latter will be used as the bit-representation. - * - * @param $bits Integer * @access public * @internal Implemented per a request by Lluis Pamies i Juarez * @return Math_BigInteger */ - function bitwise_not($bits = -1) + function bitwise_not() { - // calculuate "not" without regard to $bits - $temp = ~$this->toBytes(); + // calculuate "not" without regard to $this->precision + // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) + $temp = $this->toBytes(); + $pre_msb = decbin(ord($temp[0])); + $temp = ~$temp; $msb = decbin(ord($temp[0])); - $msb = substr($msb, strpos($msb, '0')); + if (strlen($msb) == 8) { + $msb = substr($msb, strpos($msb, '0')); + } $temp[0] = chr(bindec($msb)); // see if we need to add extra leading 1's - $current_bits = strlen($msb) + 8 * strlen($temp) - 8; - $new_bits = $bits - $current_bits; + $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8; + $new_bits = $this->precision - $current_bits; if ($new_bits <= 0) { - return new Math_BigInteger($temp, 256); + return $this->_normalize(new Math_BigInteger($temp, 256)); } // generate as many leading 1's as we need to. $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3); $this->_base256_lshift($leading_ones, $current_bits); - $temp = str_pad($temp, ceil($bits / 8), chr(0), STR_PAD_LEFT); + $temp = str_pad($temp, ceil($this->bits / 8), chr(0), STR_PAD_LEFT); - return new Math_BigInteger($leading_ones | $temp, 256); + return $this->_normalize(new Math_BigInteger($leading_ones | $temp, 256)); } /** @@ -2232,7 +2384,7 @@ class Math_BigInteger { $temp->_rshift($shift); } - return $temp; + return $this->_normalize($temp); } /** @@ -2270,7 +2422,65 @@ class Math_BigInteger { $temp->_lshift($shift); } - return $temp; + return $this->_normalize($temp); + } + + /** + * Logical Left Rotate + * + * Instead of the top x bits being dropped they're appended to the shifted bit string. + * + * @param Integer $shift + * @return Math_BigInteger + * @access public + */ + function bitwise_leftRotate($shift) + { + $bits = $this->toBytes(); + + if ($this->precision > 0) { + $precision = $this->precision; + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) { + $mask = $this->bitmask->subtract(new Math_BigInteger(1)); + $mask = $mask->toBytes(); + } else { + $mask = $this->bitmask->toBytes(); + } + } else { + $temp = ord($bits[0]); + for ($i = 0; $temp >> $i; $i++); + $precision = 8 * strlen($bits) - 8 + $i; + $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3); + } + + if ($shift < 0) { + $shift+= $precision; + } + $shift%= $precision; + + if (!$shift) { + return $this->copy(); + } + + $left = $this->bitwise_leftShift($shift); + $left = $left->bitwise_and(new Math_BigInteger($mask, 256)); + $right = $this->bitwise_rightShift($precision - $shift); + $result = MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right); + return $this->_normalize($result); + } + + /** + * Logical Right Rotate + * + * Instead of the bottom x bits being dropped they're prepended to the shifted bit string. + * + * @param Integer $shift + * @return Math_BigInteger + * @access public + */ + function bitwise_rightRotate($shift) + { + return $this->bitwise_leftRotate(-$shift); } /** @@ -2313,7 +2523,7 @@ class Math_BigInteger { $compare = $max->compare($min); if (!$compare) { - return $min; + return $this->_normalize($min); } else if ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; @@ -2347,21 +2557,23 @@ class Math_BigInteger { $random = new Math_BigInteger($random, 256); - return $random->add($min); + return $this->_normalize($random->add($min)); } /** * Generate a random prime number. * - * If there's not a prime within the given range, false will be returned. + * If there's not a prime within the given range, false will be returned. If more than $timeout seconds have elapsed, + * give up and return false. * * @param optional Integer $min * @param optional Integer $max + * @param optional Integer $timeout * @return Math_BigInteger * @access public * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}. */ - function randomPrime($min = false, $max = false) + function randomPrime($min = false, $max = false, $timeout = false) { // gmp_nextprime() requires PHP 5 >= 5.2.0 per . if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) { @@ -2408,7 +2620,13 @@ class Math_BigInteger { $one = new Math_BigInteger(1); $two = new Math_BigInteger(2); + $start = time(); + do { + if ($timeout !== false && time() - $start > $timeout) { + return false; + } + $x = $this->random($min, $max); if ($x->equals($two)) { @@ -2421,7 +2639,7 @@ class Math_BigInteger { gmp_setbit($x->value, 0); break; case MATH_BIGINTEGER_MODE_BCMATH: - if ($this->value[strlen($this->value) - 1] % 2 == 0) { + if ($x->value[strlen($x->value) - 1] % 2 == 0) { $x = $x->add($one); } break; @@ -2451,7 +2669,7 @@ class Math_BigInteger { * $t parameter is distributability. Math_BigInteger::randomPrime() can be distributed accross multiple pageloads * on a website instead of just one. * - * @param optional Integer $min + * @param optional Integer $t * @return Boolean * @access public * @internal Uses the @@ -2644,33 +2862,57 @@ class Math_BigInteger { $carry = ($this->value[$i] & $carry_mask) << $carry_shift; $this->value[$i] = $temp; } - - $this->_normalize(); } /** * Normalize * - * Deletes leading zeros. + * Deletes leading zeros and truncates (if necessary) to maintain the appropriate precision * - * @see divide() * @return Math_BigInteger * @access private */ - function _normalize() + function _normalize($result) { - if ( !count($this->value) ) { - return $this; + $result->precision = $this->precision; + $result->bitmask = $this->bitmask; + + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + if (!empty($result->bitmask->value)) { + $result->value = gmp_and($result->value, $result->bitmask->value); + } + + return $result; + case MATH_BIGINTEGER_MODE_BCMATH: + if (!empty($result->bitmask->value)) { + $result->value = bcmod($result->value, $result->bitmask->value); + } + + return $result; } - for ($i=count($this->value) - 1; $i >= 0; $i--) { - if ( $this->value[$i] ) { + if ( !count($result->value) ) { + return $result; + } + + for ($i = count($result->value) - 1; $i >= 0; $i--) { + if ( $result->value[$i] ) { break; } - unset($this->value[$i]); + unset($result->value[$i]); } - return $this; + if (!empty($result->bitmask->value)) { + $length = min(count($result->value), count($this->bitmask->value)); + $result->value = array_slice($result->value, 0, $length); + + for ($i = 0; $i < $length; $i++) { + $result->value[$i] = $result->value[$i] & $this->bitmask->value[$i]; + } + } + + return $result; } /** diff --git a/phpseclib/Net/SFTP.php b/phpseclib/Net/SFTP.php index 78c7d697..b32f8ab6 100644 --- a/phpseclib/Net/SFTP.php +++ b/phpseclib/Net/SFTP.php @@ -48,7 +48,7 @@ * @author Jim Wigginton * @copyright MMIX Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: SFTP.php,v 1.9 2009-10-16 03:37:24 terrafrost Exp $ + * @version $Id: SFTP.php,v 1.10 2009-11-23 19:06:07 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -318,15 +318,16 @@ class Net_SFTP extends Net_SSH2 { return false; } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: $this->_string_shift($response, 4); // skip over client channel - list(, $server_channel) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); $this->server_channels[$this->client_channel] = $server_channel; $this->_string_shift($response, 4); // skip over (server) window size - list(, $this->packet_size_client_to_server) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4))); + $this->packet_size_client_to_server = $packet_size_client_to_server; break; case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: user_error('Unable to open channel', E_USER_NOTICE); @@ -345,7 +346,7 @@ class Net_SFTP extends Net_SSH2 { return false; } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_CHANNEL_SUCCESS: @@ -366,11 +367,12 @@ class Net_SFTP extends Net_SSH2 { return false; } - list(, $this->version) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nversion', $this->_string_shift($response, 4))); + $this->version = $version; while (!empty($response)) { - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $key = $this->_string_shift($response, $length); - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $value = $this->_string_shift($response, $length); $this->extensions[$key] = $value; } @@ -502,13 +504,13 @@ class Net_SFTP extends Net_SSH2 { // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks // at is the first part and that part is defined the same in SFTP versions 3 through 6. $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $realpath = $this->_string_shift($response, $length); break; case NET_SFTP_STATUS: // skip over the status code - hopefully the error message will give us all the info we need, anyway $this->_string_shift($response, 4); - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->debug_info.= "\r\n\r\nSSH_FXP_STATUS:\r\n" . $this->_string_shift($response, $length); return false; default: @@ -622,11 +624,11 @@ class Net_SFTP extends Net_SSH2 { $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_NAME: - list(, $count) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Ncount', $this->_string_shift($response, 4))); for ($i = 0; $i < $count; $i++) { - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $contents[] = $this->_string_shift($response, $length); - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); // we don't care about the longname $this->_parseAttributes($response); // we also don't care about the attributes // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the @@ -634,9 +636,9 @@ class Net_SFTP extends Net_SSH2 { } break; case NET_SFTP_STATUS: - list(, $status) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_EOF) { - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->debug_info.= "\r\n\r\nSSH_FXP_STATUS:\r\n" . $this->_string_shift($response, $length); return false; } @@ -754,9 +756,9 @@ class Net_SFTP extends Net_SSH2 { return false; } - list(, $status) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->debug_info.= "\r\n\r\nSSH_FXP_STATUS:\r\n" . $this->_string_shift($response, $length); return false; } @@ -792,7 +794,7 @@ class Net_SFTP extends Net_SSH2 { return false; } - list(, $status) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED? return false; @@ -845,7 +847,7 @@ class Net_SFTP extends Net_SSH2 { break; case NET_SFTP_STATUS: $this->_string_shift($response, 4); - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->debug_info.= "\r\n\r\nSSH_FXP_STATUS:\r\n" . $this->_string_shift($response, $length); return false; default: @@ -978,10 +980,9 @@ class Net_SFTP extends Net_SSH2 { $content = ''; } -define('DEBUG2', true); $read = 0; while ($read < $attrs['size']) { - $packet = pack('Na*N3', strlen($handle), $handle, 0, $read, 1 << 20); //100000); // 100000 is completely arbitrarily chosen + $packet = pack('Na*N3', strlen($handle), $handle, 0, $read, 1 << 20); if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { return false; } @@ -999,7 +1000,7 @@ define('DEBUG2', true); break; case NET_SFTP_STATUS: $this->_string_shift($response, 4); - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->debug_info.= "\r\n\r\nSSH_FXP_STATUS:\r\n" . $this->_string_shift($response, $length); return false; default: @@ -1007,9 +1008,6 @@ define('DEBUG2', true); return false; } } -echo "decrypted: " . $this->decrypt->total2 . "\r\n"; -echo "encrypted: " . $this->encrypt->total2 . "\r\n"; -echo "...: " . $this->encrypt->tests . "\r\n"; if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { return false; @@ -1058,7 +1056,7 @@ echo "...: " . $this->encrypt->tests . "\r\n"; return false; } - list(, $status) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nstatus', $this->_string_shift($response, 4))); // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED return $status == NET_SFTP_STATUS_OK; @@ -1096,7 +1094,7 @@ echo "...: " . $this->encrypt->tests . "\r\n"; return false; } - list(, $status) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nstatus', $this->_string_shift($response, 4))); // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED return $status == NET_SFTP_STATUS_OK; @@ -1114,7 +1112,7 @@ echo "...: " . $this->encrypt->tests . "\r\n"; function _parseAttributes(&$response) { $attr = array(); - list(, $flags) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nflags', $this->_string_shift($response, 4))); // SFTPv4+ have a type field (a byte) that follows the above flag field foreach ($this->attributes as $key => $value) { switch ($flags & $key) { @@ -1124,29 +1122,28 @@ echo "...: " . $this->encrypt->tests . "\r\n"; // of course, you shouldn't be using Net_SFTP to transfer files that are in excess of 4GB // (0xFFFFFFFF bytes), anyway. as such, we'll just represent all file sizes that are bigger than // 4GB as being 4GB. - list(, $upper) = unpack('N', $this->_string_shift($response, 4)); - list(, $attr['size']) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nupper/Nsize', $this->_string_shift($response, 8))); if ($upper) { $attr['size'] = 0xFFFFFFFF; + } else { + $attr['size'] = $size; } break; case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only) - list(, $attr['uid']) = unpack('N', $this->_string_shift($response, 4)); - list(, $attr['gid']) = unpack('N', $this->_string_shift($response, 4)); + $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8)); break; case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 - list(, $attr['permissions']) = unpack('N', $this->_string_shift($response, 4)); + $attr+= unpack('Npermissions', $this->_string_shift($response, 4)); break; case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 - list(, $attr['atime']) = unpack('N', $this->_string_shift($response, 4)); - list(, $attr['mtime']) = unpack('N', $this->_string_shift($response, 4)); + $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); break; case NET_SFTP_ATTR_EXTENDED: // 0x80000000 - list(, $count) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Ncount', $this->_string_shift($response, 4))); for ($i = 0; $i < $count; $i++) { - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $key = $this->_string_shift($response, $length); - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $attr[$key] = $this->_string_shift($response, $length); } } @@ -1212,7 +1209,7 @@ echo "...: " . $this->encrypt->tests . "\r\n"; } $this->packet_buffer.= $temp; } - list(, $length) = unpack('N', $this->_string_shift($this->packet_buffer, 4)); + extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4))); $tempLength = $length; $tempLength-= strlen($this->packet_buffer); diff --git a/phpseclib/Net/SSH1.php b/phpseclib/Net/SSH1.php index 9a04da68..ac12bced 100644 --- a/phpseclib/Net/SSH1.php +++ b/phpseclib/Net/SSH1.php @@ -65,7 +65,7 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: SSH1.php,v 1.12 2009-06-09 04:00:38 terrafrost Exp $ + * @version $Id: SSH1.php,v 1.13 2009-11-23 19:06:07 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -440,7 +440,7 @@ class Net_SSH1 { $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4); // get a list of the supported ciphers - list(, $supported_ciphers_mask) = unpack('N', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4)); + extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4))); foreach ($this->supported_ciphers as $mask=>$name) { if (($supported_ciphers_mask & (1 << $mask)) == 0) { unset($this->supported_ciphers[$mask]); @@ -448,7 +448,7 @@ class Net_SSH1 { } // get a list of the supported authentications - list(, $supported_authentications_mask) = unpack('N', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4)); + extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4))); foreach ($this->supported_authentications as $mask=>$name) { if (($supported_authentications_mask & (1 << $mask)) == 0) { unset($this->supported_authentications[$mask]); diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 877517fc..e09ebf65 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -41,7 +41,7 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: SSH2.php,v 1.25 2009-11-03 22:03:43 terrafrost Exp $ + * @version $Id: SSH2.php,v 1.26 2009-11-23 19:06:07 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -625,10 +625,12 @@ class Net_SSH2 { ); static $encryption_algorithms = array( - 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key 'aes128-cbc', // RECOMMENDED AES with a 128-bit key 'aes192-cbc', // OPTIONAL AES with a 192-bit key 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key + + 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key + '3des-cbc', // REQUIRED three-key 3DES in CBC mode 'none' // OPTIONAL no encryption; NOT RECOMMENDED ); @@ -665,7 +667,7 @@ class Net_SSH2 { $response = $kexinit_payload_server; $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) - list(, $server_cookie) = unpack('a16', $this->_string_shift($response, 16)); + extract(unpack('a16server_cookie', $this->_string_shift($response, 16))); $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); @@ -697,7 +699,7 @@ class Net_SSH2 { $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - list(, $first_kex_packet_follows) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); $first_kex_packet_follows = $first_kex_packet_follows != 0; // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place. @@ -840,8 +842,7 @@ class Net_SSH2 { user_error('Connection closed by server', E_USER_NOTICE); return false; } - - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_KEXDH_REPLY) { user_error('Expected SSH_MSG_KEXDH_REPLY', E_USER_NOTICE); @@ -996,7 +997,7 @@ class Net_SSH2 { return false; } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_NEWKEYS) { user_error('Expected SSH_MSG_NEWKEYS', E_USER_NOTICE); @@ -1200,7 +1201,7 @@ class Net_SSH2 { return false; } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { user_error('Expected SSH_MSG_SERVICE_ACCEPT', E_USER_NOTICE); @@ -1233,15 +1234,15 @@ class Net_SSH2 { return false; } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->debug_info.= "\r\n\r\nSSH_MSG_USERAUTH_PASSWD_CHANGEREQ:\r\n" . utf8_decode($this->_string_shift($response, $length)); return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); case NET_SSH2_MSG_USERAUTH_FAILURE: - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->debug_info.= "\r\n\r\nSSH_MSG_USERAUTH_FAILURE:\r\n" . $this->_string_shift($response, $length); return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); case NET_SSH2_MSG_USERAUTH_SUCCESS: @@ -1296,15 +1297,16 @@ class Net_SSH2 { return false; } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: $this->_string_shift($response, 4); // skip over client channel - list(, $server_channel) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); $this->server_channels[$client_channel] = $server_channel; $this->_string_shift($response, 4); // skip over (server) window size - list(, $this->packet_size_client_to_server) = unpack('N', $this->_string_shift($response, 4)); + $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)); + $this->packet_size_client_to_server = $temp['packet_size_client_to_server']; break; case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: user_error('Unable to open channel', E_USER_NOTICE); @@ -1326,7 +1328,7 @@ class Net_SSH2 { return false; } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_CHANNEL_SUCCESS: @@ -1352,7 +1354,7 @@ class Net_SSH2 { return false; } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_CHANNEL_SUCCESS: @@ -1424,9 +1426,7 @@ class Net_SSH2 { $raw = $this->decrypt->decrypt($raw); } - $temp = unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)); - $packet_length = $temp['packet_length']; - $padding_length = $temp['padding_length']; + extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5))); $remaining_length = $packet_length + 4 - $this->decrypt_block_size; $buffer = ''; @@ -1480,7 +1480,7 @@ class Net_SSH2 { switch (ord($payload[0])) { case NET_SSH2_MSG_DISCONNECT: $this->_string_shift($payload, 1); - list(, $reason_code, $length) = unpack('N2', $this->_string_shift($payload, 8)); + extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); $this->debug_info.= "\r\n\r\nSSH_MSG_DISCONNECT:\r\n" . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length)); $this->bitmask = 0; return false; @@ -1489,7 +1489,7 @@ class Net_SSH2 { break; case NET_SSH2_MSG_DEBUG: $this->_string_shift($payload, 2); - list(, $length) = unpack('N', $payload); + extract(unpack('Nlength', $this->_string_shift($payload, 4))); $this->debug_info.= "\r\n\r\nSSH_MSG_DEBUG:\r\n" . utf8_decode($this->_string_shift($payload, $length)); $payload = $this->_get_binary_packet(); break; @@ -1508,7 +1508,7 @@ class Net_SSH2 { // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { $this->_string_shift($payload, 1); - list(, $length) = unpack('N', $payload); + extract(unpack('Nlength', $this->_string_shift($payload, 4))); $this->debug_info.= "\r\n\r\nSSH_MSG_USERAUTH_BANNER:\r\n" . utf8_decode($this->_string_shift($payload, $length)); $payload = $this->_get_binary_packet(); } @@ -1518,7 +1518,7 @@ class Net_SSH2 { switch (ord($payload[0])) { case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4 $this->_string_shift($payload, 1); - list(, $length) = unpack('N', $payload); + extract(unpack('Nlength', $this->_string_shift($payload))); $this->debug_info.= "\r\n\r\nSSH_MSG_GLOBAL_REQUEST:\r\n" . utf8_decode($this->_string_shift($payload, $length)); if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) { @@ -1529,11 +1529,11 @@ class Net_SSH2 { break; case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 $this->_string_shift($payload, 1); - list(, $length) = unpack('N', $payload); + extract(unpack('N', $this->_string_shift($payload, 4))); $this->debug_info.= "\r\n\r\nSSH_MSG_CHANNEL_OPEN:\r\n" . utf8_decode($this->_string_shift($payload, $length)); $this->_string_shift($payload, 4); // skip over client channel - list(, $server_channel) = unpack('N', $this->_string_shift($payload, 4)); + extract(unpack('Nserver_channel', $this->_string_shift($payload, 4))); $packet = pack('CN3a*Na*', NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, ''); @@ -1569,16 +1569,16 @@ class Net_SSH2 { return false; } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_CHANNEL_DATA: $this->_string_shift($response, 4); // skip over client channel - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); return $this->_string_shift($response, $length); case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: $this->_string_shift($response, 4); // skip over client channel - list(, $data_type_code, $length) = unpack('N2', $this->_string_shift($response, 8)); + extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); $data = $this->_string_shift($response, $length); switch ($data_type_code) { case NET_SSH2_EXTENDED_DATA_STDERR: @@ -1587,15 +1587,15 @@ class Net_SSH2 { break; case NET_SSH2_MSG_CHANNEL_REQUEST: $this->_string_shift($response, 4); // skip over client channel - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $value = $this->_string_shift($response, $length); switch ($value) { case 'exit-signal': $this->_string_shift($response, 1); - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->debug_info.= "\r\n\r\nSSH_MSG_CHANNEL_REQUEST (exit-signal):\r\nSIG" . $this->_string_shift($response, $length); $this->_string_shift($response, 1); - list(, $length) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->debug_info.= "\r\n" . $this->_string_shift($response, $length); case 'exit-status': default: @@ -1606,7 +1606,7 @@ class Net_SSH2 { } break; case NET_SSH2_MSG_CHANNEL_CLOSE: - list(, $client_channel) = unpack('N', $this->_string_shift($response, 4)); + extract(unpack('Nclient_channel', $this->_string_shift($response, 4))); $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); return true; case NET_SSH2_MSG_CHANNEL_EOF: