diff --git a/src/PhpZip/Model/Entry/ZipAbstractEntry.php b/src/PhpZip/Model/Entry/ZipAbstractEntry.php index 6fb64d8..72d1722 100644 --- a/src/PhpZip/Model/Entry/ZipAbstractEntry.php +++ b/src/PhpZip/Model/Entry/ZipAbstractEntry.php @@ -255,11 +255,8 @@ abstract class ZipAbstractEntry implements ZipEntry */ public function isZip64ExtensionsRequired() { - // Offset MUST be considered in decision about ZIP64 format - see - // description of Data Descriptor in ZIP File Format Specification! return 0xffffffff <= $this->getCompressedSize() - || 0xffffffff <= $this->getSize() - || 0xffffffff <= sprintf('%u', $this->getOffset()); + || 0xffffffff <= $this->getSize(); } /** @@ -432,7 +429,10 @@ abstract class ZipAbstractEntry implements ZipEntry */ public function getMethod() { - return $this->isInit(self::BIT_METHOD) ? $this->method & 0xffff : self::UNKNOWN; + $isInit = $this->isInit(self::BIT_METHOD); + return $isInit ? + $this->method & 0xffff : + self::UNKNOWN; } /** @@ -446,17 +446,14 @@ abstract class ZipAbstractEntry implements ZipEntry { if (self::UNKNOWN === $method) { $this->method = $method; + $this->setInit(self::BIT_METHOD, false); return $this; } if (0x0000 > $method || $method > 0xffff) { - throw new ZipException('method out of range'); + throw new ZipException('method out of range: ' . $method); } switch ($method) { case self::METHOD_WINZIP_AES: - $this->method = $method; - $this->setInit(self::BIT_METHOD, true); - break; - case ZipFileInterface::METHOD_STORED: case ZipFileInterface::METHOD_DEFLATED: case ZipFileInterface::METHOD_BZIP2: diff --git a/src/PhpZip/Model/ZipEntry.php b/src/PhpZip/Model/ZipEntry.php index 19dbd13..015b4ac 100644 --- a/src/PhpZip/Model/ZipEntry.php +++ b/src/PhpZip/Model/ZipEntry.php @@ -18,7 +18,7 @@ interface ZipEntry // Bit masks for initialized fields. const BIT_PLATFORM = 1, BIT_METHOD = 2 /* 1 << 1 */, - BIT_CRC = 2 /* 1 << 2 */, + BIT_CRC = 4 /* 1 << 2 */, BIT_DATE_TIME = 64 /* 1 << 6 */, BIT_EXTERNAL_ATTR = 128 /* 1 << 7*/ ; diff --git a/src/PhpZip/Stream/ZipOutputStream.php b/src/PhpZip/Stream/ZipOutputStream.php index d692e97..13cf2c5 100644 --- a/src/PhpZip/Stream/ZipOutputStream.php +++ b/src/PhpZip/Stream/ZipOutputStream.php @@ -223,7 +223,6 @@ class ZipOutputStream implements ZipOutputStreamInterface | ($entry->isDataDescriptorRequired() ? ZipEntry::GPBF_DATA_DESCRIPTOR : 0) | ($utf8 ? ZipEntry::GPBF_UTF8 : 0); - $skipCrc = false; $entryContent = null; $extraFieldsCollection = $entry->getExtraFieldsCollection(); if (!($entry instanceof ZipChangesEntry && !$entry->isChangedContent())) { @@ -233,57 +232,14 @@ class ZipOutputStream implements ZipOutputStreamInterface $entry->setSize(strlen($entryContent)); $entry->setCrc(crc32($entryContent)); - if ( - $encrypted && - ( - ZipEntry::METHOD_WINZIP_AES === $method || - $entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128 || - $entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192 || - $entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256 - ) - ) { - $field = null; - $method = $entry->getMethod(); - $keyStrength = WinZipAesEntryExtraField::getKeyStrangeFromEncryptionMethod($entry->getEncryptionMethod()); // size bits - - $compressedSize = $entry->getCompressedSize(); - - if (ZipEntry::METHOD_WINZIP_AES === $method) { - /** - * @var WinZipAesEntryExtraField $field - */ - $field = $extraFieldsCollection->get(WinZipAesEntryExtraField::getHeaderId()); - if (null !== $field) { - $method = $field->getMethod(); - if (ZipEntry::UNKNOWN !== $compressedSize) { - $compressedSize -= $field->getKeyStrength() / 2 // salt value - + 2 // password verification value - + 10; // authentication code - } - $entry->setMethod($method); - } - } - if (null === $field) { - $field = ExtraFieldsFactory::createWinZipAesEntryExtra(); - } - $field->setKeyStrength($keyStrength); - $field->setMethod($method); - $size = $entry->getSize(); - if (20 <= $size && ZipFileInterface::METHOD_BZIP2 !== $method) { - $field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_1); - } else { - $field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_2); - $skipCrc = true; - } - $extraFieldsCollection->add($field); - if (ZipEntry::UNKNOWN !== $compressedSize) { - $compressedSize += $field->getKeyStrength() / 2 // salt value - + 2 // password verification value - + 10; // authentication code - $entry->setCompressedSize($compressedSize); - } - if ($skipCrc) { - $entry->setCrc(0); + if ($encrypted && ZipEntry::METHOD_WINZIP_AES === $method) { + /** + * @var WinZipAesEntryExtraField $field + */ + $field = $extraFieldsCollection->get(WinZipAesEntryExtraField::getHeaderId()); + if (null !== $field) { + $method = $field->getMethod(); + $entry->setMethod($method); } } @@ -337,14 +293,23 @@ class ZipOutputStream implements ZipOutputStreamInterface } if ($encrypted) { - if ( - $entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128 || - $entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192 || - $entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256 - ) { - if ($skipCrc) { + if (in_array($entry->getEncryptionMethod(), [ + ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128, + ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192, + ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256, + ], true)) { + $keyStrength = WinZipAesEntryExtraField::getKeyStrangeFromEncryptionMethod($entry->getEncryptionMethod()); // size bits + $field = ExtraFieldsFactory::createWinZipAesEntryExtra(); + $field->setKeyStrength($keyStrength); + $field->setMethod($method); + $size = $entry->getSize(); + if (20 <= $size && ZipFileInterface::METHOD_BZIP2 !== $method) { + $field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_1); + } else { + $field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_2); $entry->setCrc(0); } + $extraFieldsCollection->add($field); $entry->setMethod(ZipEntry::METHOD_WINZIP_AES); $winZipAesEngine = new WinZipAesEngine($entry); @@ -375,6 +340,7 @@ class ZipOutputStream implements ZipOutputStreamInterface * @param ZipEntry $entry * @param string $content * @return string + * @throws ZipException */ protected function determineBestCompressionMethod(ZipEntry $entry, $content) { diff --git a/tests/PhpZip/ZipPasswordTest.php b/tests/PhpZip/ZipPasswordTest.php index ac96f10..46a969c 100644 --- a/tests/PhpZip/ZipPasswordTest.php +++ b/tests/PhpZip/ZipPasswordTest.php @@ -346,4 +346,28 @@ class ZipPasswordTest extends ZipFileAddDirTest $zipFile->close(); } + + /** + * @see https://github.com/Ne-Lexa/php-zip/issues/9 + */ + public function testIssues9() + { + $contents = str_pad('', 1000, 'test;test2;test3' . PHP_EOL, STR_PAD_RIGHT); + $password = base64_encode(CryptoUtil::randomBytes(20)); + + $encryptMethod = ZipFile::ENCRYPTION_METHOD_WINZIP_AES_256; + $zipFile = new ZipFile(); + $zipFile + ->addFromString('codes.csv', $contents) + ->setPassword($password, $encryptMethod) + ->saveAsFile($this->outputFilename) + ->close(); + + $this->assertCorrectZipArchive($this->outputFilename, $password); + + $zipFile->openFile($this->outputFilename); + $zipFile->setReadPassword($password); + $this->assertEquals($zipFile['codes.csv'], $contents); + $zipFile->close(); + } }