ZipPasswordTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. <?php
  2. namespace PhpZip;
  3. use PhpZip\Exception\ZipAuthenticationException;
  4. use PhpZip\Exception\ZipEntryNotFoundException;
  5. use PhpZip\Exception\ZipException;
  6. use PhpZip\Model\ZipInfo;
  7. use PhpZip\Util\CryptoUtil;
  8. /**
  9. * Tests with zip password.
  10. */
  11. class ZipPasswordTest extends ZipFileAddDirTest
  12. {
  13. /**
  14. * Test archive password.
  15. * @throws ZipException
  16. */
  17. public function testSetPassword()
  18. {
  19. if (PHP_INT_SIZE === 4) {
  20. $this->markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
  21. }
  22. $password = base64_encode(CryptoUtil::randomBytes(100));
  23. $badPassword = "bad password";
  24. // create encryption password with ZipCrypto
  25. $zipFile = new ZipFile();
  26. $zipFile->addDir(__DIR__);
  27. $zipFile->setPassword($password, ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL);
  28. $zipFile->saveAsFile($this->outputFilename);
  29. $zipFile->close();
  30. $this->assertCorrectZipArchive($this->outputFilename, $password);
  31. // check bad password for ZipCrypto
  32. $zipFile->openFile($this->outputFilename);
  33. $zipFile->setReadPassword($badPassword);
  34. foreach ($zipFile->getListFiles() as $entryName) {
  35. try {
  36. $zipFile[$entryName];
  37. $this->fail("Expected Exception has not been raised.");
  38. } catch (ZipAuthenticationException $ae) {
  39. $this->assertContains('Invalid password for zip entry', $ae->getMessage());
  40. }
  41. }
  42. // check correct password for ZipCrypto
  43. $zipFile->setReadPassword($password);
  44. foreach ($zipFile->getAllInfo() as $info) {
  45. $this->assertTrue($info->isEncrypted());
  46. $this->assertContains('ZipCrypto', $info->getMethodName());
  47. $decryptContent = $zipFile[$info->getName()];
  48. $this->assertNotEmpty($decryptContent);
  49. $this->assertContains('<?php', $decryptContent);
  50. }
  51. // change encryption method to WinZip Aes and update file
  52. $zipFile->setPassword($password, ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES);
  53. $zipFile->saveAsFile($this->outputFilename);
  54. $zipFile->close();
  55. $this->assertCorrectZipArchive($this->outputFilename, $password);
  56. // check from WinZip AES encryption
  57. $zipFile->openFile($this->outputFilename);
  58. // set bad password WinZip AES
  59. $zipFile->setReadPassword($badPassword);
  60. foreach ($zipFile->getListFiles() as $entryName) {
  61. try {
  62. $zipFile[$entryName];
  63. $this->fail("Expected Exception has not been raised.");
  64. } catch (ZipAuthenticationException $ae) {
  65. $this->assertNotNull($ae);
  66. }
  67. }
  68. // set correct password WinZip AES
  69. $zipFile->setReadPassword($password);
  70. foreach ($zipFile->getAllInfo() as $info) {
  71. $this->assertTrue($info->isEncrypted());
  72. $this->assertContains('WinZip', $info->getMethodName());
  73. $decryptContent = $zipFile[$info->getName()];
  74. $this->assertNotEmpty($decryptContent);
  75. $this->assertContains('<?php', $decryptContent);
  76. }
  77. // clear password
  78. $zipFile->addFromString('file1', '');
  79. $zipFile->disableEncryption();
  80. $zipFile->addFromString('file2', '');
  81. $zipFile->saveAsFile($this->outputFilename);
  82. $zipFile->close();
  83. $this->assertCorrectZipArchive($this->outputFilename);
  84. // check remove password
  85. $zipFile->openFile($this->outputFilename);
  86. foreach ($zipFile->getAllInfo() as $info) {
  87. $this->assertFalse($info->isEncrypted());
  88. }
  89. $zipFile->close();
  90. }
  91. /**
  92. * @throws ZipException
  93. */
  94. public function testTraditionalEncryption()
  95. {
  96. if (PHP_INT_SIZE === 4) {
  97. $this->markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
  98. }
  99. $password = base64_encode(CryptoUtil::randomBytes(50));
  100. $zip = new ZipFile();
  101. $zip->addDirRecursive($this->outputDirname);
  102. $zip->setPassword($password, ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL);
  103. $zip->saveAsFile($this->outputFilename);
  104. $zip->close();
  105. $this->assertCorrectZipArchive($this->outputFilename, $password);
  106. $zip->openFile($this->outputFilename);
  107. $zip->setReadPassword($password);
  108. $this->assertFilesResult($zip, array_keys(self::$files));
  109. foreach ($zip->getAllInfo() as $info) {
  110. if (!$info->isFolder()) {
  111. $this->assertTrue($info->isEncrypted());
  112. $this->assertContains('ZipCrypto', $info->getMethodName());
  113. }
  114. }
  115. $zip->close();
  116. }
  117. /**
  118. * @dataProvider winZipKeyStrengthProvider
  119. * @param int $encryptionMethod
  120. * @param int $bitSize
  121. * @throws ZipException
  122. */
  123. public function testWinZipAesEncryption($encryptionMethod, $bitSize)
  124. {
  125. $password = base64_encode(CryptoUtil::randomBytes(50));
  126. $zip = new ZipFile();
  127. $zip->addDirRecursive($this->outputDirname);
  128. $zip->setPassword($password, $encryptionMethod);
  129. $zip->saveAsFile($this->outputFilename);
  130. $zip->close();
  131. $this->assertCorrectZipArchive($this->outputFilename, $password);
  132. $zip->openFile($this->outputFilename);
  133. $zip->setReadPassword($password);
  134. $this->assertFilesResult($zip, array_keys(self::$files));
  135. foreach ($zip->getAllInfo() as $info) {
  136. if (!$info->isFolder()) {
  137. $this->assertTrue($info->isEncrypted());
  138. $this->assertEquals($info->getEncryptionMethod(), $encryptionMethod);
  139. $this->assertContains('WinZip AES-' . $bitSize, $info->getMethodName());
  140. }
  141. }
  142. $zip->close();
  143. }
  144. /**
  145. * @return array
  146. */
  147. public function winZipKeyStrengthProvider()
  148. {
  149. return [
  150. [ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128, 128],
  151. [ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192, 192],
  152. [ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES, 256],
  153. [ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256, 256],
  154. ];
  155. }
  156. /**
  157. * @throws Exception\ZipEntryNotFoundException
  158. * @throws ZipException
  159. */
  160. public function testEncryptionEntries()
  161. {
  162. if (PHP_INT_SIZE === 4) {
  163. $this->markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
  164. }
  165. $password1 = '353442434235424234';
  166. $password2 = 'adgerhvrwjhqqehtqhkbqrgewg';
  167. $zip = new ZipFile();
  168. $zip->addDir($this->outputDirname);
  169. $zip->setPasswordEntry('.hidden', $password1, ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL);
  170. $zip->setPasswordEntry('text file.txt', $password2, ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES);
  171. $zip->saveAsFile($this->outputFilename);
  172. $zip->close();
  173. $zip->openFile($this->outputFilename);
  174. $zip->setReadPasswordEntry('.hidden', $password1);
  175. $zip->setReadPasswordEntry('text file.txt', $password2);
  176. $this->assertFilesResult($zip, [
  177. '.hidden',
  178. 'text file.txt',
  179. 'Текстовый документ.txt',
  180. 'empty dir/',
  181. ]);
  182. $info = $zip->getEntryInfo('.hidden');
  183. $this->assertTrue($info->isEncrypted());
  184. $this->assertContains('ZipCrypto', $info->getMethodName());
  185. $info = $zip->getEntryInfo('text file.txt');
  186. $this->assertTrue($info->isEncrypted());
  187. $this->assertContains('WinZip AES', $info->getMethodName());
  188. $this->assertFalse($zip->getEntryInfo('Текстовый документ.txt')->isEncrypted());
  189. $this->assertFalse($zip->getEntryInfo('empty dir/')->isEncrypted());
  190. $zip->close();
  191. }
  192. /**
  193. * @throws Exception\ZipEntryNotFoundException
  194. * @throws ZipException
  195. */
  196. public function testEncryptionEntriesWithDefaultPassword()
  197. {
  198. if (PHP_INT_SIZE === 4) {
  199. $this->markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
  200. }
  201. $password1 = '353442434235424234';
  202. $password2 = 'adgerhvrwjhqqehtqhkbqrgewg';
  203. $defaultPassword = ' f f f f f ffff f5 ';
  204. $zip = new ZipFile();
  205. $zip->addDir($this->outputDirname);
  206. $zip->setPassword($defaultPassword);
  207. $zip->setPasswordEntry('.hidden', $password1, ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL);
  208. $zip->setPasswordEntry('text file.txt', $password2, ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES);
  209. $zip->saveAsFile($this->outputFilename);
  210. $zip->close();
  211. $zip->openFile($this->outputFilename);
  212. $zip->setReadPassword($defaultPassword);
  213. $zip->setReadPasswordEntry('.hidden', $password1);
  214. $zip->setReadPasswordEntry('text file.txt', $password2);
  215. $this->assertFilesResult($zip, [
  216. '.hidden',
  217. 'text file.txt',
  218. 'Текстовый документ.txt',
  219. 'empty dir/',
  220. ]);
  221. $info = $zip->getEntryInfo('.hidden');
  222. $this->assertTrue($info->isEncrypted());
  223. $this->assertContains('ZipCrypto', $info->getMethodName());
  224. $info = $zip->getEntryInfo('text file.txt');
  225. $this->assertTrue($info->isEncrypted());
  226. $this->assertContains('WinZip AES', $info->getMethodName());
  227. $info = $zip->getEntryInfo('Текстовый документ.txt');
  228. $this->assertTrue($info->isEncrypted());
  229. $this->assertContains('WinZip AES', $info->getMethodName());
  230. $this->assertFalse($zip->getEntryInfo('empty dir/')->isEncrypted());
  231. $zip->close();
  232. }
  233. /**
  234. * @expectedException \PhpZip\Exception\ZipException
  235. * @expectedExceptionMessage Invalid encryption method
  236. */
  237. public function testSetEncryptionMethodInvalid()
  238. {
  239. $zipFile = new ZipFile();
  240. $encryptionMethod = 9999;
  241. $zipFile->setPassword('pass', $encryptionMethod);
  242. $zipFile['entry'] = 'content';
  243. $zipFile->outputAsString();
  244. }
  245. /**
  246. * @throws Exception\ZipEntryNotFoundException
  247. * @throws ZipException
  248. */
  249. public function testEntryPassword()
  250. {
  251. $zipFile = new ZipFile();
  252. $zipFile->setPassword('pass');
  253. $zipFile['file'] = 'content';
  254. $this->assertFalse($zipFile->getEntryInfo('file')->isEncrypted());
  255. for ($i = 1; $i <= 10; $i++) {
  256. $zipFile['file' . $i] = 'content';
  257. if ($i < 6) {
  258. $zipFile->setPasswordEntry('file' . $i, 'pass');
  259. $this->assertTrue($zipFile->getEntryInfo('file' . $i)->isEncrypted());
  260. } else {
  261. $this->assertFalse($zipFile->getEntryInfo('file' . $i)->isEncrypted());
  262. }
  263. }
  264. $zipFile->disableEncryptionEntry('file3');
  265. $this->assertFalse($zipFile->getEntryInfo('file3')->isEncrypted());
  266. $this->asserttrue($zipFile->getEntryInfo('file2')->isEncrypted());
  267. $zipFile->disableEncryption();
  268. $infoList = $zipFile->getAllInfo();
  269. array_walk($infoList, function (ZipInfo $zipInfo) {
  270. $this->assertFalse($zipInfo->isEncrypted());
  271. });
  272. $zipFile->close();
  273. }
  274. /**
  275. * @expectedException \PhpZip\Exception\ZipException
  276. * @expectedExceptionMessage Invalid encryption method
  277. */
  278. public function testInvalidEncryptionMethodEntry()
  279. {
  280. $zipFile = new ZipFile();
  281. $zipFile->addFromString('file', 'content', ZipFileInterface::METHOD_STORED);
  282. $zipFile->setPasswordEntry('file', 'pass', 99);
  283. }
  284. /**
  285. * @throws ZipEntryNotFoundException
  286. * @throws ZipException
  287. */
  288. public function testArchivePasswordUpdateWithoutSetReadPassword()
  289. {
  290. $zipFile = new ZipFile();
  291. $zipFile['file1'] = 'content';
  292. $zipFile['file2'] = 'content';
  293. $zipFile['file3'] = 'content';
  294. $zipFile->setPassword('password');
  295. $zipFile->saveAsFile($this->outputFilename);
  296. $zipFile->close();
  297. $this->assertCorrectZipArchive($this->outputFilename, 'password');
  298. $zipFile->openFile($this->outputFilename);
  299. $this->assertCount(3, $zipFile);
  300. foreach ($zipFile->getAllInfo() as $info) {
  301. $this->assertTrue($info->isEncrypted());
  302. }
  303. unset($zipFile['file3']);
  304. $zipFile['file4'] = 'content';
  305. $zipFile->rewrite();
  306. $this->assertCorrectZipArchive($this->outputFilename, 'password');
  307. $this->assertCount(3, $zipFile);
  308. $this->assertFalse(isset($zipFile['file3']));
  309. $this->assertTrue(isset($zipFile['file4']));
  310. $this->assertTrue($zipFile->getEntryInfo('file1')->isEncrypted());
  311. $this->assertTrue($zipFile->getEntryInfo('file2')->isEncrypted());
  312. $this->assertFalse($zipFile->getEntryInfo('file4')->isEncrypted());
  313. $this->assertEquals($zipFile['file4'], 'content');
  314. $zipFile->extractTo($this->outputDirname, ['file4']);
  315. $this->assertTrue(file_exists($this->outputDirname . DIRECTORY_SEPARATOR . 'file4'));
  316. $this->assertEquals(file_get_contents($this->outputDirname . DIRECTORY_SEPARATOR . 'file4'), $zipFile['file4']);
  317. $zipFile->close();
  318. }
  319. /**
  320. * @see https://github.com/Ne-Lexa/php-zip/issues/9
  321. * @throws ZipException
  322. */
  323. public function testIssues9()
  324. {
  325. $contents = str_pad('', 1000, 'test;test2;test3' . PHP_EOL, STR_PAD_RIGHT);
  326. $password = base64_encode(CryptoUtil::randomBytes(20));
  327. $encryptMethod = ZipFile::ENCRYPTION_METHOD_WINZIP_AES_256;
  328. $zipFile = new ZipFile();
  329. $zipFile
  330. ->addFromString('codes.csv', $contents)
  331. ->setPassword($password, $encryptMethod)
  332. ->saveAsFile($this->outputFilename)
  333. ->close();
  334. $this->assertCorrectZipArchive($this->outputFilename, $password);
  335. $zipFile->openFile($this->outputFilename);
  336. $zipFile->setReadPassword($password);
  337. $this->assertEquals($zipFile['codes.csv'], $contents);
  338. $zipFile->close();
  339. }
  340. /**
  341. * @throws ZipEntryNotFoundException
  342. * @throws ZipException
  343. */
  344. public function testReadAesEncryptedAndRewriteArchive()
  345. {
  346. $file = __DIR__ . '/resources/aes_password_archive.zip';
  347. $password = '1234567890';
  348. $zipFile = new ZipFile();
  349. $zipFile->openFile($file);
  350. $zipFile->setReadPassword($password);
  351. $zipFile->setEntryComment('contents.txt', 'comment'); // change entry, but not changed contents
  352. $zipFile->saveAsFile($this->outputFilename);
  353. $zipFile2 = new ZipFile();
  354. $zipFile2->openFile($this->outputFilename);
  355. $zipFile2->setReadPassword($password);
  356. $this->assertEquals($zipFile2->getListFiles(), $zipFile->getListFiles());
  357. foreach ($zipFile as $name => $contents) {
  358. $this->assertNotEmpty($name);
  359. $this->assertNotEmpty($contents);
  360. $this->assertContains('test contents', $contents);
  361. $this->assertEquals($zipFile2[$name], $contents);
  362. }
  363. $zipFile2->close();
  364. $zipFile->close();
  365. }
  366. }