PageRenderTime 66ms CodeModel.GetById 40ms RepoModel.GetById 1ms app.codeStats 0ms

/monica/vendor/zendframework/zendframework/library/Zend/Crypt/Password/Apache.php

https://bitbucket.org/alexandretaz/maniac_divers
PHP | 298 lines | 185 code | 23 blank | 90 comment | 28 complexity | d5072d2c1883f82b42eff112a5fe0cf2 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Crypt\Password;
  10. use Traversable;
  11. use Zend\Math\Rand;
  12. use Zend\Stdlib\ArrayUtils;
  13. /**
  14. * Apache password authentication
  15. *
  16. * @see http://httpd.apache.org/docs/2.2/misc/password_encryptions.html
  17. */
  18. class Apache implements PasswordInterface
  19. {
  20. CONST BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  21. CONST ALPHA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  22. /**
  23. * @var array
  24. */
  25. protected $supportedFormat = array(
  26. 'crypt',
  27. 'sha1',
  28. 'md5',
  29. 'digest',
  30. );
  31. /**
  32. * @var string
  33. */
  34. protected $format;
  35. /**
  36. * @var string AuthName (realm) for digest authentication
  37. */
  38. protected $authName;
  39. /**
  40. * @var string UserName
  41. */
  42. protected $userName;
  43. /**
  44. * Constructor
  45. *
  46. * @param array|Traversable $options
  47. * @throws Exception\InvalidArgumentException
  48. */
  49. public function __construct($options = array())
  50. {
  51. if (empty($options)) {
  52. return;
  53. }
  54. if (!is_array($options) && !$options instanceof Traversable) {
  55. throw new Exception\InvalidArgumentException(
  56. 'The options parameter must be an array or a Traversable'
  57. );
  58. }
  59. foreach ($options as $key => $value) {
  60. switch (strtolower($key)) {
  61. case 'format':
  62. $this->setFormat($value);
  63. break;
  64. case 'authname':
  65. $this->setAuthName($value);
  66. break;
  67. case 'username':
  68. $this->setUserName($value);
  69. break;
  70. }
  71. }
  72. }
  73. /**
  74. * Generate the hash of a password
  75. *
  76. * @param string $password
  77. * @throws Exception\RuntimeException
  78. * @return string
  79. */
  80. public function create($password)
  81. {
  82. if (empty($this->format)) {
  83. throw new Exception\RuntimeException(
  84. 'You must specify a password format'
  85. );
  86. }
  87. switch ($this->format) {
  88. case 'crypt' :
  89. $hash = crypt($password, Rand::getString(2, self::ALPHA64));
  90. break;
  91. case 'sha1' :
  92. $hash = '{SHA}' . base64_encode(sha1($password, true));
  93. break;
  94. case 'md5' :
  95. $hash = $this->apr1Md5($password);
  96. break;
  97. case 'digest':
  98. if (empty($this->userName) || empty($this->authName)) {
  99. throw new Exception\RuntimeException(
  100. 'You must specify UserName and AuthName (realm) to generate the digest'
  101. );
  102. }
  103. $hash = md5($this->userName . ':' . $this->authName . ':' .$password);
  104. break;
  105. }
  106. return $hash;
  107. }
  108. /**
  109. * Verify if a password is correct against an hash value
  110. *
  111. * @param string $password
  112. * @param string $hash
  113. * @return boolean
  114. */
  115. public function verify($password, $hash)
  116. {
  117. if (substr($hash, 0, 5) === '{SHA}') {
  118. $hash2 = '{SHA}' . base64_encode(sha1($password, true));
  119. return ($hash === $hash2);
  120. }
  121. if (substr($hash, 0, 6) === '$apr1$') {
  122. $token = explode('$', $hash);
  123. if (empty($token[2])) {
  124. throw new Exception\InvalidArgumentException(
  125. 'The APR1 password format is not valid'
  126. );
  127. }
  128. $hash2 = $this->apr1Md5($password, $token[2]);
  129. return ($hash === $hash2);
  130. }
  131. if (strlen($hash) > 13) { // digest
  132. if (empty($this->userName) || empty($this->authName)) {
  133. throw new Exception\RuntimeException(
  134. 'You must specify UserName and AuthName (realm) to verify the digest'
  135. );
  136. }
  137. $hash2 = md5($this->userName . ':' . $this->authName . ':' .$password);
  138. return ($hash === $hash2);
  139. }
  140. return (crypt($password, $hash) === $hash);
  141. }
  142. /**
  143. * Set the format of the password
  144. *
  145. * @param integer|string $cost
  146. * @throws Exception\InvalidArgumentException
  147. * @return Apache
  148. */
  149. public function setFormat($format)
  150. {
  151. $format = strtolower($format);
  152. if (!in_array($format, $this->supportedFormat)) {
  153. throw new Exception\InvalidArgumentException(sprintf(
  154. 'The format %s specified is not valid. The supported formats are: %s',
  155. $format, implode(',', $this->supportedFormat)
  156. ));
  157. }
  158. $this->format = $format;
  159. return $this;
  160. }
  161. /**
  162. * Get the format of the password
  163. *
  164. * @return string
  165. */
  166. public function getFormat()
  167. {
  168. return $this->format;
  169. }
  170. /**
  171. * Set the AuthName (for digest authentication)
  172. *
  173. * @param string $name
  174. * @return Apache
  175. */
  176. public function setAuthName($name)
  177. {
  178. $this->authName = $name;
  179. return $this;
  180. }
  181. /**
  182. * Get the AuthName (for digest authentication)
  183. *
  184. * @return string
  185. */
  186. public function getAuthName()
  187. {
  188. return $this->authName;
  189. }
  190. /**
  191. * Set the username
  192. *
  193. * @param string $name
  194. * @return Apache
  195. */
  196. public function setUserName($name)
  197. {
  198. $this->userName = $name;
  199. return $this;
  200. }
  201. /**
  202. * Get the username
  203. *
  204. * @return string
  205. */
  206. public function getUserName()
  207. {
  208. return $this->userName;
  209. }
  210. /**
  211. * Convert a binary string using the alphabet "./0-9A-Za-z"
  212. *
  213. * @param string $value
  214. * @return string
  215. */
  216. protected function toAlphabet64($value)
  217. {
  218. return strtr(strrev(substr(base64_encode($value), 2)), self::BASE64, self::ALPHA64);
  219. }
  220. /**
  221. * APR1 MD5 algorithm
  222. *
  223. * @param string $password
  224. * @return string
  225. */
  226. protected function apr1Md5($password, $salt = null)
  227. {
  228. if (null === $salt) {
  229. $salt = Rand::getString(8, self::ALPHA64);
  230. } else {
  231. if (strlen($salt) !== 8) {
  232. throw new Exception\InvalidArgumentException(
  233. 'The salt value for APR1 algorithm must be 8 characters long'
  234. );
  235. }
  236. for ($i = 0; $i < 8; $i++) {
  237. if (strpos(self::ALPHA64, $salt[$i]) === false) {
  238. throw new Exception\InvalidArgumentException(
  239. 'The salt value must be a string in the alphabet "./0-9A-Za-z"'
  240. );
  241. }
  242. }
  243. }
  244. $len = strlen($password);
  245. $text = $password . '$apr1$' . $salt;
  246. $bin = pack("H32", md5($password . $salt . $password));
  247. for ($i = $len; $i > 0; $i -= 16) {
  248. $text .= substr($bin, 0, min(16, $i));
  249. }
  250. for ($i = $len; $i > 0; $i >>= 1) {
  251. $text .= ($i & 1) ? chr(0) : $password[0];
  252. }
  253. $bin = pack("H32", md5($text));
  254. for ($i = 0; $i < 1000; $i++) {
  255. $new = ($i & 1) ? $password : $bin;
  256. if ($i % 3) {
  257. $new .= $salt;
  258. }
  259. if ($i % 7) {
  260. $new .= $password;
  261. }
  262. $new .= ($i & 1) ? $bin : $password;
  263. $bin = pack("H32", md5($new));
  264. }
  265. $tmp = '';
  266. for ($i = 0; $i < 5; $i++) {
  267. $k = $i + 6;
  268. $j = $i + 12;
  269. if ($j == 16) $j = 5;
  270. $tmp = $bin[$i] . $bin[$k] . $bin[$j] . $tmp;
  271. }
  272. $tmp = chr(0) . chr(0) . $bin[11] . $tmp;
  273. return '$apr1$' . $salt . '$' . $this->toAlphabet64($tmp);
  274. }
  275. }