PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Phpass/Hash.php

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