From c7a6431e099067becda33986ee664fe52ea10e33 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Thu, 28 Nov 2013 23:43:57 +0000 Subject: [PATCH] Feature: (trvrnrth) Work Item GH-261 - Add support for reading protected (RC4 encrypted) .xls files (tweaked for PHP 5.2 compatibility) --- Classes/PHPExcel/Reader/Excel5.php | 2 +- Classes/PHPExcel/Reader/Excel5/MD5.php | 346 ++++++++++++------------- changelog.txt | 2 +- 3 files changed, 171 insertions(+), 179 deletions(-) diff --git a/Classes/PHPExcel/Reader/Excel5.php b/Classes/PHPExcel/Reader/Excel5.php index 601c492d..b9932e63 100644 --- a/Classes/PHPExcel/Reader/Excel5.php +++ b/Classes/PHPExcel/Reader/Excel5.php @@ -1687,7 +1687,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce substr($recordData, 38, 16), $this->_md5Ctxt )) { - throw new \Exception('Decryption password incorrect'); + throw new PHPExcel_Reader_Exception('Decryption password incorrect'); } $this->_encryption = self::MS_BIFF_CRYPTO_RC4; diff --git a/Classes/PHPExcel/Reader/Excel5/MD5.php b/Classes/PHPExcel/Reader/Excel5/MD5.php index 62e05156..3663236b 100644 --- a/Classes/PHPExcel/Reader/Excel5/MD5.php +++ b/Classes/PHPExcel/Reader/Excel5/MD5.php @@ -21,209 +21,201 @@ * @category PHPExcel * @package PHPExcel_Reader_Excel5 * @copyright Copyright (c) 2006 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL * @version ##VERSION##, ##DATE## */ + /** * PHPExcel_Reader_Excel5_MD5 * - * @category PHPExcel - * @package PHPExcel_Reader_Excel5 - * @copyright Copyright (c) 2006 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel) + * @category PHPExcel + * @package PHPExcel_Reader_Excel5 + * @copyright Copyright (c) 2006 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel) */ class PHPExcel_Reader_Excel5_MD5 { - // Context - private $a; - private $b; - private $c; - private $d; + // Context + private $a; + private $b; + private $c; + private $d; - /** - * MD5 stream constructor - */ - public function __construct() - { - $this->reset(); - } - /** - * Reset the MD5 stream context - */ - public function reset() - { - $this->a = 0x67452301; - $this->b = 0xEFCDAB89; - $this->c = 0x98BADCFE; - $this->d = 0x10325476; - } + /** + * MD5 stream constructor + */ + public function __construct() + { + $this->reset(); + } - /** - * Get MD5 stream context - * - * @return string - */ - public function getContext() - { - $s = ''; - foreach (array('a', 'b', 'c', 'd') as $i) { - $v = $this->{$i}; - $s .= chr($v & 0xff); - $s .= chr(($v >> 8) & 0xff); - $s .= chr(($v >> 16) & 0xff); - $s .= chr(($v >> 24) & 0xff); - } - return $s; - } + /** + * Reset the MD5 stream context + */ + public function reset() + { + $this->a = 0x67452301; + $this->b = 0xEFCDAB89; + $this->c = 0x98BADCFE; + $this->d = 0x10325476; + } - /** - * Add data to context - * - * @param string $data Data to add - */ - public function add($data) - { - $words = array_values(unpack('V16', $data)); - - $A = $this->a; - $B = $this->b; - $C = $this->c; - $D = $this->d; - /* ROUND 1 */ - self::FF($A, $B, $C, $D, $words[0], 7, 0xd76aa478); - self::FF($D, $A, $B, $C, $words[1], 12, 0xe8c7b756); - self::FF($C, $D, $A, $B, $words[2], 17, 0x242070db); - self::FF($B, $C, $D, $A, $words[3], 22, 0xc1bdceee); - self::FF($A, $B, $C, $D, $words[4], 7, 0xf57c0faf); - self::FF($D, $A, $B, $C, $words[5], 12, 0x4787c62a); - self::FF($C, $D, $A, $B, $words[6], 17, 0xa8304613); - self::FF($B, $C, $D, $A, $words[7], 22, 0xfd469501); - self::FF($A, $B, $C, $D, $words[8], 7, 0x698098d8); - self::FF($D, $A, $B, $C, $words[9], 12, 0x8b44f7af); - self::FF($C, $D, $A, $B, $words[10], 17, 0xffff5bb1); - self::FF($B, $C, $D, $A, $words[11], 22, 0x895cd7be); - self::FF($A, $B, $C, $D, $words[12], 7, 0x6b901122); - self::FF($D, $A, $B, $C, $words[13], 12, 0xfd987193); - self::FF($C, $D, $A, $B, $words[14], 17, 0xa679438e); - self::FF($B, $C, $D, $A, $words[15], 22, 0x49b40821); + /** + * Get MD5 stream context + * + * @return string + */ + public function getContext() + { + $s = ''; + foreach (array('a', 'b', 'c', 'd') as $i) { + $v = $this->{$i}; + $s .= chr($v & 0xff); + $s .= chr(($v >> 8) & 0xff); + $s .= chr(($v >> 16) & 0xff); + $s .= chr(($v >> 24) & 0xff); + } - /* ROUND 2 */ - self::GG($A, $B, $C, $D, $words[1], 5, 0xf61e2562); - self::GG($D, $A, $B, $C, $words[6], 9, 0xc040b340); - self::GG($C, $D, $A, $B, $words[11], 14, 0x265e5a51); - self::GG($B, $C, $D, $A, $words[0], 20, 0xe9b6c7aa); - self::GG($A, $B, $C, $D, $words[5], 5, 0xd62f105d); - self::GG($D, $A, $B, $C, $words[10], 9, 0x02441453); - self::GG($C, $D, $A, $B, $words[15], 14, 0xd8a1e681); - self::GG($B, $C, $D, $A, $words[4], 20, 0xe7d3fbc8); - self::GG($A, $B, $C, $D, $words[9], 5, 0x21e1cde6); - self::GG($D, $A, $B, $C, $words[14], 9, 0xc33707d6); - self::GG($C, $D, $A, $B, $words[3], 14, 0xf4d50d87); - self::GG($B, $C, $D, $A, $words[8], 20, 0x455a14ed); - self::GG($A, $B, $C, $D, $words[13], 5, 0xa9e3e905); - self::GG($D, $A, $B, $C, $words[2], 9, 0xfcefa3f8); - self::GG($C, $D, $A, $B, $words[7], 14, 0x676f02d9); - self::GG($B, $C, $D, $A, $words[12], 20, 0x8d2a4c8a); + return $s; + } - /* ROUND 3 */ - self::HH($A, $B, $C, $D, $words[5], 4, 0xfffa3942); - self::HH($D, $A, $B, $C, $words[8], 11, 0x8771f681); - self::HH($C, $D, $A, $B, $words[11], 16, 0x6d9d6122); - self::HH($B, $C, $D, $A, $words[14], 23, 0xfde5380c); - self::HH($A, $B, $C, $D, $words[1], 4, 0xa4beea44); - self::HH($D, $A, $B, $C, $words[4], 11, 0x4bdecfa9); - self::HH($C, $D, $A, $B, $words[7], 16, 0xf6bb4b60); - self::HH($B, $C, $D, $A, $words[10], 23, 0xbebfbc70); - self::HH($A, $B, $C, $D, $words[13], 4, 0x289b7ec6); - self::HH($D, $A, $B, $C, $words[0], 11, 0xeaa127fa); - self::HH($C, $D, $A, $B, $words[3], 16, 0xd4ef3085); - self::HH($B, $C, $D, $A, $words[6], 23, 0x04881d05); - self::HH($A, $B, $C, $D, $words[9], 4, 0xd9d4d039); - self::HH($D, $A, $B, $C, $words[12], 11, 0xe6db99e5); - self::HH($C, $D, $A, $B, $words[15], 16, 0x1fa27cf8); - self::HH($B, $C, $D, $A, $words[2], 23, 0xc4ac5665); - /* ROUND 4 */ - self::II($A, $B, $C, $D, $words[0], 6, 0xf4292244); - self::II($D, $A, $B, $C, $words[7], 10, 0x432aff97); - self::II($C, $D, $A, $B, $words[14], 15, 0xab9423a7); - self::II($B, $C, $D, $A, $words[5], 21, 0xfc93a039); - self::II($A, $B, $C, $D, $words[12], 6, 0x655b59c3); - self::II($D, $A, $B, $C, $words[3], 10, 0x8f0ccc92); - self::II($C, $D, $A, $B, $words[10], 15, 0xffeff47d); - self::II($B, $C, $D, $A, $words[1], 21, 0x85845dd1); - self::II($A, $B, $C, $D, $words[8], 6, 0x6fa87e4f); - self::II($D, $A, $B, $C, $words[15], 10, 0xfe2ce6e0); - self::II($C, $D, $A, $B, $words[6], 15, 0xa3014314); - self::II($B, $C, $D, $A, $words[13], 21, 0x4e0811a1); - self::II($A, $B, $C, $D, $words[4], 6, 0xf7537e82); - self::II($D, $A, $B, $C, $words[11], 10, 0xbd3af235); - self::II($C, $D, $A, $B, $words[2], 15, 0x2ad7d2bb); - self::II($B, $C, $D, $A, $words[9], 21, 0xeb86d391); + /** + * Add data to context + * + * @param string $data Data to add + */ + public function add($data) + { + $words = array_values(unpack('V16', $data)); - $this->a = ($this->a + $A) & 0xffffffff; - $this->b = ($this->b + $B) & 0xffffffff; - $this->c = ($this->c + $C) & 0xffffffff; - $this->d = ($this->d + $D) & 0xffffffff; - } + $A = $this->a; + $B = $this->b; + $C = $this->c; + $D = $this->d; - private static function F($X, $Y, $Z) - { - $calc = (($X & $Y) | ((~ $X) & $Z)); // X AND Y OR NOT X AND Z - return $calc; - } + $F = array('PHPExcel_Reader_Excel5_MD5','F'); + $G = array('PHPExcel_Reader_Excel5_MD5','G'); + $H = array('PHPExcel_Reader_Excel5_MD5','H'); + $I = array('PHPExcel_Reader_Excel5_MD5','I'); - private static function G($X, $Y, $Z) - { - $calc = (($X & $Z) | ($Y & (~ $Z))); // X AND Z OR Y AND NOT Z - return $calc; - } + /* ROUND 1 */ + self::step($F, $A, $B, $C, $D, $words[0], 7, 0xd76aa478); + self::step($F, $D, $A, $B, $C, $words[1], 12, 0xe8c7b756); + self::step($F, $C, $D, $A, $B, $words[2], 17, 0x242070db); + self::step($F, $B, $C, $D, $A, $words[3], 22, 0xc1bdceee); + self::step($F, $A, $B, $C, $D, $words[4], 7, 0xf57c0faf); + self::step($F, $D, $A, $B, $C, $words[5], 12, 0x4787c62a); + self::step($F, $C, $D, $A, $B, $words[6], 17, 0xa8304613); + self::step($F, $B, $C, $D, $A, $words[7], 22, 0xfd469501); + self::step($F, $A, $B, $C, $D, $words[8], 7, 0x698098d8); + self::step($F, $D, $A, $B, $C, $words[9], 12, 0x8b44f7af); + self::step($F, $C, $D, $A, $B, $words[10], 17, 0xffff5bb1); + self::step($F, $B, $C, $D, $A, $words[11], 22, 0x895cd7be); + self::step($F, $A, $B, $C, $D, $words[12], 7, 0x6b901122); + self::step($F, $D, $A, $B, $C, $words[13], 12, 0xfd987193); + self::step($F, $C, $D, $A, $B, $words[14], 17, 0xa679438e); + self::step($F, $B, $C, $D, $A, $words[15], 22, 0x49b40821); - private static function H($X, $Y, $Z) - { - $calc = ($X ^ $Y ^ $Z); // X XOR Y XOR Z - return $calc; - } + /* ROUND 2 */ + self::step($G, $A, $B, $C, $D, $words[1], 5, 0xf61e2562); + self::step($G, $D, $A, $B, $C, $words[6], 9, 0xc040b340); + self::step($G, $C, $D, $A, $B, $words[11], 14, 0x265e5a51); + self::step($G, $B, $C, $D, $A, $words[0], 20, 0xe9b6c7aa); + self::step($G, $A, $B, $C, $D, $words[5], 5, 0xd62f105d); + self::step($G, $D, $A, $B, $C, $words[10], 9, 0x02441453); + self::step($G, $C, $D, $A, $B, $words[15], 14, 0xd8a1e681); + self::step($G, $B, $C, $D, $A, $words[4], 20, 0xe7d3fbc8); + self::step($G, $A, $B, $C, $D, $words[9], 5, 0x21e1cde6); + self::step($G, $D, $A, $B, $C, $words[14], 9, 0xc33707d6); + self::step($G, $C, $D, $A, $B, $words[3], 14, 0xf4d50d87); + self::step($G, $B, $C, $D, $A, $words[8], 20, 0x455a14ed); + self::step($G, $A, $B, $C, $D, $words[13], 5, 0xa9e3e905); + self::step($G, $D, $A, $B, $C, $words[2], 9, 0xfcefa3f8); + self::step($G, $C, $D, $A, $B, $words[7], 14, 0x676f02d9); + self::step($G, $B, $C, $D, $A, $words[12], 20, 0x8d2a4c8a); - private static function I($X, $Y, $Z) - { - $calc = ($Y ^ ($X | (~ $Z))) ; // Y XOR (X OR NOT Z) - return $calc; - } + /* ROUND 3 */ + self::step($H, $A, $B, $C, $D, $words[5], 4, 0xfffa3942); + self::step($H, $D, $A, $B, $C, $words[8], 11, 0x8771f681); + self::step($H, $C, $D, $A, $B, $words[11], 16, 0x6d9d6122); + self::step($H, $B, $C, $D, $A, $words[14], 23, 0xfde5380c); + self::step($H, $A, $B, $C, $D, $words[1], 4, 0xa4beea44); + self::step($H, $D, $A, $B, $C, $words[4], 11, 0x4bdecfa9); + self::step($H, $C, $D, $A, $B, $words[7], 16, 0xf6bb4b60); + self::step($H, $B, $C, $D, $A, $words[10], 23, 0xbebfbc70); + self::step($H, $A, $B, $C, $D, $words[13], 4, 0x289b7ec6); + self::step($H, $D, $A, $B, $C, $words[0], 11, 0xeaa127fa); + self::step($H, $C, $D, $A, $B, $words[3], 16, 0xd4ef3085); + self::step($H, $B, $C, $D, $A, $words[6], 23, 0x04881d05); + self::step($H, $A, $B, $C, $D, $words[9], 4, 0xd9d4d039); + self::step($H, $D, $A, $B, $C, $words[12], 11, 0xe6db99e5); + self::step($H, $C, $D, $A, $B, $words[15], 16, 0x1fa27cf8); + self::step($H, $B, $C, $D, $A, $words[2], 23, 0xc4ac5665); - private static function FF(&$A, $B, $C, $D, $M, $s, $t) - { - $A = ($A + self::F($B, $C, $D) + $M + $t) & 0xffffffff; - $A = self::rotate($A, $s); - $A = ($B + $A) & 0xffffffff; - } + /* ROUND 4 */ + self::step($I, $A, $B, $C, $D, $words[0], 6, 0xf4292244); + self::step($I, $D, $A, $B, $C, $words[7], 10, 0x432aff97); + self::step($I, $C, $D, $A, $B, $words[14], 15, 0xab9423a7); + self::step($I, $B, $C, $D, $A, $words[5], 21, 0xfc93a039); + self::step($I, $A, $B, $C, $D, $words[12], 6, 0x655b59c3); + self::step($I, $D, $A, $B, $C, $words[3], 10, 0x8f0ccc92); + self::step($I, $C, $D, $A, $B, $words[10], 15, 0xffeff47d); + self::step($I, $B, $C, $D, $A, $words[1], 21, 0x85845dd1); + self::step($I, $A, $B, $C, $D, $words[8], 6, 0x6fa87e4f); + self::step($I, $D, $A, $B, $C, $words[15], 10, 0xfe2ce6e0); + self::step($I, $C, $D, $A, $B, $words[6], 15, 0xa3014314); + self::step($I, $B, $C, $D, $A, $words[13], 21, 0x4e0811a1); + self::step($I, $A, $B, $C, $D, $words[4], 6, 0xf7537e82); + self::step($I, $D, $A, $B, $C, $words[11], 10, 0xbd3af235); + self::step($I, $C, $D, $A, $B, $words[2], 15, 0x2ad7d2bb); + self::step($I, $B, $C, $D, $A, $words[9], 21, 0xeb86d391); - private static function GG(&$A, $B, $C, $D, $M, $s, $t) - { - $A = ($A + self::G($B, $C, $D) + $M + $t) & 0xffffffff; - $A = self::rotate($A, $s); - $A = ($B + $A) & 0xffffffff; - } + $this->a = ($this->a + $A); + $this->b = ($this->b + $B); + $this->c = ($this->c + $C); + $this->d = ($this->d + $D); + } - private static function HH(&$A, $B, $C, $D, $M, $s, $t) - { - $A = ($A + self::H($B, $C, $D) + $M + $t) & 0xffffffff; - $A = self::rotate($A, $s); - $A = ($B + $A) & 0xffffffff; - } - private static function II(&$A, $B, $C, $D, $M, $s, $t) - { - $A = ($A + self::I($B, $C, $D) + $M + $t) & 0xffffffff; - $A = self::rotate($A, $s); - $A = ($B + $A) & 0xffffffff; - } + private static function F($X, $Y, $Z) + { + return (($X & $Y) | ((~ $X) & $Z)); // X AND Y OR NOT X AND Z + } - private static function rotate ($decimal, $bits) - { - return (($decimal << $bits) | ($decimal >> (32 - $bits))) & 0xffffffff; - } + + private static function G($X, $Y, $Z) + { + return (($X & $Z) | ($Y & (~ $Z))); // X AND Z OR Y AND NOT Z + } + + + private static function H($X, $Y, $Z) + { + return ($X ^ $Y ^ $Z); // X XOR Y XOR Z + } + + + private static function I($X, $Y, $Z) + { + return ($Y ^ ($X | (~ $Z))) ; // Y XOR (X OR NOT Z) + } + + + private static function step($func, &$A, $B, $C, $D, $M, $s, $t) + { + $A = ($A + call_user_func($func, $B, $C, $D) + $M + $t); + $A = self::rotate($A, $s); + $A = ($B + $A); + } + + + private static function rotate($decimal, $bits) + { + $binary = str_pad(decbin($decimal), 32, "0", STR_PAD_LEFT); + return bindec(substr($binary, $bits).substr($binary, 0, $bits)); + } } diff --git a/changelog.txt b/changelog.txt index f9c6ab87..e1497e59 100644 --- a/changelog.txt +++ b/changelog.txt @@ -42,7 +42,7 @@ Fixed in develop branch for release v1.8.0: - Feature: (amerov) - Implementation of the Excel HLOOKUP() function - Feature: (MBaker) - Added "Quote Prefix" to style settings (Excel2007 Reader and Writer only) - Feature: (MBaker) - Added Horizontal FILL alignment for Excel5 and Excel2007 Readers/Writers, and Horizontal DISTRIBUTED alignment for Excel2007 Reader/Writer -- Feature: (trvrnrth) - Add support for reading protected (RC4 encrypted) .xls files (64-bit Linux only) +- Feature: (trvrnrth) Work Item GH-261 - Add support for reading protected (RC4 encrypted) .xls files - Feature: (LWol) Work Item GH-252 - Adding support for macros, Ribbon in Excel 2007 - General: (cdhutch) Work item 20055 - Remove array_shift in ReferenceHelper::insertNewBefore improves column or row delete speed - General: (MBaker) - Improve stock chart handling and rendering, with help from Swashata Ghosh