From 42fb96db16a3c469e0048387b3b1c743c64d8340 Mon Sep 17 00:00:00 2001 From: terrafrost Date: Sat, 1 Oct 2016 10:48:42 -0500 Subject: [PATCH] fix float to int conversions on 32-bit linux PHP < 5.3 installs --- phpseclib/Crypt/Blowfish.php | 72 ++++++++++++++++++++++++------------ phpseclib/Crypt/Hash.php | 9 ++++- 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/phpseclib/Crypt/Blowfish.php b/phpseclib/Crypt/Blowfish.php index a9b1edac..e610209b 100644 --- a/phpseclib/Crypt/Blowfish.php +++ b/phpseclib/Crypt/Blowfish.php @@ -478,16 +478,14 @@ class Crypt_Blowfish extends Crypt_Base for ($i = 0; $i < 16; $i+= 2) { $l^= $p[$i]; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ + $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^ $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]; + $sb_3[$l & 0xff]); $r^= $p[$i + 1]; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ + $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^ $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]; + $sb_3[$r & 0xff]); } return pack("N*", $r ^ $p[17], $l ^ $p[16]); } @@ -513,16 +511,14 @@ class Crypt_Blowfish extends Crypt_Base for ($i = 17; $i > 2; $i-= 2) { $l^= $p[$i]; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ + $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^ $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]; + $sb_3[$l & 0xff]); $r^= $p[$i - 1]; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ + $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^ $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]; + $sb_3[$r & 0xff]); } return pack("N*", $r ^ $p[0], $l ^ $p[1]); } @@ -548,6 +544,18 @@ class Crypt_Blowfish extends Crypt_Base $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } + // on 32-bit linux systems with PHP < 5.3 float to integer conversion is bad + switch (true) { + case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8: + case version_compare(PHP_VERSION, '5.3.0') >= 0: + case (PHP_OS & "\xDF\xDF\xDF") === 'WIN': + $safeint = '%s'; + break; + default: + $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | '; + $safeint.= '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))'; + } + if (!isset($lambda_functions[$code_hash])) { switch (true) { case $gen_hi_opt_code: @@ -583,16 +591,14 @@ class Crypt_Blowfish extends Crypt_Base for ($i = 0; $i < 16; $i+= 2) { $encrypt_block.= ' $l^= ' . $p[$i] . '; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ + $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]; + $sb_3[$l & 0xff]') . '; $r^= ' . $p[$i + 1] . '; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ + $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]; + $sb_3[$r & 0xff]') . '; '; } $encrypt_block.= ' @@ -612,16 +618,14 @@ class Crypt_Blowfish extends Crypt_Base for ($i = 17; $i > 2; $i-= 2) { $decrypt_block.= ' $l^= ' . $p[$i] . '; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ + $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]; + $sb_3[$l & 0xff]') . '; $r^= ' . $p[$i - 1] . '; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ + $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]; + $sb_3[$r & 0xff]') . '; '; } @@ -644,4 +648,24 @@ class Crypt_Blowfish extends Crypt_Base } $this->inline_crypt = $lambda_functions[$code_hash]; } + + /** + * Convert float to int + * + * On 32-bit Linux installs running PHP < 5.3 converting floats to ints doesn't always work + * + * @access private + * @param string $x + * @return int + */ + function safe_intval($x) + { + // PHP 5.3, per http://php.net/releases/5_3_0.php, introduced "more consistent float rounding" + // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster + if (is_int($x) || version_compare(PHP_VERSION, '5.3.0') >= 0 || (PHP_OS & "\xDF\xDF\xDF") === 'WIN') { + return $x; + } + return (fmod($x, 0x80000000) & 0x7FFFFFFF) | + ((fmod(floor($x / 0x80000000), 2) & 1) << 31); + } } diff --git a/phpseclib/Crypt/Hash.php b/phpseclib/Crypt/Hash.php index 5dda8eae..53b72c85 100644 --- a/phpseclib/Crypt/Hash.php +++ b/phpseclib/Crypt/Hash.php @@ -820,7 +820,14 @@ class Crypt_Hash $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument; } - return fmod($result, $mod); + // PHP 5.3, per http://php.net/releases/5_3_0.php, introduced "more consistent float rounding" + // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster + if (is_int($result) || version_compare(PHP_VERSION, '5.3.0') >= 0 || (PHP_OS & "\xDF\xDF\xDF") === 'WIN') { + return fmod($result, $mod); + } + + return (fmod($result, 0x80000000) & 0x7FFFFFFF) | + ((fmod(floor($result / 0x80000000), 2) & 1) << 31); } /**