Encryption.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP
  6. *
  7. * This content is released under the MIT License (MIT)
  8. *
  9. * Copyright (c) 2014 - 2019, British Columbia Institute of Technology
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. * THE SOFTWARE.
  28. *
  29. * @package CodeIgniter
  30. * @author EllisLab Dev Team
  31. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
  32. * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
  33. * @license https://opensource.org/licenses/MIT MIT License
  34. * @link https://codeigniter.com
  35. * @since Version 3.0.0
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39. /**
  40. * CodeIgniter Encryption Class
  41. *
  42. * Provides two-way keyed encryption via PHP's MCrypt and/or OpenSSL extensions.
  43. *
  44. * @package CodeIgniter
  45. * @subpackage Libraries
  46. * @category Libraries
  47. * @author Andrey Andreev
  48. * @link https://codeigniter.com/user_guide/libraries/encryption.html
  49. */
  50. class CI_Encryption {
  51. /**
  52. * Encryption cipher
  53. *
  54. * @var string
  55. */
  56. protected $_cipher = 'aes-128';
  57. /**
  58. * Cipher mode
  59. *
  60. * @var string
  61. */
  62. protected $_mode = 'cbc';
  63. /**
  64. * Cipher handle
  65. *
  66. * @var mixed
  67. */
  68. protected $_handle;
  69. /**
  70. * Encryption key
  71. *
  72. * @var string
  73. */
  74. protected $_key;
  75. /**
  76. * PHP extension to be used
  77. *
  78. * @var string
  79. */
  80. protected $_driver;
  81. /**
  82. * List of usable drivers (PHP extensions)
  83. *
  84. * @var array
  85. */
  86. protected $_drivers = array();
  87. /**
  88. * List of available modes
  89. *
  90. * @var array
  91. */
  92. protected $_modes = array(
  93. 'mcrypt' => array(
  94. 'cbc' => 'cbc',
  95. 'ecb' => 'ecb',
  96. 'ofb' => 'nofb',
  97. 'ofb8' => 'ofb',
  98. 'cfb' => 'ncfb',
  99. 'cfb8' => 'cfb',
  100. 'ctr' => 'ctr',
  101. 'stream' => 'stream'
  102. ),
  103. 'openssl' => array(
  104. 'cbc' => 'cbc',
  105. 'ecb' => 'ecb',
  106. 'ofb' => 'ofb',
  107. 'cfb' => 'cfb',
  108. 'cfb8' => 'cfb8',
  109. 'ctr' => 'ctr',
  110. 'stream' => '',
  111. 'xts' => 'xts'
  112. )
  113. );
  114. /**
  115. * List of supported HMAC algorithms
  116. *
  117. * name => digest size pairs
  118. *
  119. * @var array
  120. */
  121. protected $_digests = array(
  122. 'sha224' => 28,
  123. 'sha256' => 32,
  124. 'sha384' => 48,
  125. 'sha512' => 64
  126. );
  127. /**
  128. * mbstring.func_overload flag
  129. *
  130. * @var bool
  131. */
  132. protected static $func_overload;
  133. // --------------------------------------------------------------------
  134. /**
  135. * Class constructor
  136. *
  137. * @param array $params Configuration parameters
  138. * @return void
  139. */
  140. public function __construct(array $params = array())
  141. {
  142. $this->_drivers = array(
  143. 'mcrypt' => defined('MCRYPT_DEV_URANDOM'),
  144. 'openssl' => extension_loaded('openssl')
  145. );
  146. if ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl'])
  147. {
  148. show_error('Encryption: Unable to find an available encryption driver.');
  149. }
  150. isset(self::$func_overload) OR self::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload'));
  151. $this->initialize($params);
  152. if ( ! isset($this->_key) && self::strlen($key = config_item('encryption_key')) > 0)
  153. {
  154. $this->_key = $key;
  155. }
  156. log_message('info', 'Encryption Class Initialized');
  157. }
  158. // --------------------------------------------------------------------
  159. /**
  160. * Initialize
  161. *
  162. * @param array $params Configuration parameters
  163. * @return CI_Encryption
  164. */
  165. public function initialize(array $params)
  166. {
  167. if ( ! empty($params['driver']))
  168. {
  169. if (isset($this->_drivers[$params['driver']]))
  170. {
  171. if ($this->_drivers[$params['driver']])
  172. {
  173. $this->_driver = $params['driver'];
  174. }
  175. else
  176. {
  177. log_message('error', "Encryption: Driver '".$params['driver']."' is not available.");
  178. }
  179. }
  180. else
  181. {
  182. log_message('error', "Encryption: Unknown driver '".$params['driver']."' cannot be configured.");
  183. }
  184. }
  185. if (empty($this->_driver))
  186. {
  187. $this->_driver = ($this->_drivers['openssl'] === TRUE)
  188. ? 'openssl'
  189. : 'mcrypt';
  190. log_message('debug', "Encryption: Auto-configured driver '".$this->_driver."'.");
  191. }
  192. empty($params['cipher']) && $params['cipher'] = $this->_cipher;
  193. empty($params['key']) OR $this->_key = $params['key'];
  194. $this->{'_'.$this->_driver.'_initialize'}($params);
  195. return $this;
  196. }
  197. // --------------------------------------------------------------------
  198. /**
  199. * Initialize MCrypt
  200. *
  201. * @param array $params Configuration parameters
  202. * @return void
  203. */
  204. protected function _mcrypt_initialize($params)
  205. {
  206. if ( ! empty($params['cipher']))
  207. {
  208. $params['cipher'] = strtolower($params['cipher']);
  209. $this->_cipher_alias($params['cipher']);
  210. if ( ! in_array($params['cipher'], mcrypt_list_algorithms(), TRUE))
  211. {
  212. log_message('error', 'Encryption: MCrypt cipher '.strtoupper($params['cipher']).' is not available.');
  213. }
  214. else
  215. {
  216. $this->_cipher = $params['cipher'];
  217. }
  218. }
  219. if ( ! empty($params['mode']))
  220. {
  221. $params['mode'] = strtolower($params['mode']);
  222. if ( ! isset($this->_modes['mcrypt'][$params['mode']]))
  223. {
  224. log_message('error', 'Encryption: MCrypt mode '.strtoupper($params['mode']).' is not available.');
  225. }
  226. else
  227. {
  228. $this->_mode = $this->_modes['mcrypt'][$params['mode']];
  229. }
  230. }
  231. if (isset($this->_cipher, $this->_mode))
  232. {
  233. if (is_resource($this->_handle)
  234. && (strtolower(mcrypt_enc_get_algorithms_name($this->_handle)) !== $this->_cipher
  235. OR strtolower(mcrypt_enc_get_modes_name($this->_handle)) !== $this->_mode)
  236. )
  237. {
  238. mcrypt_module_close($this->_handle);
  239. }
  240. if ($this->_handle = mcrypt_module_open($this->_cipher, '', $this->_mode, ''))
  241. {
  242. log_message('info', 'Encryption: MCrypt cipher '.strtoupper($this->_cipher).' initialized in '.strtoupper($this->_mode).' mode.');
  243. }
  244. else
  245. {
  246. log_message('error', 'Encryption: Unable to initialize MCrypt with cipher '.strtoupper($this->_cipher).' in '.strtoupper($this->_mode).' mode.');
  247. }
  248. }
  249. }
  250. // --------------------------------------------------------------------
  251. /**
  252. * Initialize OpenSSL
  253. *
  254. * @param array $params Configuration parameters
  255. * @return void
  256. */
  257. protected function _openssl_initialize($params)
  258. {
  259. if ( ! empty($params['cipher']))
  260. {
  261. $params['cipher'] = strtolower($params['cipher']);
  262. $this->_cipher_alias($params['cipher']);
  263. $this->_cipher = $params['cipher'];
  264. }
  265. if ( ! empty($params['mode']))
  266. {
  267. $params['mode'] = strtolower($params['mode']);
  268. if ( ! isset($this->_modes['openssl'][$params['mode']]))
  269. {
  270. log_message('error', 'Encryption: OpenSSL mode '.strtoupper($params['mode']).' is not available.');
  271. }
  272. else
  273. {
  274. $this->_mode = $this->_modes['openssl'][$params['mode']];
  275. }
  276. }
  277. if (isset($this->_cipher, $this->_mode))
  278. {
  279. // This is mostly for the stream mode, which doesn't get suffixed in OpenSSL
  280. $handle = empty($this->_mode)
  281. ? $this->_cipher
  282. : $this->_cipher.'-'.$this->_mode;
  283. if ( ! in_array($handle, openssl_get_cipher_methods(), TRUE))
  284. {
  285. $this->_handle = NULL;
  286. log_message('error', 'Encryption: Unable to initialize OpenSSL with method '.strtoupper($handle).'.');
  287. }
  288. else
  289. {
  290. $this->_handle = $handle;
  291. log_message('info', 'Encryption: OpenSSL initialized with method '.strtoupper($handle).'.');
  292. }
  293. }
  294. }
  295. // --------------------------------------------------------------------
  296. /**
  297. * Create a random key
  298. *
  299. * @param int $length Output length
  300. * @return string
  301. */
  302. public function create_key($length)
  303. {
  304. if (function_exists('random_bytes'))
  305. {
  306. try
  307. {
  308. return random_bytes((int) $length);
  309. }
  310. catch (Exception $e)
  311. {
  312. log_message('error', $e->getMessage());
  313. return FALSE;
  314. }
  315. }
  316. elseif (defined('MCRYPT_DEV_URANDOM'))
  317. {
  318. return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
  319. }
  320. $is_secure = NULL;
  321. $key = openssl_random_pseudo_bytes($length, $is_secure);
  322. return ($is_secure === TRUE)
  323. ? $key
  324. : FALSE;
  325. }
  326. // --------------------------------------------------------------------
  327. /**
  328. * Encrypt
  329. *
  330. * @param string $data Input data
  331. * @param array $params Input parameters
  332. * @return string
  333. */
  334. public function encrypt($data, array $params = NULL)
  335. {
  336. if (($params = $this->_get_params($params)) === FALSE)
  337. {
  338. return FALSE;
  339. }
  340. isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');
  341. if (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE)
  342. {
  343. return FALSE;
  344. }
  345. $params['base64'] && $data = base64_encode($data);
  346. if (isset($params['hmac_digest']))
  347. {
  348. isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
  349. return hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']).$data;
  350. }
  351. return $data;
  352. }
  353. // --------------------------------------------------------------------
  354. /**
  355. * Encrypt via MCrypt
  356. *
  357. * @param string $data Input data
  358. * @param array $params Input parameters
  359. * @return string
  360. */
  361. protected function _mcrypt_encrypt($data, $params)
  362. {
  363. if ( ! is_resource($params['handle']))
  364. {
  365. return FALSE;
  366. }
  367. // The greater-than-1 comparison is mostly a work-around for a bug,
  368. // where 1 is returned for ARCFour instead of 0.
  369. $iv = (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)
  370. ? $this->create_key($iv_size)
  371. : NULL;
  372. if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)
  373. {
  374. if ($params['handle'] !== $this->_handle)
  375. {
  376. mcrypt_module_close($params['handle']);
  377. }
  378. return FALSE;
  379. }
  380. // Use PKCS#7 padding in order to ensure compatibility with OpenSSL
  381. // and other implementations outside of PHP.
  382. if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
  383. {
  384. $block_size = mcrypt_enc_get_block_size($params['handle']);
  385. $pad = $block_size - (self::strlen($data) % $block_size);
  386. $data .= str_repeat(chr($pad), $pad);
  387. }
  388. // Work-around for yet another strange behavior in MCrypt.
  389. //
  390. // When encrypting in ECB mode, the IV is ignored. Yet
  391. // mcrypt_enc_get_iv_size() returns a value larger than 0
  392. // even if ECB is used AND mcrypt_generic_init() complains
  393. // if you don't pass an IV with length equal to the said
  394. // return value.
  395. //
  396. // This probably would've been fine (even though still wasteful),
  397. // but OpenSSL isn't that dumb and we need to make the process
  398. // portable, so ...
  399. $data = (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
  400. ? $iv.mcrypt_generic($params['handle'], $data)
  401. : mcrypt_generic($params['handle'], $data);
  402. mcrypt_generic_deinit($params['handle']);
  403. if ($params['handle'] !== $this->_handle)
  404. {
  405. mcrypt_module_close($params['handle']);
  406. }
  407. return $data;
  408. }
  409. // --------------------------------------------------------------------
  410. /**
  411. * Encrypt via OpenSSL
  412. *
  413. * @param string $data Input data
  414. * @param array $params Input parameters
  415. * @return string
  416. */
  417. protected function _openssl_encrypt($data, $params)
  418. {
  419. if (empty($params['handle']))
  420. {
  421. return FALSE;
  422. }
  423. $iv = ($iv_size = openssl_cipher_iv_length($params['handle']))
  424. ? $this->create_key($iv_size)
  425. : NULL;
  426. $data = openssl_encrypt(
  427. $data,
  428. $params['handle'],
  429. $params['key'],
  430. 1, // DO NOT TOUCH!
  431. $iv
  432. );
  433. if ($data === FALSE)
  434. {
  435. return FALSE;
  436. }
  437. return $iv.$data;
  438. }
  439. // --------------------------------------------------------------------
  440. /**
  441. * Decrypt
  442. *
  443. * @param string $data Encrypted data
  444. * @param array $params Input parameters
  445. * @return string
  446. */
  447. public function decrypt($data, array $params = NULL)
  448. {
  449. if (($params = $this->_get_params($params)) === FALSE)
  450. {
  451. return FALSE;
  452. }
  453. if (isset($params['hmac_digest']))
  454. {
  455. // This might look illogical, but it is done during encryption as well ...
  456. // The 'base64' value is effectively an inverted "raw data" parameter
  457. $digest_size = ($params['base64'])
  458. ? $this->_digests[$params['hmac_digest']] * 2
  459. : $this->_digests[$params['hmac_digest']];
  460. if (self::strlen($data) <= $digest_size)
  461. {
  462. return FALSE;
  463. }
  464. $hmac_input = self::substr($data, 0, $digest_size);
  465. $data = self::substr($data, $digest_size);
  466. isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
  467. $hmac_check = hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']);
  468. // Time-attack-safe comparison
  469. $diff = 0;
  470. for ($i = 0; $i < $digest_size; $i++)
  471. {
  472. $diff |= ord($hmac_input[$i]) ^ ord($hmac_check[$i]);
  473. }
  474. if ($diff !== 0)
  475. {
  476. return FALSE;
  477. }
  478. }
  479. if ($params['base64'])
  480. {
  481. $data = base64_decode($data);
  482. }
  483. isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');
  484. return $this->{'_'.$this->_driver.'_decrypt'}($data, $params);
  485. }
  486. // --------------------------------------------------------------------
  487. /**
  488. * Decrypt via MCrypt
  489. *
  490. * @param string $data Encrypted data
  491. * @param array $params Input parameters
  492. * @return string
  493. */
  494. protected function _mcrypt_decrypt($data, $params)
  495. {
  496. if ( ! is_resource($params['handle']))
  497. {
  498. return FALSE;
  499. }
  500. // The greater-than-1 comparison is mostly a work-around for a bug,
  501. // where 1 is returned for ARCFour instead of 0.
  502. if (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)
  503. {
  504. if (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
  505. {
  506. $iv = self::substr($data, 0, $iv_size);
  507. $data = self::substr($data, $iv_size);
  508. }
  509. else
  510. {
  511. // MCrypt is dumb and this is ignored, only size matters
  512. $iv = str_repeat("\x0", $iv_size);
  513. }
  514. }
  515. else
  516. {
  517. $iv = NULL;
  518. }
  519. if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)
  520. {
  521. if ($params['handle'] !== $this->_handle)
  522. {
  523. mcrypt_module_close($params['handle']);
  524. }
  525. return FALSE;
  526. }
  527. $data = mdecrypt_generic($params['handle'], $data);
  528. // Remove PKCS#7 padding, if necessary
  529. if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
  530. {
  531. $data = self::substr($data, 0, -ord($data[self::strlen($data)-1]));
  532. }
  533. mcrypt_generic_deinit($params['handle']);
  534. if ($params['handle'] !== $this->_handle)
  535. {
  536. mcrypt_module_close($params['handle']);
  537. }
  538. return $data;
  539. }
  540. // --------------------------------------------------------------------
  541. /**
  542. * Decrypt via OpenSSL
  543. *
  544. * @param string $data Encrypted data
  545. * @param array $params Input parameters
  546. * @return string
  547. */
  548. protected function _openssl_decrypt($data, $params)
  549. {
  550. if ($iv_size = openssl_cipher_iv_length($params['handle']))
  551. {
  552. $iv = self::substr($data, 0, $iv_size);
  553. $data = self::substr($data, $iv_size);
  554. }
  555. else
  556. {
  557. $iv = NULL;
  558. }
  559. return empty($params['handle'])
  560. ? FALSE
  561. : openssl_decrypt(
  562. $data,
  563. $params['handle'],
  564. $params['key'],
  565. 1, // DO NOT TOUCH!
  566. $iv
  567. );
  568. }
  569. // --------------------------------------------------------------------
  570. /**
  571. * Get params
  572. *
  573. * @param array $params Input parameters
  574. * @return array
  575. */
  576. protected function _get_params($params)
  577. {
  578. if (empty($params))
  579. {
  580. return isset($this->_cipher, $this->_mode, $this->_key, $this->_handle)
  581. ? array(
  582. 'handle' => $this->_handle,
  583. 'cipher' => $this->_cipher,
  584. 'mode' => $this->_mode,
  585. 'key' => NULL,
  586. 'base64' => TRUE,
  587. 'hmac_digest' => 'sha512',
  588. 'hmac_key' => NULL
  589. )
  590. : FALSE;
  591. }
  592. elseif ( ! isset($params['cipher'], $params['mode'], $params['key']))
  593. {
  594. return FALSE;
  595. }
  596. if (isset($params['mode']))
  597. {
  598. $params['mode'] = strtolower($params['mode']);
  599. if ( ! isset($this->_modes[$this->_driver][$params['mode']]))
  600. {
  601. return FALSE;
  602. }
  603. $params['mode'] = $this->_modes[$this->_driver][$params['mode']];
  604. }
  605. if (isset($params['hmac']) && $params['hmac'] === FALSE)
  606. {
  607. $params['hmac_digest'] = $params['hmac_key'] = NULL;
  608. }
  609. else
  610. {
  611. if ( ! isset($params['hmac_key']))
  612. {
  613. return FALSE;
  614. }
  615. elseif (isset($params['hmac_digest']))
  616. {
  617. $params['hmac_digest'] = strtolower($params['hmac_digest']);
  618. if ( ! isset($this->_digests[$params['hmac_digest']]))
  619. {
  620. return FALSE;
  621. }
  622. }
  623. else
  624. {
  625. $params['hmac_digest'] = 'sha512';
  626. }
  627. }
  628. $params = array(
  629. 'handle' => NULL,
  630. 'cipher' => $params['cipher'],
  631. 'mode' => $params['mode'],
  632. 'key' => $params['key'],
  633. 'base64' => isset($params['raw_data']) ? ! $params['raw_data'] : FALSE,
  634. 'hmac_digest' => $params['hmac_digest'],
  635. 'hmac_key' => $params['hmac_key']
  636. );
  637. $this->_cipher_alias($params['cipher']);
  638. $params['handle'] = ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)
  639. ? $this->{'_'.$this->_driver.'_get_handle'}($params['cipher'], $params['mode'])
  640. : $this->_handle;
  641. return $params;
  642. }
  643. // --------------------------------------------------------------------
  644. /**
  645. * Get MCrypt handle
  646. *
  647. * @param string $cipher Cipher name
  648. * @param string $mode Encryption mode
  649. * @return resource
  650. */
  651. protected function _mcrypt_get_handle($cipher, $mode)
  652. {
  653. return mcrypt_module_open($cipher, '', $mode, '');
  654. }
  655. // --------------------------------------------------------------------
  656. /**
  657. * Get OpenSSL handle
  658. *
  659. * @param string $cipher Cipher name
  660. * @param string $mode Encryption mode
  661. * @return string
  662. */
  663. protected function _openssl_get_handle($cipher, $mode)
  664. {
  665. // OpenSSL methods aren't suffixed with '-stream' for this mode
  666. return ($mode === 'stream')
  667. ? $cipher
  668. : $cipher.'-'.$mode;
  669. }
  670. // --------------------------------------------------------------------
  671. /**
  672. * Cipher alias
  673. *
  674. * Tries to translate cipher names between MCrypt and OpenSSL's "dialects".
  675. *
  676. * @param string $cipher Cipher name
  677. * @return void
  678. */
  679. protected function _cipher_alias(&$cipher)
  680. {
  681. static $dictionary;
  682. if (empty($dictionary))
  683. {
  684. $dictionary = array(
  685. 'mcrypt' => array(
  686. 'aes-128' => 'rijndael-128',
  687. 'aes-192' => 'rijndael-128',
  688. 'aes-256' => 'rijndael-128',
  689. 'des3-ede3' => 'tripledes',
  690. 'bf' => 'blowfish',
  691. 'cast5' => 'cast-128',
  692. 'rc4' => 'arcfour',
  693. 'rc4-40' => 'arcfour'
  694. ),
  695. 'openssl' => array(
  696. 'rijndael-128' => 'aes-128',
  697. 'tripledes' => 'des-ede3',
  698. 'blowfish' => 'bf',
  699. 'cast-128' => 'cast5',
  700. 'arcfour' => 'rc4-40',
  701. 'rc4' => 'rc4-40'
  702. )
  703. );
  704. // Notes:
  705. //
  706. // - Rijndael-128 is, at the same time all three of AES-128,
  707. // AES-192 and AES-256. The only difference between them is
  708. // the key size. Rijndael-192, Rijndael-256 on the other hand
  709. // also have different block sizes and are NOT AES-compatible.
  710. //
  711. // - Blowfish is said to be supporting key sizes between
  712. // 4 and 56 bytes, but it appears that between MCrypt and
  713. // OpenSSL, only those of 16 and more bytes are compatible.
  714. // Also, don't know what MCrypt's 'blowfish-compat' is.
  715. //
  716. // - CAST-128/CAST5 produces a longer cipher when encrypted via
  717. // OpenSSL, but (strangely enough) can be decrypted by either
  718. // extension anyway.
  719. // Also, it appears that OpenSSL uses 16 rounds regardless of
  720. // the key size, while RFC2144 says that for key sizes lower
  721. // than 11 bytes, only 12 rounds should be used. This makes
  722. // it portable only with keys of between 11 and 16 bytes.
  723. //
  724. // - RC4 (ARCFour) has a strange implementation under OpenSSL.
  725. // Its 'rc4-40' cipher method seems to work flawlessly, yet
  726. // there's another one, 'rc4' that only works with a 16-byte key.
  727. //
  728. // - DES is compatible, but doesn't need an alias.
  729. //
  730. // Other seemingly matching ciphers between MCrypt, OpenSSL:
  731. //
  732. // - RC2 is NOT compatible and only an obscure forum post
  733. // confirms that it is MCrypt's fault.
  734. }
  735. if (isset($dictionary[$this->_driver][$cipher]))
  736. {
  737. $cipher = $dictionary[$this->_driver][$cipher];
  738. }
  739. }
  740. // --------------------------------------------------------------------
  741. /**
  742. * HKDF
  743. *
  744. * @link https://tools.ietf.org/rfc/rfc5869.txt
  745. * @param $key Input key
  746. * @param $digest A SHA-2 hashing algorithm
  747. * @param $salt Optional salt
  748. * @param $length Output length (defaults to the selected digest size)
  749. * @param $info Optional context/application-specific info
  750. * @return string A pseudo-random key
  751. */
  752. public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')
  753. {
  754. if ( ! isset($this->_digests[$digest]))
  755. {
  756. return FALSE;
  757. }
  758. if (empty($length) OR ! is_int($length))
  759. {
  760. $length = $this->_digests[$digest];
  761. }
  762. elseif ($length > (255 * $this->_digests[$digest]))
  763. {
  764. return FALSE;
  765. }
  766. self::strlen($salt) OR $salt = str_repeat("\0", $this->_digests[$digest]);
  767. $prk = hash_hmac($digest, $key, $salt, TRUE);
  768. $key = '';
  769. for ($key_block = '', $block_index = 1; self::strlen($key) < $length; $block_index++)
  770. {
  771. $key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE);
  772. $key .= $key_block;
  773. }
  774. return self::substr($key, 0, $length);
  775. }
  776. // --------------------------------------------------------------------
  777. /**
  778. * __get() magic
  779. *
  780. * @param string $key Property name
  781. * @return mixed
  782. */
  783. public function __get($key)
  784. {
  785. // Because aliases
  786. if ($key === 'mode')
  787. {
  788. return array_search($this->_mode, $this->_modes[$this->_driver], TRUE);
  789. }
  790. elseif (in_array($key, array('cipher', 'driver', 'drivers', 'digests'), TRUE))
  791. {
  792. return $this->{'_'.$key};
  793. }
  794. return NULL;
  795. }
  796. // --------------------------------------------------------------------
  797. /**
  798. * Byte-safe strlen()
  799. *
  800. * @param string $str
  801. * @return int
  802. */
  803. protected static function strlen($str)
  804. {
  805. return (self::$func_overload)
  806. ? mb_strlen($str, '8bit')
  807. : strlen($str);
  808. }
  809. // --------------------------------------------------------------------
  810. /**
  811. * Byte-safe substr()
  812. *
  813. * @param string $str
  814. * @param int $start
  815. * @param int $length
  816. * @return string
  817. */
  818. protected static function substr($str, $start, $length = NULL)
  819. {
  820. if (self::$func_overload)
  821. {
  822. // mb_substr($str, $start, null, '8bit') returns an empty
  823. // string on PHP 5.3
  824. isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start);
  825. return mb_substr($str, $start, $length, '8bit');
  826. }
  827. return isset($length)
  828. ? substr($str, $start, $length)
  829. : substr($str, $start);
  830. }
  831. }