PageRenderTime 184ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/yii/framework/base/CSecurityManager.php

https://bitbucket.org/zurmo/zurmo/
PHP | 619 lines | 319 code | 52 blank | 248 comment | 50 complexity | 46d4661a4ec39a183b87213d9007e919 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, GPL-2.0, LGPL-3.0, LGPL-2.1, BSD-2-Clause
  1. <?php
  2. /**
  3. * This file contains classes implementing security manager feature.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright 2008-2013 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CSecurityManager provides private keys, hashing and encryption functions.
  12. *
  13. * CSecurityManager is used by Yii components and applications for security-related purpose.
  14. * For example, it is used in cookie validation feature to prevent cookie data
  15. * from being tampered.
  16. *
  17. * CSecurityManager is mainly used to protect data from being tampered and viewed.
  18. * It can generate HMAC and encrypt the data. The private key used to generate HMAC
  19. * is set by {@link setValidationKey ValidationKey}. The key used to encrypt data is
  20. * specified by {@link setEncryptionKey EncryptionKey}. If the above keys are not
  21. * explicitly set, random keys will be generated and used.
  22. *
  23. * To protected data with HMAC, call {@link hashData()}; and to check if the data
  24. * is tampered, call {@link validateData()}, which will return the real data if
  25. * it is not tampered. The algorithm used to generated HMAC is specified by
  26. * {@link validation}.
  27. *
  28. * To encrypt and decrypt data, call {@link encrypt()} and {@link decrypt()}
  29. * respectively, which uses 3DES encryption algorithm. Note, the PHP Mcrypt
  30. * extension must be installed and loaded.
  31. *
  32. * CSecurityManager is a core application component that can be accessed via
  33. * {@link CApplication::getSecurityManager()}.
  34. *
  35. * @property string $validationKey The private key used to generate HMAC.
  36. * If the key is not explicitly set, a random one is generated and returned.
  37. * @property string $encryptionKey The private key used to encrypt/decrypt data.
  38. * If the key is not explicitly set, a random one is generated and returned.
  39. * @property string $validation
  40. *
  41. * @author Qiang Xue <qiang.xue@gmail.com>
  42. * @package system.base
  43. * @since 1.0
  44. */
  45. class CSecurityManager extends CApplicationComponent
  46. {
  47. const STATE_VALIDATION_KEY='Yii.CSecurityManager.validationkey';
  48. const STATE_ENCRYPTION_KEY='Yii.CSecurityManager.encryptionkey';
  49. /**
  50. * @var array known minimum lengths per encryption algorithm
  51. */
  52. protected static $encryptionKeyMinimumLengths=array(
  53. 'blowfish'=>4,
  54. 'arcfour'=>5,
  55. 'rc2'=>5,
  56. );
  57. /**
  58. * @var boolean if encryption key should be validated
  59. * @deprecated
  60. */
  61. public $validateEncryptionKey=true;
  62. /**
  63. * @var string the name of the hashing algorithm to be used by {@link computeHMAC}.
  64. * See {@link http://php.net/manual/en/function.hash-algos.php hash-algos} for the list of possible
  65. * hash algorithms. Note that if you are using PHP 5.1.1 or below, you can only use 'sha1' or 'md5'.
  66. *
  67. * Defaults to 'sha1', meaning using SHA1 hash algorithm.
  68. * @since 1.1.3
  69. */
  70. public $hashAlgorithm='sha1';
  71. /**
  72. * @var mixed the name of the crypt algorithm to be used by {@link encrypt} and {@link decrypt}.
  73. * This will be passed as the first parameter to {@link http://php.net/manual/en/function.mcrypt-module-open.php mcrypt_module_open}.
  74. *
  75. * This property can also be configured as an array. In this case, the array elements will be passed in order
  76. * as parameters to mcrypt_module_open. For example, <code>array('rijndael-128', '', 'ofb', '')</code>.
  77. *
  78. * Defaults to AES
  79. *
  80. * Note: MCRYPT_RIJNDAEL_192 and MCRYPT_RIJNDAEL_256 are *not* AES-192 and AES-256. The numbers of the MCRYPT_RIJNDAEL
  81. * constants refer to the block size, whereas the numbers of the AES variants refer to the key length. AES is Rijndael
  82. * with a block size of 128 bits and a key length of 128 bits, 192 bits or 256 bits. So to use AES in Mcrypt, you need
  83. * MCRYPT_RIJNDAEL_128 and a key with 16 bytes (AES-128), 24 bytes (AES-192) or 32 bytes (AES-256). The other two
  84. * Rijndael variants in Mcrypt should be avoided, because they're not standardized and have been analyzed much less
  85. * than AES.
  86. *
  87. * @since 1.1.3
  88. */
  89. public $cryptAlgorithm='des';
  90. private $_validationKey;
  91. private $_encryptionKey;
  92. private $_mbstring;
  93. public function init()
  94. {
  95. parent::init();
  96. $this->_mbstring=extension_loaded('mbstring');
  97. }
  98. /**
  99. * @return string a randomly generated private key.
  100. * @deprecated in favor of {@link generateRandomString()} since 1.1.14. Never use this method.
  101. */
  102. protected function generateRandomKey()
  103. {
  104. return $this->generateRandomString(32);
  105. }
  106. /**
  107. * @return string the private key used to generate HMAC.
  108. * If the key is not explicitly set, a random one is generated and returned.
  109. * @throws CException in case random string cannot be generated.
  110. */
  111. public function getValidationKey()
  112. {
  113. if($this->_validationKey!==null)
  114. return $this->_validationKey;
  115. else
  116. {
  117. if(($key=Yii::app()->getGlobalState(self::STATE_VALIDATION_KEY))!==null)
  118. $this->setValidationKey($key);
  119. else
  120. {
  121. if(($key=$this->generateRandomString(32,true))===false)
  122. if(($key=$this->generateRandomString(32,false))===false)
  123. throw new CException(Yii::t('yii',
  124. 'CSecurityManager::generateRandomString() cannot generate random string in the current environment.'));
  125. $this->setValidationKey($key);
  126. Yii::app()->setGlobalState(self::STATE_VALIDATION_KEY,$key);
  127. }
  128. return $this->_validationKey;
  129. }
  130. }
  131. /**
  132. * @param string $value the key used to generate HMAC
  133. * @throws CException if the key is empty
  134. */
  135. public function setValidationKey($value)
  136. {
  137. if(!empty($value))
  138. $this->_validationKey=$value;
  139. else
  140. throw new CException(Yii::t('yii','CSecurityManager.validationKey cannot be empty.'));
  141. }
  142. /**
  143. * @return string the private key used to encrypt/decrypt data.
  144. * If the key is not explicitly set, a random one is generated and returned.
  145. * @throws CException in case random string cannot be generated.
  146. */
  147. public function getEncryptionKey()
  148. {
  149. if($this->_encryptionKey!==null)
  150. return $this->_encryptionKey;
  151. else
  152. {
  153. if(($key=Yii::app()->getGlobalState(self::STATE_ENCRYPTION_KEY))!==null)
  154. $this->setEncryptionKey($key);
  155. else
  156. {
  157. if(($key=$this->generateRandomString(32,true))===false)
  158. if(($key=$this->generateRandomString(32,false))===false)
  159. throw new CException(Yii::t('yii',
  160. 'CSecurityManager::generateRandomString() cannot generate random string in the current environment.'));
  161. $this->setEncryptionKey($key);
  162. Yii::app()->setGlobalState(self::STATE_ENCRYPTION_KEY,$key);
  163. }
  164. return $this->_encryptionKey;
  165. }
  166. }
  167. /**
  168. * @param string $value the key used to encrypt/decrypt data.
  169. * @throws CException if the key is empty
  170. */
  171. public function setEncryptionKey($value)
  172. {
  173. $this->validateEncryptionKey($value);
  174. $this->_encryptionKey=$value;
  175. }
  176. /**
  177. * This method has been deprecated since version 1.1.3.
  178. * Please use {@link hashAlgorithm} instead.
  179. * @return string -
  180. * @deprecated
  181. */
  182. public function getValidation()
  183. {
  184. return $this->hashAlgorithm;
  185. }
  186. /**
  187. * This method has been deprecated since version 1.1.3.
  188. * Please use {@link hashAlgorithm} instead.
  189. * @param string $value -
  190. * @deprecated
  191. */
  192. public function setValidation($value)
  193. {
  194. $this->hashAlgorithm=$value;
  195. }
  196. /**
  197. * Encrypts data.
  198. * @param string $data data to be encrypted.
  199. * @param string $key the decryption key. This defaults to null, meaning using {@link getEncryptionKey EncryptionKey}.
  200. * @return string the encrypted data
  201. * @throws CException if PHP Mcrypt extension is not loaded or key is invalid
  202. */
  203. public function encrypt($data,$key=null)
  204. {
  205. /*if($key===null)
  206. $key=$this->getEncryptionKey();
  207. $this->validateEncryptionKey($key);*/
  208. $module=$this->openCryptModule();
  209. $key=$this->substr($key===null ? md5($this->getEncryptionKey()) : $key,0,mcrypt_enc_get_key_size($module));
  210. srand();
  211. $iv=mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
  212. mcrypt_generic_init($module,$key,$iv);
  213. $encrypted=$iv.mcrypt_generic($module,$data);
  214. mcrypt_generic_deinit($module);
  215. mcrypt_module_close($module);
  216. return $encrypted;
  217. }
  218. /**
  219. * Decrypts data
  220. * @param string $data data to be decrypted.
  221. * @param string $key the decryption key. This defaults to null, meaning using {@link getEncryptionKey EncryptionKey}.
  222. * @return string the decrypted data
  223. * @throws CException if PHP Mcrypt extension is not loaded or key is invalid
  224. */
  225. public function decrypt($data,$key=null)
  226. {
  227. /*if($key===null)
  228. $key=$this->getEncryptionKey();
  229. $this->validateEncryptionKey($key);*/
  230. $module=$this->openCryptModule();
  231. $key=$this->substr($key===null ? md5($this->getEncryptionKey()) : $key,0,mcrypt_enc_get_key_size($module));
  232. $ivSize=mcrypt_enc_get_iv_size($module);
  233. $iv=$this->substr($data,0,$ivSize);
  234. mcrypt_generic_init($module,$key,$iv);
  235. $decrypted=mdecrypt_generic($module,$this->substr($data,$ivSize,$this->strlen($data)));
  236. mcrypt_generic_deinit($module);
  237. mcrypt_module_close($module);
  238. return rtrim($decrypted,"\0");
  239. }
  240. /**
  241. * Opens the mcrypt module with the configuration specified in {@link cryptAlgorithm}.
  242. * @throws CException if failed to initialize the mcrypt module or PHP mcrypt extension
  243. * @return resource the mycrypt module handle.
  244. * @since 1.1.3
  245. */
  246. protected function openCryptModule()
  247. {
  248. if(extension_loaded('mcrypt'))
  249. {
  250. if(is_array($this->cryptAlgorithm))
  251. $module=@call_user_func_array('mcrypt_module_open',$this->cryptAlgorithm);
  252. else
  253. $module=@mcrypt_module_open($this->cryptAlgorithm,'', MCRYPT_MODE_CBC,'');
  254. if($module===false)
  255. throw new CException(Yii::t('yii','Failed to initialize the mcrypt module.'));
  256. return $module;
  257. }
  258. else
  259. throw new CException(Yii::t('yii','CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.'));
  260. }
  261. /**
  262. * Prefixes data with an HMAC.
  263. * @param string $data data to be hashed.
  264. * @param string $key the private key to be used for generating HMAC. Defaults to null, meaning using {@link validationKey}.
  265. * @return string data prefixed with HMAC
  266. */
  267. public function hashData($data,$key=null)
  268. {
  269. return $this->computeHMAC($data,$key).$data;
  270. }
  271. /**
  272. * Validates if data is tampered.
  273. * @param string $data data to be validated. The data must be previously
  274. * generated using {@link hashData()}.
  275. * @param string $key the private key to be used for generating HMAC. Defaults to null, meaning using {@link validationKey}.
  276. * @return string the real data with HMAC stripped off. False if the data
  277. * is tampered.
  278. */
  279. public function validateData($data,$key=null)
  280. {
  281. if (!is_string($data))
  282. return false;
  283. $len=$this->strlen($this->computeHMAC('test'));
  284. if($this->strlen($data)>=$len)
  285. {
  286. $hmac=$this->substr($data,0,$len);
  287. $data2=$this->substr($data,$len,$this->strlen($data));
  288. return $this->compareString($hmac,$this->computeHMAC($data2,$key))?$data2:false;
  289. }
  290. else
  291. return false;
  292. }
  293. /**
  294. * Computes the HMAC for the data with {@link getValidationKey validationKey}. This method has been made public
  295. * since 1.1.14.
  296. * @param string $data data to be generated HMAC.
  297. * @param string|null $key the private key to be used for generating HMAC. Defaults to null, meaning using
  298. * {@link validationKey} value.
  299. * @param string|null $hashAlgorithm the name of the hashing algorithm to be used.
  300. * See {@link http://php.net/manual/en/function.hash-algos.php hash-algos} for the list of possible
  301. * hash algorithms. Note that if you are using PHP 5.1.1 or below, you can only use 'sha1' or 'md5'.
  302. * Defaults to null, meaning using {@link hashAlgorithm} value.
  303. * @return string the HMAC for the data.
  304. * @throws CException on unsupported hash algorithm given.
  305. */
  306. public function computeHMAC($data,$key=null,$hashAlgorithm=null)
  307. {
  308. if($key===null)
  309. $key=$this->getValidationKey();
  310. if($hashAlgorithm===null)
  311. $hashAlgorithm=$this->hashAlgorithm;
  312. if(function_exists('hash_hmac'))
  313. return hash_hmac($hashAlgorithm,$data,$key);
  314. if(0===strcasecmp($hashAlgorithm,'sha1'))
  315. {
  316. $pack='H40';
  317. $func='sha1';
  318. }
  319. elseif(0===strcasecmp($hashAlgorithm,'md5'))
  320. {
  321. $pack='H32';
  322. $func='md5';
  323. }
  324. else
  325. {
  326. throw new CException(Yii::t('yii','Only SHA1 and MD5 hashing algorithms are supported when using PHP 5.1.1 or below.'));
  327. }
  328. if($this->strlen($key)>64)
  329. $key=pack($pack,$func($key));
  330. if($this->strlen($key)<64)
  331. $key=str_pad($key,64,chr(0));
  332. $key=$this->substr($key,0,64);
  333. return $func((str_repeat(chr(0x5C), 64) ^ $key) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ $key) . $data)));
  334. }
  335. /**
  336. * Generate a random ASCII string. Generates only [0-9a-zA-z_~] characters which are all
  337. * transparent in raw URL encoding.
  338. * @param integer $length length of the generated string in characters.
  339. * @param boolean $cryptographicallyStrong set this to require cryptographically strong randomness.
  340. * @return string|boolean random string or false in case it cannot be generated.
  341. * @since 1.1.14
  342. */
  343. public function generateRandomString($length,$cryptographicallyStrong=true)
  344. {
  345. if(($randomBytes=$this->generateRandomBytes($length+2,$cryptographicallyStrong))!==false)
  346. return strtr($this->substr(base64_encode($randomBytes),0,$length),array('+'=>'_','/'=>'~'));
  347. return false;
  348. }
  349. /**
  350. * Generates a string of random bytes.
  351. * @param integer $length number of random bytes to be generated.
  352. * @param boolean $cryptographicallyStrong whether to fail if a cryptographically strong
  353. * result cannot be generated. The method attempts to read from a cryptographically strong
  354. * pseudorandom number generator (CS-PRNG), see
  355. * {@link https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Requirements Wikipedia}.
  356. * However, in some runtime environments, PHP has no access to a CS-PRNG, in which case
  357. * the method returns false if $cryptographicallyStrong is true. When $cryptographicallyStrong is false,
  358. * the method always returns a pseudorandom result but may fall back to using {@link generatePseudoRandomBlock}.
  359. * This method does not guarantee that entropy, from sources external to the CS-PRNG, was mixed into
  360. * the CS-PRNG state between each successive call. The caller can therefore expect non-blocking
  361. * behavior, unlike, for example, reading from /dev/random on Linux, see
  362. * {@link http://eprint.iacr.org/2006/086.pdf Gutterman et al 2006}.
  363. * @return boolean|string generated random binary string or false on failure.
  364. * @since 1.1.14
  365. */
  366. public function generateRandomBytes($length,$cryptographicallyStrong=true)
  367. {
  368. $bytes='';
  369. if(function_exists('openssl_random_pseudo_bytes'))
  370. {
  371. $bytes=openssl_random_pseudo_bytes($length,$strong);
  372. if($this->strlen($bytes)>=$length && ($strong || !$cryptographicallyStrong))
  373. return $this->substr($bytes,0,$length);
  374. }
  375. if(function_exists('mcrypt_create_iv') &&
  376. ($bytes=mcrypt_create_iv($length, MCRYPT_DEV_URANDOM))!==false &&
  377. $this->strlen($bytes)>=$length)
  378. {
  379. return $this->substr($bytes,0,$length);
  380. }
  381. if(($file=@fopen('/dev/urandom','rb'))!==false &&
  382. ($bytes=@fread($file,$length))!==false &&
  383. (fclose($file) || true) &&
  384. $this->strlen($bytes)>=$length)
  385. {
  386. return $this->substr($bytes,0,$length);
  387. }
  388. $i=0;
  389. while($this->strlen($bytes)<$length &&
  390. ($byte=$this->generateSessionRandomBlock())!==false &&
  391. ++$i<3)
  392. {
  393. $bytes.=$byte;
  394. }
  395. if($this->strlen($bytes)>=$length)
  396. return $this->substr($bytes,0,$length);
  397. if ($cryptographicallyStrong)
  398. return false;
  399. while($this->strlen($bytes)<$length)
  400. $bytes.=$this->generatePseudoRandomBlock();
  401. return $this->substr($bytes,0,$length);
  402. }
  403. /**
  404. * Generate a pseudo random block of data using several sources. On some systems this may be a bit
  405. * better than PHP's {@link mt_rand} built-in function, which is not really random.
  406. * @return string of 64 pseudo random bytes.
  407. * @since 1.1.14
  408. */
  409. public function generatePseudoRandomBlock()
  410. {
  411. $bytes='';
  412. if (function_exists('openssl_random_pseudo_bytes')
  413. && ($bytes=openssl_random_pseudo_bytes(512))!==false
  414. && $this->strlen($bytes)>=512)
  415. {
  416. return $this->substr($bytes,0,512);
  417. }
  418. for($i=0;$i<32;++$i)
  419. $bytes.=pack('S',mt_rand(0,0xffff));
  420. // On UNIX and UNIX-like operating systems the numerical values in `ps`, `uptime` and `iostat`
  421. // ought to be fairly unpredictable. Gather the non-zero digits from those.
  422. foreach(array('ps','uptime','iostat') as $command) {
  423. @exec($command,$commandResult,$retVal);
  424. if(is_array($commandResult) && !empty($commandResult) && $retVal==0)
  425. $bytes.=preg_replace('/[^1-9]/','',implode('',$commandResult));
  426. }
  427. // Gather the current time's microsecond part. Note: this is only a source of entropy on
  428. // the first call! If multiple calls are made, the entropy is only as much as the
  429. // randomness in the time between calls.
  430. $bytes.=$this->substr(microtime(),2,6);
  431. // Concatenate everything gathered, mix it with sha512. hash() is part of PHP core and
  432. // enabled by default but it can be disabled at compile time but we ignore that possibility here.
  433. return hash('sha512',$bytes,true);
  434. }
  435. /**
  436. * Get random bytes from the system entropy source via PHP session manager.
  437. * @return boolean|string 20-byte random binary string or false on error.
  438. * @since 1.1.14
  439. */
  440. public function generateSessionRandomBlock()
  441. {
  442. ini_set('session.entropy_length',20);
  443. if(ini_get('session.entropy_length')!=20)
  444. return false;
  445. // These calls are (supposed to be, according to PHP manual) safe even if
  446. // there is already an active session for the calling script.
  447. @session_start();
  448. @session_regenerate_id();
  449. $bytes=session_id();
  450. if(!$bytes)
  451. return false;
  452. // $bytes has 20 bytes of entropy but the session manager converts the binary
  453. // random bytes into something readable. We have to convert that back.
  454. // SHA-1 should do it without losing entropy.
  455. return sha1($bytes,true);
  456. }
  457. /**
  458. * Returns the length of the given string.
  459. * If available uses the multibyte string function mb_strlen.
  460. * @param string $string the string being measured for length
  461. * @return integer the length of the string
  462. */
  463. private function strlen($string)
  464. {
  465. return $this->_mbstring ? mb_strlen($string,'8bit') : strlen($string);
  466. }
  467. /**
  468. * Returns the portion of string specified by the start and length parameters.
  469. * If available uses the multibyte string function mb_substr
  470. * @param string $string the input string. Must be one character or longer.
  471. * @param integer $start the starting position
  472. * @param integer $length the desired portion length
  473. * @return string the extracted part of string, or FALSE on failure or an empty string.
  474. */
  475. private function substr($string,$start,$length)
  476. {
  477. return $this->_mbstring ? mb_substr($string,$start,$length,'8bit') : substr($string,$start,$length);
  478. }
  479. /**
  480. * Checks if a key is valid for {@link cryptAlgorithm}.
  481. * @param string $key the key to check
  482. * @return boolean the validation result
  483. * @throws CException if the supported key lengths of the cipher are unknown
  484. */
  485. protected function validateEncryptionKey($key)
  486. {
  487. if(is_string($key))
  488. {
  489. $cryptAlgorithm = is_array($this->cryptAlgorithm) ? $this->cryptAlgorithm[0] : $this->cryptAlgorithm;
  490. $supportedKeyLengths=mcrypt_module_get_supported_key_sizes($cryptAlgorithm);
  491. if($supportedKeyLengths)
  492. {
  493. if(!in_array($this->strlen($key),$supportedKeyLengths)) {
  494. throw new CException(Yii::t('yii','Encryption key length can be {keyLengths}.',array('{keyLengths}'=>implode(',',$supportedKeyLengths))));
  495. }
  496. }
  497. elseif(isset(self::$encryptionKeyMinimumLengths[$cryptAlgorithm]))
  498. {
  499. $minLength=self::$encryptionKeyMinimumLengths[$cryptAlgorithm];
  500. $maxLength=mcrypt_module_get_algo_key_size($cryptAlgorithm);
  501. if($this->strlen($key)<$minLength || $this->strlen($key)>$maxLength)
  502. throw new CException(Yii::t('yii','Encryption key length must be between {minLength} and {maxLength}.',array('{minLength}'=>$minLength,'{maxLength}'=>$maxLength)));
  503. }
  504. else
  505. throw new CException(Yii::t('yii','Failed to validate key. Supported key lengths of cipher not known.'));
  506. }
  507. else
  508. throw new CException(Yii::t('yii','Encryption key should be a string.'));
  509. }
  510. /**
  511. * Decrypts legacy ciphertext which was produced by the old, broken implementation of encrypt().
  512. * @deprecated use only to convert data encrypted prior to 1.1.16
  513. * @param string $data data to be decrypted.
  514. * @param string $key the decryption key. This defaults to null, meaning the key should be loaded from persistent storage.
  515. * @param string|array $cipher the algorithm to be used
  516. * @return string the decrypted data
  517. * @throws CException if PHP Mcrypt extension is not loaded
  518. * @throws CException if the key is missing
  519. */
  520. public function legacyDecrypt($data,$key=null,$cipher='des')
  521. {
  522. if (!$key)
  523. {
  524. $key=Yii::app()->getGlobalState(self::STATE_ENCRYPTION_KEY);
  525. if(!$key)
  526. throw new CException(Yii::t('yii','No encryption key specified.'));
  527. $key = md5($key);
  528. }
  529. if(extension_loaded('mcrypt'))
  530. {
  531. if(is_array($cipher))
  532. $module=@call_user_func_array('mcrypt_module_open',$cipher);
  533. else
  534. $module=@mcrypt_module_open($cipher,'', MCRYPT_MODE_CBC,'');
  535. if($module===false)
  536. throw new CException(Yii::t('yii','Failed to initialize the mcrypt module.'));
  537. }
  538. else
  539. throw new CException(Yii::t('yii','CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.'));
  540. $derivedKey=$this->substr($key,0,mcrypt_enc_get_key_size($module));
  541. $ivSize=mcrypt_enc_get_iv_size($module);
  542. $iv=$this->substr($data,0,$ivSize);
  543. mcrypt_generic_init($module,$derivedKey,$iv);
  544. $decrypted=mdecrypt_generic($module,$this->substr($data,$ivSize,$this->strlen($data)));
  545. mcrypt_generic_deinit($module);
  546. mcrypt_module_close($module);
  547. return rtrim($decrypted,"\0");
  548. }
  549. /**
  550. * Performs string comparison using timing attack resistant approach.
  551. * @see http://codereview.stackexchange.com/questions/13512
  552. * @param string $expected string to compare.
  553. * @param string $actual user-supplied string.
  554. * @return boolean whether strings are equal.
  555. */
  556. public function compareString($expected,$actual)
  557. {
  558. $expected.="\0";
  559. $actual.="\0";
  560. $expectedLength=$this->strlen($expected);
  561. $actualLength=$this->strlen($actual);
  562. $diff=$expectedLength-$actualLength;
  563. for($i=0;$i<$actualLength;$i++)
  564. $diff|=(ord($actual[$i])^ord($expected[$i%$expectedLength]));
  565. return $diff===0;
  566. }
  567. }