WinZipAesEntryExtraField.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?php
  2. namespace PhpZip\Extra\Fields;
  3. use PhpZip\Exception\ZipException;
  4. use PhpZip\Extra\ExtraField;
  5. use PhpZip\ZipFileInterface;
  6. /**
  7. * WinZip AES Extra Field.
  8. *
  9. * @see http://www.winzip.com/win/en/aes_info.htm AES Encryption Information: Encryption Specification AE-1 and AE-2 (WinZip Computing, S.L.)
  10. * @see http://www.winzip.com/win/en/aes_tips.htm AES Coding Tips for Developers (WinZip Computing, S.L.)
  11. * @author Ne-Lexa alexey@nelexa.ru
  12. * @license MIT
  13. */
  14. class WinZipAesEntryExtraField implements ExtraField
  15. {
  16. const DATA_SIZE = 7;
  17. const VENDOR_ID = 17729; // 'A' | ('E' << 8);
  18. /**
  19. * Entries of this type <em>do</em> include the standard ZIP CRC-32 value.
  20. * For use with @see WinZipAesEntryExtraField::setVendorVersion()}/@see WinZipAesEntryExtraField::getVendorVersion().
  21. */
  22. const VV_AE_1 = 1;
  23. /**
  24. * Entries of this type do <em>not</em> include the standard ZIP CRC-32 value.
  25. * For use with @see WinZipAesEntryExtraField::setVendorVersion()}/@see WinZipAesEntryExtraField::getVendorVersion().
  26. */
  27. const VV_AE_2 = 2;
  28. const KEY_STRENGTH_128BIT = 128;
  29. const KEY_STRENGTH_192BIT = 192;
  30. const KEY_STRENGTH_256BIT = 256;
  31. protected static $keyStrengths = [
  32. self::KEY_STRENGTH_128BIT => 0x01,
  33. self::KEY_STRENGTH_192BIT => 0x02,
  34. self::KEY_STRENGTH_256BIT => 0x03
  35. ];
  36. protected static $encryptionMethods = [
  37. self::KEY_STRENGTH_128BIT => ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128,
  38. self::KEY_STRENGTH_192BIT => ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192,
  39. self::KEY_STRENGTH_256BIT => ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
  40. ];
  41. /**
  42. * Vendor version.
  43. *
  44. * @var int
  45. */
  46. protected $vendorVersion = self::VV_AE_1;
  47. /**
  48. * Encryption strength.
  49. *
  50. * @var int
  51. */
  52. protected $encryptionStrength = self::KEY_STRENGTH_256BIT;
  53. /**
  54. * Zip compression method.
  55. *
  56. * @var int
  57. */
  58. protected $method;
  59. /**
  60. * Returns the Header ID (type) of this Extra Field.
  61. * The Header ID is an unsigned short integer (two bytes)
  62. * which must be constant during the life cycle of this object.
  63. *
  64. * @return int
  65. */
  66. public static function getHeaderId()
  67. {
  68. return 0x9901;
  69. }
  70. /**
  71. * Returns the vendor version.
  72. *
  73. * @see WinZipAesEntryExtraField::VV_AE_1
  74. * @see WinZipAesEntryExtraField::VV_AE_2
  75. */
  76. public function getVendorVersion()
  77. {
  78. return $this->vendorVersion & 0xffff;
  79. }
  80. /**
  81. * Sets the vendor version.
  82. *
  83. * @see WinZipAesEntryExtraField::VV_AE_1
  84. * @see WinZipAesEntryExtraField::VV_AE_2
  85. * @param int $vendorVersion the vendor version.
  86. * @throws ZipException Unsupport vendor version.
  87. */
  88. public function setVendorVersion($vendorVersion)
  89. {
  90. if ($vendorVersion < self::VV_AE_1 || self::VV_AE_2 < $vendorVersion) {
  91. throw new ZipException($vendorVersion);
  92. }
  93. $this->vendorVersion = $vendorVersion;
  94. }
  95. /**
  96. * Returns vendor id.
  97. *
  98. * @return int
  99. */
  100. public function getVendorId()
  101. {
  102. return self::VENDOR_ID;
  103. }
  104. /**
  105. * @return bool|int
  106. * @throws ZipException
  107. */
  108. public function getKeyStrength()
  109. {
  110. return self::keyStrength($this->encryptionStrength);
  111. }
  112. /**
  113. * @param int $encryptionStrength Encryption strength as bits.
  114. * @return int
  115. * @throws ZipException If unsupport encryption strength.
  116. */
  117. public static function keyStrength($encryptionStrength)
  118. {
  119. $flipKeyStrength = array_flip(self::$keyStrengths);
  120. if (!isset($flipKeyStrength[$encryptionStrength])) {
  121. throw new ZipException("Unsupport encryption strength " . $encryptionStrength);
  122. }
  123. return $flipKeyStrength[$encryptionStrength];
  124. }
  125. /**
  126. * Returns compression method.
  127. *
  128. * @return int
  129. */
  130. public function getMethod()
  131. {
  132. return $this->method & 0xffff;
  133. }
  134. /**
  135. * Internal encryption method.
  136. *
  137. * @return int
  138. * @throws ZipException
  139. */
  140. public function getEncryptionMethod()
  141. {
  142. return isset(self::$encryptionMethods[$this->getKeyStrength()]) ?
  143. self::$encryptionMethods[$this->getKeyStrength()] :
  144. self::$encryptionMethods[self::KEY_STRENGTH_256BIT];
  145. }
  146. /**
  147. * @param int $encryptionMethod
  148. * @return int
  149. * @throws ZipException
  150. */
  151. public static function getKeyStrangeFromEncryptionMethod($encryptionMethod)
  152. {
  153. $flipKey = array_flip(self::$encryptionMethods);
  154. if (!isset($flipKey[$encryptionMethod])) {
  155. throw new ZipException("Unsupport encryption method " . $encryptionMethod);
  156. }
  157. return $flipKey[$encryptionMethod];
  158. }
  159. /**
  160. * Sets compression method.
  161. *
  162. * @param int $compressionMethod Compression method
  163. * @throws ZipException Compression method out of range.
  164. */
  165. public function setMethod($compressionMethod)
  166. {
  167. if (0x0000 > $compressionMethod || $compressionMethod > 0xffff) {
  168. throw new ZipException('Compression method out of range');
  169. }
  170. $this->method = $compressionMethod;
  171. }
  172. /**
  173. * Set key strength.
  174. *
  175. * @param int $keyStrength
  176. */
  177. public function setKeyStrength($keyStrength)
  178. {
  179. $this->encryptionStrength = self::encryptionStrength($keyStrength);
  180. }
  181. /**
  182. * Returns encryption strength.
  183. *
  184. * @param int $keyStrength Key strength in bits.
  185. * @return int
  186. */
  187. public static function encryptionStrength($keyStrength)
  188. {
  189. return isset(self::$keyStrengths[$keyStrength]) ?
  190. self::$keyStrengths[$keyStrength] :
  191. self::$keyStrengths[self::KEY_STRENGTH_128BIT];
  192. }
  193. /**
  194. * Serializes a Data Block.
  195. * @return string
  196. */
  197. public function serialize()
  198. {
  199. return pack(
  200. 'vvcv',
  201. $this->vendorVersion,
  202. self::VENDOR_ID,
  203. $this->encryptionStrength,
  204. $this->method
  205. );
  206. }
  207. /**
  208. * Initializes this Extra Field by deserializing a Data Block.
  209. * @param string $data
  210. * @throws ZipException
  211. */
  212. public function deserialize($data)
  213. {
  214. $size = strlen($data);
  215. if (self::DATA_SIZE !== $size) {
  216. throw new ZipException('WinZip AES Extra data invalid size: ' . $size . '. Must be ' . self::DATA_SIZE);
  217. }
  218. /**
  219. * @var int $vendorVersion
  220. * @var int $vendorId
  221. * @var int $keyStrength
  222. * @var int $method
  223. */
  224. $unpack = unpack('vvendorVersion/vvendorId/ckeyStrength/vmethod', $data);
  225. $this->setVendorVersion($unpack['vendorVersion']);
  226. if (self::VENDOR_ID !== $unpack['vendorId']) {
  227. throw new ZipException('Vendor id invalid: ' . $unpack['vendorId'] . '. Must be ' . self::VENDOR_ID);
  228. }
  229. $this->setKeyStrength(self::keyStrength($unpack['keyStrength'])); // checked
  230. $this->setMethod($unpack['method']);
  231. }
  232. }