PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Phpass/Hash.php

http://github.com/rchouinard/phpass
PHP | 258 lines | 81 code | 21 blank | 156 comment | 10 complexity | b423f55c511879e190ca513f9e3989f9 MD5 | raw file
  1. <?php
  2. /**
  3. * PHP Password Library
  4. *
  5. * @package PHPass\Hashes
  6. * @category Cryptography
  7. * @author Ryan Chouinard <rchouinard at gmail.com>
  8. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  9. * @link https://github.com/rchouinard/phpass Project at GitHub
  10. */
  11. namespace Phpass;
  12. use Phpass\Hash\Adapter;
  13. use Phpass\Hash\Adapter\Bcrypt;
  14. use Phpass\Exception\InvalidArgumentException;
  15. use Phpass\Exception\RuntimeException;
  16. /**
  17. * Hash class
  18. *
  19. * Provides a simple API for working with the various hash adapters. If the
  20. * class is constructed with no arguments, it will construct a bcrypt
  21. * adapter with default settings for use internally.
  22. *
  23. * If an optional HMAC key is provided, password strings will be hashed using
  24. * the chosen HMAC algorithm and the supplied key before being passed to the
  25. * adapter. HMAC-SHA256 is used by default.
  26. *
  27. * <?php
  28. * // Just use the defaults (works well in most cases)
  29. * $phpassHash = new \Phpass\Hash;
  30. *
  31. * // Generate a password hash
  32. * $passwordHash = $phpassHash->hashPassword($password);
  33. *
  34. * // Check a password
  35. * if ($phpassHash->checkPassword($password, $passwordHash)) {
  36. * // Passwords match!
  37. * }
  38. *
  39. * @package PHPass\Hashes
  40. * @category Cryptography
  41. * @author Ryan Chouinard <rchouinard at gmail.com>
  42. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  43. * @link https://github.com/rchouinard/phpass Project at GitHub
  44. */
  45. class Hash
  46. {
  47. /**
  48. * Instance of the adapter to use for hashing strings.
  49. *
  50. * @var Adapter
  51. */
  52. protected $_adapter;
  53. /**
  54. * Name of selected hashing algorithm.
  55. *
  56. * See \hash_algos() for a list of supported algorithms.
  57. *
  58. * @var string
  59. */
  60. protected $_hmacAlgo = 'sha256';
  61. /**
  62. * Shared secret key used for generating the HMAC variant of the string.
  63. *
  64. * @var string
  65. */
  66. protected $_hmacKey;
  67. /**
  68. * Class constructor.
  69. *
  70. * Expects either an associative array of options, or an instance of a
  71. * class implementing the Adapter interface. If neither is given, or if the
  72. * 'adapter' option key is omitted, an instance of the Bcrypt adapter is
  73. * created internally by default.
  74. *
  75. * <?php
  76. * // Just use the defaults (works for most cases)
  77. * $phpassHash = new \Phpass\Hash;
  78. *
  79. * // Customize the adapter
  80. * $adapter = new \Phpass\Hash\Adapter\Pbkdf2(array (
  81. * 'iterationCountLog2' => 12 // 2^12 iterations
  82. * ));
  83. * $phpassHash = new \Phpass\Hash($adapter);
  84. *
  85. * // Customize the adapter as well as use additional HMAC hashing
  86. * $options = array (
  87. * 'adapter' => new \Phpass\Hash\Adapter\ExtDes,
  88. * 'hmacKey' => 'mys3cr3tk3y'
  89. * );
  90. * $phpassHash = new \Phpass\Hash($options);
  91. *
  92. * @param Array|Adapter $options
  93. * Either an associative array of options, or an instance of Adapter.
  94. * @return void
  95. * @throws InvalidArgumentException
  96. * An InvalidArgumentException is thrown if a value other than an Adapter
  97. * instance or options array is passed to the constructor.
  98. */
  99. public function __construct($options = array ())
  100. {
  101. $this->_adapter = new Bcrypt;
  102. if ($options instanceof Adapter) {
  103. $options = array ('adapter' => $options);
  104. }
  105. if (!is_array($options)) {
  106. throw new InvalidArgumentException('Expected an instance of Phpass\\Hash\\Adapter or an associative array of options.');
  107. }
  108. $this->setOptions($options);
  109. }
  110. /**
  111. * Set the adapter to use for hashing strings.
  112. *
  113. * @param Adapter $adapter
  114. * An instance of a class implementing the Adapter interface.
  115. * @return Hash
  116. */
  117. public function setAdapter(Adapter $adapter)
  118. {
  119. $this->_adapter = $adapter;
  120. return $this;
  121. }
  122. /**
  123. * Retrieve the adapter used for hashing strings.
  124. *
  125. * @return Adapter
  126. */
  127. public function getAdapter()
  128. {
  129. return $this->_adapter;
  130. }
  131. /**
  132. * Set options.
  133. *
  134. * <dl>
  135. * <dt>adapter</dt>
  136. * <dd>Instance of a class implementing the Adapter interface.</dd>
  137. * <dt>hmacKey</dt>
  138. * <dd>Shared secret key used for generating the HMAC variant of the
  139. * string.</dd>
  140. * <dt>hmacAlgo</dt>
  141. * <dd>Name of selected hashing algorithm. See \hmac_algos() for a list
  142. * of supported algorithms.</dd>
  143. * </dl>
  144. *
  145. * @param Array $options
  146. * An associative array of options.
  147. * @return Hash
  148. * @throws RuntimeException
  149. * A RuntimeException is thrown if HMAC options are passed in, but the
  150. * hash extension is not loaded.
  151. * @throws InvalidArgumentException
  152. * An InvalidArgumentException is thrown if a value does not match what
  153. * is expected for the option key.
  154. */
  155. public function setOptions(Array $options)
  156. {
  157. $options = array_change_key_case($options, CASE_LOWER);
  158. if (array_key_exists('hmackey', $options) || array_key_exists('hmacalgo', $options)) {
  159. if (!extension_loaded('hash')) {
  160. throw new RuntimeException("Required extension 'hash' is not loaded.");
  161. }
  162. }
  163. foreach ($options as $option => $value) {
  164. switch ($option) {
  165. case 'adapter':
  166. if (!$value instanceof Adapter) {
  167. throw new InvalidArgumentException("Value of key 'adapter' must be an instance of Phpass\\Hash\\Adapter.");
  168. }
  169. $this->setAdapter($value);
  170. break;
  171. case 'hmackey':
  172. $this->_hmacKey = (string) $value;
  173. break;
  174. case 'hmacalgo':
  175. if (!in_array($value, hash_algos())) {
  176. throw new InvalidArgumentException("Given hash algorithm '${value}' is not supported by this system.");
  177. }
  178. $this->_hmacAlgo = $value;
  179. break;
  180. default:
  181. break;
  182. }
  183. }
  184. return $this;
  185. }
  186. /**
  187. * Check if a string matches a given hash value.
  188. *
  189. * @param string $password
  190. * The string to check.
  191. * @param string $storedHash
  192. * The hash string to check against.
  193. * @return boolean
  194. * Returns true if the string matches the hash string, and false
  195. * otherwise.
  196. */
  197. public function checkPassword($password, $storedHash)
  198. {
  199. $hash = $this->_crypt($password, $storedHash);
  200. return ($hash == $storedHash);
  201. }
  202. /**
  203. * Return a hashed string using the configured adapter.
  204. *
  205. * @param string $password
  206. * The string to be hashed.
  207. * @return string
  208. * Returns the hashed string.
  209. */
  210. public function hashPassword($password)
  211. {
  212. return $this->_crypt($password);
  213. }
  214. /**
  215. * Return a hashed string, optionally using a pre-calculated salt.
  216. *
  217. * If Hash::$_hmacKey is set, this method will generate the HMAC hash of
  218. * the password string before passing the value to the adapter.
  219. *
  220. * @param string $password
  221. * The string to be hashed.
  222. * @param string $salt
  223. * An optional salt string to base the hashing on. If not provided, the
  224. * adapter will generate a new secure salt value.
  225. * @return string
  226. * Returns the hashed string.
  227. */
  228. protected function _crypt($password, $salt = null)
  229. {
  230. if (isset($this->_hmacKey)) {
  231. $password = hash_hmac($this->_hmacAlgo, $password, $this->_hmacKey);
  232. }
  233. $adapter = $this->getAdapter();
  234. $hash = $adapter->crypt($password, $salt);
  235. return $hash;
  236. }
  237. }