Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • Net
  • None
  • PHP
  • system
    • base
    • caching
      • dependencies
    • collections
    • console
    • db
      • ar
      • schema
        • cubrid
        • mssql
        • mysql
        • oci
        • pgsql
        • sqlite
    • i18n
      • gettext
    • logging
    • test
    • utils
    • validators
    • web
      • actions
      • auth
      • filters
      • form
      • helpers
      • renderers
      • services
      • widgets
        • captcha
        • pagers
  • Text
    • Highlighter
  • zii
    • behaviors
    • widgets
      • grid
      • jui

Classes

  • BaseDocsMassAction
  • CApplication
  • CApplicationComponent
  • CBehavior
  • CComponent
  • CEnumerable
  • CErrorEvent
  • CErrorHandler
  • CEvent
  • CExceptionEvent
  • CModel
  • CModelBehavior
  • CModelEvent
  • CModule
  • CommonFieldsBehavior
  • CSecurityManager
  • CStatePersister
  • Expression
  • MassAction
  • MassAddToList
  • MassCompleteAction
  • MassMoveFileSysObjToFolder
  • MassRemoveFromList
  • MassRenameFileSysObj
  • MassUncompleteAction
  • MobileRecentItems
  • ModulePanelItem
  • NewListFromSelection
  • PanelItem
  • QuickCRUDBehavior
  • RecentItemPanelItem
  • ServiceRoutingBehavior
  • SettingsPanelItem
  • X2AddressBehavior
  • X2AuthCache
  • X2BaseListViewBehavior

Interfaces

  • IAction
  • IApplicationComponent
  • IAuthManager
  • IBehavior
  • IFilter
  • IStatePersister
  • IUserIdentity
  • IViewRenderer
  • IWebServiceProvider
  • IWebUser

Exceptions

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