From 233021529cf0ff1ae2961b05159fb9f28c9ce019 Mon Sep 17 00:00:00 2001 From: Trevor North Date: Tue, 29 Oct 2013 11:25:36 +0000 Subject: [PATCH] Implement Excel 5 RC4 stream decryption The decryption functions and objects implemented here are are based on the source of Spreadsheet-ParseExcel: http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel/ --- Classes/PHPExcel/Reader/Excel5.php | 378 ++++++++++++++++++++----- Classes/PHPExcel/Reader/Excel5/MD5.php | 229 +++++++++++++++ Classes/PHPExcel/Reader/Excel5/RC4.php | 88 ++++++ 3 files changed, 622 insertions(+), 73 deletions(-) create mode 100644 Classes/PHPExcel/Reader/Excel5/MD5.php create mode 100644 Classes/PHPExcel/Reader/Excel5/RC4.php diff --git a/Classes/PHPExcel/Reader/Excel5.php b/Classes/PHPExcel/Reader/Excel5.php index 8dc8d6c..601c492 100644 --- a/Classes/PHPExcel/Reader/Excel5.php +++ b/Classes/PHPExcel/Reader/Excel5.php @@ -159,6 +159,13 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce const XLS_Type_PAGELAYOUTVIEW = 0x088b; const XLS_Type_UNKNOWN = 0xffff; + // Encryption type + const MS_BIFF_CRYPTO_NONE = 0; + const MS_BIFF_CRYPTO_XOR = 1; + const MS_BIFF_CRYPTO_RC4 = 2; + + // Size of stream blocks when using RC4 encryption + const REKEY_BLOCK = 0x400; /** * Summary Information stream data. @@ -379,6 +386,40 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce */ private $_sharedFormulaParts; + /** + * The type of encryption in use + * + * @var int + */ + private $_encryption = 0; + + /** + * The position in the stream after which contents are encrypted + * + * @var int + */ + private $_encryptionStartPos = false; + + /** + * The current RC4 decryption object + * + * @var PHPExcel_Reader_Excel5_RC4 + */ + private $_rc4Key = null; + + /** + * The position in the stream that the RC4 decryption object was left at + * + * @var int + */ + private $_rc4Pos = 0; + + /** + * The current MD5 context state + * + * @var string + */ + private $_md5Ctxt = null; /** * Create a new PHPExcel_Reader_Excel5 instance @@ -531,7 +572,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce case self::XLS_Type_BOOLERR: case self::XLS_Type_LABEL: $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -1051,7 +1092,63 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce return $this->_phpExcel; } + + /** + * Read record data from stream, decrypting as required + * + * @param string $data Data stream to read from + * @param int $pos Position to start reading from + * @param int $length Record data length + * + * @return string Record data + */ + private function _readRecordData($data, $pos, $len) + { + $data = substr($data, $pos, $len); + + // File not encrypted, or record before encryption start point + if ($this->_encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->_encryptionStartPos) { + return $data; + } + + $recordData = ''; + if ($this->_encryption == self::MS_BIFF_CRYPTO_RC4) { + $oldBlock = floor($this->_rc4Pos / self::REKEY_BLOCK); + $block = floor($pos / self::REKEY_BLOCK); + $endBlock = floor(($pos + $len) / self::REKEY_BLOCK); + + // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting + // at a point earlier in the current block, re-use it as we can save some time. + if ($block != $oldBlock || $pos < $this->_rc4Pos || !$this->_rc4Key) { + $this->_rc4Key = $this->_makeKey($block, $this->_md5Ctxt); + $step = $pos % self::REKEY_BLOCK; + } else { + $step = $pos - $this->_rc4Pos; + } + $this->_rc4Key->RC4(str_repeat("\0", $step)); + + // Decrypt record data (re-keying at the end of every block) + while ($block != $endBlock) { + $step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK); + $recordData .= $this->_rc4Key->RC4(substr($data, 0, $step)); + $data = substr($data, $step); + $pos += $step; + $len -= $step; + $block++; + $this->_rc4Key = $this->_makeKey($block, $this->_md5Ctxt); + } + $recordData .= $this->_rc4Key->RC4(substr($data, 0, $len)); + + // Keep track of the position of this decryptor. + // We'll try and re-use it later if we can to speed things up + $this->_rc4Pos = $pos + $len; + + } elseif ($this->_encryption == self::MS_BIFF_CRYPTO_XOR) { + throw new PHPExcel_Reader_Exception('XOr encryption not supported'); + } + return $recordData; + } /** * Use OLE reader to extract the relevant data streams from the OLE file @@ -1404,7 +1501,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readDefault() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); -// $recordData = substr($this->_data, $this->_pos + 4, $length); +// $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -1419,7 +1516,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce { // echo 'Read Cell Annotation
'; $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -1481,7 +1578,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readTextObject() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -1565,22 +1662,156 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce * * -- "OpenOffice.org's Documentation of the Microsoft * Excel File Format" + * + * The decryption functions and objects used from here on in + * are based on the source of Spreadsheet-ParseExcel: + * http://search.cpan.org/~jmcnamara/Spreadsheet-ParseExcel/ */ private function _readFilepass() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + if ($length != 54) { + throw new PHPExcel_Reader_Exception('Unexpected file pass record length'); + } + + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); + // move stream pointer to next record $this->_pos += 4 + $length; + + if (!$this->_verifyPassword( + 'VelvetSweatshop', + substr($recordData, 6, 16), + substr($recordData, 22, 16), + substr($recordData, 38, 16), + $this->_md5Ctxt + )) { + throw new \Exception('Decryption password incorrect'); + } + + $this->_encryption = self::MS_BIFF_CRYPTO_RC4; - if (!$this->_readDataOnly) { - // offset: 0; size: 2; 16-bit hash value of password - $password = strtoupper(dechex(self::_GetInt2d($recordData, 0))); // the hashed password - } - throw new PHPExcel_Reader_Exception('Cannot read encrypted file'); + // Decryption required from the record after next onwards + $this->_encryptionStartPos = $this->_pos + self::_GetInt2d($this->_data, $this->_pos + 2); } + /** + * Make an RC4 decryptor for the given block + * + * @var int $block Block for which to create decrypto + * @var string $valContext MD5 context state + * + * @return PHPExcel_Reader_Excel5_RC4 + */ + private function _makeKey($block, $valContext) + { + $pwarray = str_repeat("\0", 64); + + for ($i = 0; $i < 5; $i++) { + $pwarray[$i] = $valContext[$i]; + } + + $pwarray[5] = chr($block & 0xff); + $pwarray[6] = chr(($block >> 8) & 0xff); + $pwarray[7] = chr(($block >> 16) & 0xff); + $pwarray[8] = chr(($block >> 24) & 0xff); + + $pwarray[9] = "\x80"; + $pwarray[56] = "\x48"; + + $md5 = new PHPExcel_Reader_Excel5_MD5(); + $md5->add($pwarray); + + $s = $md5->getContext(); + return new PHPExcel_Reader_Excel5_RC4($s); + } + + /** + * Verify RC4 file password + * + * @var string $password Password to check + * @var string $docid Document id + * @var string $salt_data Salt data + * @var string $hashedsalt_data Hashed salt data + * @var string &$valContext Set to the MD5 context of the value + * + * @return bool Success + */ + private function _verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext) + { + $pwarray = str_repeat("\0", 64); + + for ($i = 0; $i < strlen($password); $i++) { + $o = ord(substr($password, $i, 1)); + $pwarray[2 * $i] = chr($o & 0xff); + $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff); + } + $pwarray[2 * $i] = chr(0x80); + $pwarray[56] = chr(($i << 4) & 0xff); + + $md5 = new PHPExcel_Reader_Excel5_MD5(); + $md5->add($pwarray); + + $mdContext1 = $md5->getContext(); + + $offset = 0; + $keyoffset = 0; + $tocopy = 5; + + $md5->reset(); + + while ($offset != 16) { + if ((64 - $offset) < 5) { + $tocopy = 64 - $offset; + } + + for ($i = 0; $i <= $tocopy; $i++) { + $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i]; + } + + $offset += $tocopy; + + if ($offset == 64) { + $md5->add($pwarray); + $keyoffset = $tocopy; + $tocopy = 5 - $tocopy; + $offset = 0; + continue; + } + + $keyoffset = 0; + $tocopy = 5; + for ($i = 0; $i < 16; $i++) { + $pwarray[$offset + $i] = $docid[$i]; + } + $offset += 16; + } + + $pwarray[16] = "\x80"; + for ($i = 0; $i < 47; $i++) { + $pwarray[17 + $i] = "\0"; + } + $pwarray[56] = "\x80"; + $pwarray[57] = "\x0a"; + + $md5->add($pwarray); + $valContext = $md5->getContext(); + + $key = $this->_makeKey(0, $valContext); + + $salt = $key->RC4($salt_data); + $hashedsalt = $key->RC4($hashedsalt_data); + + $salt .= "\x80" . str_repeat("\0", 47); + $salt[56] = "\x80"; + + $md5->reset(); + $md5->add($salt); + $mdContext2 = $md5->getContext(); + + return $mdContext2 == $hashedsalt; + } /** * CODEPAGE @@ -1594,7 +1825,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readCodepage() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -1621,7 +1852,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readDateMode() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -1640,7 +1871,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readFont() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -1738,7 +1969,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readFormat() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -1776,7 +2007,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readXf() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2062,7 +2293,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readXfExt() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2239,7 +2470,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readStyle() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2280,7 +2511,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readPalette() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2313,14 +2544,15 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readSheet() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); + + // offset: 0; size: 4; absolute stream position of the BOF record of the sheet + // NOTE: not encrypted + $rec_offset = self::_GetInt4d($this->_data, $this->_pos + 4); // move stream pointer to next record $this->_pos += 4 + $length; - // offset: 0; size: 4; absolute stream position of the BOF record of the sheet - $rec_offset = self::_GetInt4d($recordData, 0); - // offset: 4; size: 1; sheet state switch (ord($recordData{4})) { case 0x00: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break; @@ -2356,7 +2588,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readExternalBook() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2420,7 +2652,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readExternName() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2455,7 +2687,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readExternSheet() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2492,7 +2724,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readDefinedName() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2754,7 +2986,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readPrintGridlines() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2773,7 +3005,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readDefaultRowHeight() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2791,7 +3023,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readSheetPr() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2818,7 +3050,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readHorizontalPageBreaks() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2847,7 +3079,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readVerticalPageBreaks() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2875,7 +3107,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readHeader() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2903,7 +3135,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readFooter() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2930,7 +3162,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readHcenter() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2950,7 +3182,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readVcenter() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2970,7 +3202,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readLeftMargin() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -2988,7 +3220,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readRightMargin() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3006,7 +3238,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readTopMargin() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3024,7 +3256,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readBottomMargin() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3042,7 +3274,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readPageSetup() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3100,7 +3332,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readProtect() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3123,7 +3355,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readScenProtect() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3147,7 +3379,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readObjectProtect() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3171,7 +3403,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readPassword() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3190,7 +3422,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readDefColWidth() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3209,7 +3441,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readColInfo() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3268,7 +3500,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readRow() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3338,7 +3570,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readRk() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3383,7 +3615,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readLabelSst() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3461,7 +3693,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readMulRk() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3516,7 +3748,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readNumber() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3558,7 +3790,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readFormula() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3699,7 +3931,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readSharedFmla() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3732,7 +3964,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readString() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3760,7 +3992,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readBoolErr() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3819,7 +4051,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readMulBlank() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3861,7 +4093,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readLabel() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3904,7 +4136,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readBlank() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3951,7 +4183,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readObj() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -3996,7 +4228,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readWindow2() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -4063,7 +4295,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce */ private function _readPageLayoutView(){ $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -4102,7 +4334,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readScl() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -4124,7 +4356,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readPane() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -4152,7 +4384,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readSelection() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -4229,7 +4461,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readMergedCells() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -4252,7 +4484,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readHyperLink() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer forward to next record $this->_pos += 4 + $length; @@ -4428,7 +4660,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readDataValidations() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer forward to next record $this->_pos += 4 + $length; @@ -4441,7 +4673,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readDataValidation() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer forward to next record $this->_pos += 4 + $length; @@ -4601,7 +4833,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readSheetLayout() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -4641,7 +4873,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readSheetProtection() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -4742,7 +4974,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readRangeProtection() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // move stream pointer to next record $this->_pos += 4 + $length; @@ -4886,7 +5118,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce private function _readContinue() { $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); + $recordData = $this->_readRecordData($this->_data, $this->_pos + 4, $length); // check if we are reading drawing data // this is in case a free CONTINUE record occurs in other circumstances we are unaware of @@ -4951,7 +5183,7 @@ class PHPExcel_Reader_Excel5 extends PHPExcel_Reader_Abstract implements PHPExce $identifier = self::_GetInt2d($this->_data, $this->_pos); // offset: 2; size: 2; length $length = self::_GetInt2d($this->_data, $this->_pos + 2); - $data .= substr($this->_data, $this->_pos + 4, $length); + $data .= $this->_readRecordData($this->_data, $this->_pos + 4, $length); $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length; diff --git a/Classes/PHPExcel/Reader/Excel5/MD5.php b/Classes/PHPExcel/Reader/Excel5/MD5.php new file mode 100644 index 0000000..62e0515 --- /dev/null +++ b/Classes/PHPExcel/Reader/Excel5/MD5.php @@ -0,0 +1,229 @@ +reset(); + } + + /** + * Reset the MD5 stream context + */ + public function reset() + { + $this->a = 0x67452301; + $this->b = 0xEFCDAB89; + $this->c = 0x98BADCFE; + $this->d = 0x10325476; + } + + /** + * 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; + } + + /** + * 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); + + /* 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); + + /* 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); + + $this->a = ($this->a + $A) & 0xffffffff; + $this->b = ($this->b + $B) & 0xffffffff; + $this->c = ($this->c + $C) & 0xffffffff; + $this->d = ($this->d + $D) & 0xffffffff; + } + + private static function F($X, $Y, $Z) + { + $calc = (($X & $Y) | ((~ $X) & $Z)); // X AND Y OR NOT X AND Z + return $calc; + } + + private static function G($X, $Y, $Z) + { + $calc = (($X & $Z) | ($Y & (~ $Z))); // X AND Z OR Y AND NOT Z + return $calc; + } + + private static function H($X, $Y, $Z) + { + $calc = ($X ^ $Y ^ $Z); // X XOR Y XOR Z + return $calc; + } + + private static function I($X, $Y, $Z) + { + $calc = ($Y ^ ($X | (~ $Z))) ; // Y XOR (X OR NOT Z) + return $calc; + } + + 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; + } + + 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; + } + + 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 rotate ($decimal, $bits) + { + return (($decimal << $bits) | ($decimal >> (32 - $bits))) & 0xffffffff; + } +} diff --git a/Classes/PHPExcel/Reader/Excel5/RC4.php b/Classes/PHPExcel/Reader/Excel5/RC4.php new file mode 100644 index 0000000..492e1b4 --- /dev/null +++ b/Classes/PHPExcel/Reader/Excel5/RC4.php @@ -0,0 +1,88 @@ +i = 0; $this->i < 256; $this->i++) { + $this->s[$this->i] = $this->i; + } + + $this->j = 0; + for ($this->i = 0; $this->i < 256; $this->i++) { + $this->j = ($this->j + $this->s[$this->i] + ord($key[$this->i % $len])) % 256; + $t = $this->s[$this->i]; + $this->s[$this->i] = $this->s[$this->j]; + $this->s[$this->j] = $t; + } + $this->i = $this->j = 0; + } + + /** + * Symmetric decryption/encryption function + * + * @param string $data Data to encrypt/decrypt + * + * @return string + */ + public function RC4($data) + { + $len = strlen($data); + for ($c = 0; $c < $len; $c++) { + $this->i = ($this->i + 1) % 256; + $this->j = ($this->j + $this->s[$this->i]) % 256; + $t = $this->s[$this->i]; + $this->s[$this->i] = $this->s[$this->j]; + $this->s[$this->j] = $t; + + $t = ($this->s[$this->i] + $this->s[$this->j]) % 256; + + $data[$c] = chr(ord($data[$c]) ^ $this->s[$t]); + } + return $data; + } +}