ZipPasswordTest.php 16 KB

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