crypt.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.5
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2013 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. use \PHPSecLib\Crypt_AES;
  14. use \PHPSecLib\Crypt_Hash;
  15. class Crypt
  16. {
  17. /*
  18. * Crypto object used to encrypt/decrypt
  19. *
  20. * @var object
  21. */
  22. private static $crypter = null;
  23. /*
  24. * Hash object used to generate hashes
  25. *
  26. * @var object
  27. */
  28. private static $hasher = null;
  29. /*
  30. * Crypto configuration
  31. *
  32. * @var array
  33. */
  34. private static $config = array();
  35. /*
  36. * initialisation and auto configuration
  37. */
  38. public static function _init()
  39. {
  40. static::$crypter = new Crypt_AES();
  41. static::$hasher = new Crypt_Hash('sha256');
  42. // load the config
  43. \Config::load('crypt', true);
  44. static::$config = \Config::get('crypt', array ());
  45. // generate random crypto keys if we don't have them or they are incorrect length
  46. $update = false;
  47. foreach(array('crypto_key', 'crypto_iv', 'crypto_hmac') as $key)
  48. {
  49. if ( empty(static::$config[$key]) or (strlen(static::$config[$key]) % 4) != 0)
  50. {
  51. $crypto = '';
  52. for ($i = 0; $i < 8; $i++)
  53. {
  54. $crypto .= static::safe_b64encode(pack('n', mt_rand(0, 0xFFFF)));
  55. }
  56. static::$config[$key] = $crypto;
  57. $update = true;
  58. }
  59. }
  60. // update the config if needed
  61. if ($update === true)
  62. {
  63. try
  64. {
  65. \Config::save('crypt', static::$config);
  66. }
  67. catch (\FileAccessException $e)
  68. {
  69. // failed to write the config file, inform the user
  70. echo \View::forge('errors/crypt_keys', array(
  71. 'keys' => static::$config
  72. ));
  73. die();
  74. }
  75. }
  76. static::$crypter->enableContinuousBuffer();
  77. static::$hasher->setKey(static::safe_b64decode(static::$config['crypto_hmac']));
  78. }
  79. // --------------------------------------------------------------------
  80. /*
  81. * encrypt a string value, optionally with a custom key
  82. *
  83. * @param string value to encrypt
  84. * @param string optional custom key to be used for this encryption
  85. * @access public
  86. * @return string encrypted value
  87. */
  88. public static function encode($value, $key = false)
  89. {
  90. $key ? static::$crypter->setKey($key) : static::$crypter->setKey(static::safe_b64decode(static::$config['crypto_key']));
  91. static::$crypter->setIV(static::safe_b64decode(static::$config['crypto_iv']));
  92. $value = static::$crypter->encrypt($value);
  93. return static::safe_b64encode(static::add_hmac($value));
  94. }
  95. // --------------------------------------------------------------------
  96. /*
  97. * decrypt a string value, optionally with a custom key
  98. *
  99. * @param string value to decrypt
  100. * @param string optional custom key to be used for this encryption
  101. * @access public
  102. * @return string encrypted value
  103. */
  104. public static function decode($value, $key = false)
  105. {
  106. $key ? static::$crypter->setKey($key) : static::$crypter->setKey(static::safe_b64decode(static::$config['crypto_key']));
  107. static::$crypter->setIV(static::safe_b64decode(static::$config['crypto_iv']));
  108. $value = static::safe_b64decode($value);
  109. if ($value = static::validate_hmac($value))
  110. {
  111. return static::$crypter->decrypt($value);
  112. }
  113. else
  114. {
  115. return false;
  116. }
  117. }
  118. // --------------------------------------------------------------------
  119. private static function safe_b64encode($value)
  120. {
  121. $data = base64_encode($value);
  122. $data = str_replace(array('+','/','='), array('-','_',''), $data);
  123. return $data;
  124. }
  125. private static function safe_b64decode($value)
  126. {
  127. $data = str_replace(array('-','_'), array('+','/'), $value);
  128. $mod4 = strlen($data) % 4;
  129. if ($mod4)
  130. {
  131. $data .= substr('====', $mod4);
  132. }
  133. return base64_decode($data);
  134. }
  135. private static function add_hmac($value)
  136. {
  137. // calculate the hmac-sha256 hash of this value
  138. $hmac = static::safe_b64encode(static::$hasher->hash($value));
  139. // append it and return the hmac protected string
  140. return $value.$hmac;
  141. }
  142. private static function validate_hmac($value)
  143. {
  144. // strip the hmac-sha256 hash from the value
  145. $hmac = substr($value, strlen($value)-43);
  146. // and remove it from the value
  147. $value = substr($value, 0, strlen($value)-43);
  148. // only return the value if it wasn't tampered with
  149. return (static::secure_compare(static::safe_b64encode(static::$hasher->hash($value)), $hmac)) ? $value : false;
  150. }
  151. private static function secure_compare($a, $b)
  152. {
  153. // make sure we're only comparing equal length strings
  154. if (strlen($a) !== strlen($b))
  155. {
  156. return false;
  157. }
  158. // and that all comparisons take equal time
  159. $result = 0;
  160. for ($i = 0; $i < strlen($a); $i++)
  161. {
  162. $result |= ord($a[$i]) ^ ord($b[$i]);
  163. }
  164. return $result == 0;
  165. }
  166. }