PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/zendframework/zend-crypt/src/Symmetric/Mcrypt.php

https://gitlab.com/yousafsyed/easternglamor
PHP | 508 lines | 274 code | 43 blank | 191 comment | 29 complexity | 1e31d7b04969f9be65a71fb3c6d31336 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Crypt\Symmetric;
  10. use Traversable;
  11. use Zend\Stdlib\ArrayUtils;
  12. /**
  13. * Symmetric encryption using the Mcrypt extension
  14. *
  15. * NOTE: DO NOT USE only this class to encrypt data.
  16. * This class doesn't provide authentication and integrity check over the data.
  17. * PLEASE USE Zend\Crypt\BlockCipher instead!
  18. */
  19. class Mcrypt implements SymmetricInterface
  20. {
  21. const DEFAULT_PADDING = 'pkcs7';
  22. /**
  23. * Key
  24. *
  25. * @var string
  26. */
  27. protected $key;
  28. /**
  29. * IV
  30. *
  31. * @var string
  32. */
  33. protected $iv;
  34. /**
  35. * Encryption algorithm
  36. *
  37. * @var string
  38. */
  39. protected $algo = 'aes';
  40. /**
  41. * Encryption mode
  42. *
  43. * @var string
  44. */
  45. protected $mode = 'cbc';
  46. /**
  47. * Padding
  48. *
  49. * @var Padding\PaddingInterface
  50. */
  51. protected $padding;
  52. /**
  53. * Padding plugins
  54. *
  55. * @var PaddingPluginManager
  56. */
  57. protected static $paddingPlugins = null;
  58. /**
  59. * Supported cipher algorithms
  60. *
  61. * @var array
  62. */
  63. protected $supportedAlgos = array(
  64. 'aes' => 'rijndael-128',
  65. 'blowfish' => 'blowfish',
  66. 'des' => 'des',
  67. '3des' => 'tripledes',
  68. 'tripledes' => 'tripledes',
  69. 'cast-128' => 'cast-128',
  70. 'cast-256' => 'cast-256',
  71. 'rijndael-128' => 'rijndael-128',
  72. 'rijndael-192' => 'rijndael-192',
  73. 'rijndael-256' => 'rijndael-256',
  74. 'saferplus' => 'saferplus',
  75. 'serpent' => 'serpent',
  76. 'twofish' => 'twofish'
  77. );
  78. /**
  79. * Supported encryption modes
  80. *
  81. * @var array
  82. */
  83. protected $supportedModes = array(
  84. 'cbc' => 'cbc',
  85. 'cfb' => 'cfb',
  86. 'ctr' => 'ctr',
  87. 'ofb' => 'ofb',
  88. 'nofb' => 'nofb',
  89. 'ncfb' => 'ncfb'
  90. );
  91. /**
  92. * Constructor
  93. *
  94. * @param array|Traversable $options
  95. * @throws Exception\RuntimeException
  96. * @throws Exception\InvalidArgumentException
  97. */
  98. public function __construct($options = array())
  99. {
  100. if (!extension_loaded('mcrypt')) {
  101. throw new Exception\RuntimeException(
  102. 'You cannot use ' . __CLASS__ . ' without the Mcrypt extension'
  103. );
  104. }
  105. if (!empty($options)) {
  106. if ($options instanceof Traversable) {
  107. $options = ArrayUtils::iteratorToArray($options);
  108. } elseif (!is_array($options)) {
  109. throw new Exception\InvalidArgumentException(
  110. 'The options parameter must be an array, a Zend\Config\Config object or a Traversable'
  111. );
  112. }
  113. foreach ($options as $key => $value) {
  114. switch (strtolower($key)) {
  115. case 'algo':
  116. case 'algorithm':
  117. $this->setAlgorithm($value);
  118. break;
  119. case 'mode':
  120. $this->setMode($value);
  121. break;
  122. case 'key':
  123. $this->setKey($value);
  124. break;
  125. case 'iv':
  126. case 'salt':
  127. $this->setSalt($value);
  128. break;
  129. case 'padding':
  130. $plugins = static::getPaddingPluginManager();
  131. $padding = $plugins->get($value);
  132. $this->padding = $padding;
  133. break;
  134. }
  135. }
  136. }
  137. $this->setDefaultOptions($options);
  138. }
  139. /**
  140. * Set default options
  141. *
  142. * @param array $options
  143. * @return void
  144. */
  145. protected function setDefaultOptions($options = array())
  146. {
  147. if (!isset($options['padding'])) {
  148. $plugins = static::getPaddingPluginManager();
  149. $padding = $plugins->get(self::DEFAULT_PADDING);
  150. $this->padding = $padding;
  151. }
  152. }
  153. /**
  154. * Returns the padding plugin manager. If it doesn't exist it's created.
  155. *
  156. * @return PaddingPluginManager
  157. */
  158. public static function getPaddingPluginManager()
  159. {
  160. if (static::$paddingPlugins === null) {
  161. self::setPaddingPluginManager(new PaddingPluginManager());
  162. }
  163. return static::$paddingPlugins;
  164. }
  165. /**
  166. * Set the padding plugin manager
  167. *
  168. * @param string|PaddingPluginManager $plugins
  169. * @throws Exception\InvalidArgumentException
  170. * @return void
  171. */
  172. public static function setPaddingPluginManager($plugins)
  173. {
  174. if (is_string($plugins)) {
  175. if (!class_exists($plugins)) {
  176. throw new Exception\InvalidArgumentException(sprintf(
  177. 'Unable to locate padding plugin manager via class "%s"; class does not exist',
  178. $plugins
  179. ));
  180. }
  181. $plugins = new $plugins();
  182. }
  183. if (!$plugins instanceof PaddingPluginManager) {
  184. throw new Exception\InvalidArgumentException(sprintf(
  185. 'Padding plugins must extend %s\PaddingPluginManager; received "%s"',
  186. __NAMESPACE__,
  187. (is_object($plugins) ? get_class($plugins) : gettype($plugins))
  188. ));
  189. }
  190. static::$paddingPlugins = $plugins;
  191. }
  192. /**
  193. * Get the maximum key size for the selected cipher and mode of operation
  194. *
  195. * @return int
  196. */
  197. public function getKeySize()
  198. {
  199. return mcrypt_get_key_size($this->supportedAlgos[$this->algo], $this->supportedModes[$this->mode]);
  200. }
  201. /**
  202. * Set the encryption key
  203. * If the key is longer than maximum supported, it will be truncated by getKey().
  204. *
  205. * @param string $key
  206. * @throws Exception\InvalidArgumentException
  207. * @return Mcrypt
  208. */
  209. public function setKey($key)
  210. {
  211. $keyLen = strlen($key);
  212. if (!$keyLen) {
  213. throw new Exception\InvalidArgumentException('The key cannot be empty');
  214. }
  215. $keySizes = mcrypt_module_get_supported_key_sizes($this->supportedAlgos[$this->algo]);
  216. $maxKey = $this->getKeySize();
  217. /*
  218. * blowfish has $keySizes empty, meaning it can have arbitrary key length.
  219. * the others are more picky.
  220. */
  221. if (!empty($keySizes) && $keyLen < $maxKey) {
  222. if (!in_array($keyLen, $keySizes)) {
  223. throw new Exception\InvalidArgumentException(
  224. "The size of the key must be one of " . implode(", ", $keySizes) . " bytes or longer"
  225. );
  226. }
  227. }
  228. $this->key = $key;
  229. return $this;
  230. }
  231. /**
  232. * Get the encryption key
  233. *
  234. * @return string
  235. */
  236. public function getKey()
  237. {
  238. if (empty($this->key)) {
  239. return;
  240. }
  241. return substr($this->key, 0, $this->getKeySize());
  242. }
  243. /**
  244. * Set the encryption algorithm (cipher)
  245. *
  246. * @param string $algo
  247. * @throws Exception\InvalidArgumentException
  248. * @return Mcrypt
  249. */
  250. public function setAlgorithm($algo)
  251. {
  252. if (!array_key_exists($algo, $this->supportedAlgos)) {
  253. throw new Exception\InvalidArgumentException(
  254. "The algorithm $algo is not supported by " . __CLASS__
  255. );
  256. }
  257. $this->algo = $algo;
  258. return $this;
  259. }
  260. /**
  261. * Get the encryption algorithm
  262. *
  263. * @return string
  264. */
  265. public function getAlgorithm()
  266. {
  267. return $this->algo;
  268. }
  269. /**
  270. * Set the padding object
  271. *
  272. * @param Padding\PaddingInterface $padding
  273. * @return Mcrypt
  274. */
  275. public function setPadding(Padding\PaddingInterface $padding)
  276. {
  277. $this->padding = $padding;
  278. return $this;
  279. }
  280. /**
  281. * Get the padding object
  282. *
  283. * @return Padding\PaddingInterface
  284. */
  285. public function getPadding()
  286. {
  287. return $this->padding;
  288. }
  289. /**
  290. * Encrypt
  291. *
  292. * @param string $data
  293. * @throws Exception\InvalidArgumentException
  294. * @return string
  295. */
  296. public function encrypt($data)
  297. {
  298. // Cannot encrypt empty string
  299. if (!is_string($data) || $data === '') {
  300. throw new Exception\InvalidArgumentException('The data to encrypt cannot be empty');
  301. }
  302. if (null === $this->getKey()) {
  303. throw new Exception\InvalidArgumentException('No key specified for the encryption');
  304. }
  305. if (null === $this->getSalt()) {
  306. throw new Exception\InvalidArgumentException('The salt (IV) cannot be empty');
  307. }
  308. if (null === $this->getPadding()) {
  309. throw new Exception\InvalidArgumentException('You have to specify a padding method');
  310. }
  311. // padding
  312. $data = $this->padding->pad($data, $this->getBlockSize());
  313. $iv = $this->getSalt();
  314. // encryption
  315. $result = mcrypt_encrypt(
  316. $this->supportedAlgos[$this->algo],
  317. $this->getKey(),
  318. $data,
  319. $this->supportedModes[$this->mode],
  320. $iv
  321. );
  322. return $iv . $result;
  323. }
  324. /**
  325. * Decrypt
  326. *
  327. * @param string $data
  328. * @throws Exception\InvalidArgumentException
  329. * @return string
  330. */
  331. public function decrypt($data)
  332. {
  333. if (empty($data)) {
  334. throw new Exception\InvalidArgumentException('The data to decrypt cannot be empty');
  335. }
  336. if (null === $this->getKey()) {
  337. throw new Exception\InvalidArgumentException('No key specified for the decryption');
  338. }
  339. if (null === $this->getPadding()) {
  340. throw new Exception\InvalidArgumentException('You have to specify a padding method');
  341. }
  342. $iv = substr($data, 0, $this->getSaltSize());
  343. $ciphertext = substr($data, $this->getSaltSize());
  344. $result = mcrypt_decrypt(
  345. $this->supportedAlgos[$this->algo],
  346. $this->getKey(),
  347. $ciphertext,
  348. $this->supportedModes[$this->mode],
  349. $iv
  350. );
  351. // unpadding
  352. return $this->padding->strip($result);
  353. }
  354. /**
  355. * Get the salt (IV) size
  356. *
  357. * @return int
  358. */
  359. public function getSaltSize()
  360. {
  361. return mcrypt_get_iv_size($this->supportedAlgos[$this->algo], $this->supportedModes[$this->mode]);
  362. }
  363. /**
  364. * Get the supported algorithms
  365. *
  366. * @return array
  367. */
  368. public function getSupportedAlgorithms()
  369. {
  370. return array_keys($this->supportedAlgos);
  371. }
  372. /**
  373. * Set the salt (IV)
  374. *
  375. * @param string $salt
  376. * @throws Exception\InvalidArgumentException
  377. * @return Mcrypt
  378. */
  379. public function setSalt($salt)
  380. {
  381. if (empty($salt)) {
  382. throw new Exception\InvalidArgumentException('The salt (IV) cannot be empty');
  383. }
  384. if (strlen($salt) < $this->getSaltSize()) {
  385. throw new Exception\InvalidArgumentException(
  386. 'The size of the salt (IV) must be at least ' . $this->getSaltSize() . ' bytes'
  387. );
  388. }
  389. $this->iv = $salt;
  390. return $this;
  391. }
  392. /**
  393. * Get the salt (IV) according to the size requested by the algorithm
  394. *
  395. * @return string
  396. */
  397. public function getSalt()
  398. {
  399. if (empty($this->iv)) {
  400. return;
  401. }
  402. if (strlen($this->iv) < $this->getSaltSize()) {
  403. throw new Exception\RuntimeException(
  404. 'The size of the salt (IV) must be at least ' . $this->getSaltSize() . ' bytes'
  405. );
  406. }
  407. return substr($this->iv, 0, $this->getSaltSize());
  408. }
  409. /**
  410. * Get the original salt value
  411. *
  412. * @return string
  413. */
  414. public function getOriginalSalt()
  415. {
  416. return $this->iv;
  417. }
  418. /**
  419. * Set the cipher mode
  420. *
  421. * @param string $mode
  422. * @throws Exception\InvalidArgumentException
  423. * @return Mcrypt
  424. */
  425. public function setMode($mode)
  426. {
  427. if (!empty($mode)) {
  428. $mode = strtolower($mode);
  429. if (!array_key_exists($mode, $this->supportedModes)) {
  430. throw new Exception\InvalidArgumentException(
  431. "The mode $mode is not supported by " . __CLASS__
  432. );
  433. }
  434. $this->mode = $mode;
  435. }
  436. return $this;
  437. }
  438. /**
  439. * Get the cipher mode
  440. *
  441. * @return string
  442. */
  443. public function getMode()
  444. {
  445. return $this->mode;
  446. }
  447. /**
  448. * Get all supported encryption modes
  449. *
  450. * @return array
  451. */
  452. public function getSupportedModes()
  453. {
  454. return array_keys($this->supportedModes);
  455. }
  456. /**
  457. * Get the block size
  458. *
  459. * @return int
  460. */
  461. public function getBlockSize()
  462. {
  463. return mcrypt_get_block_size($this->supportedAlgos[$this->algo], $this->supportedModes[$this->mode]);
  464. }
  465. }