fix calendar css, take 2. (#213)
[openemr.git] / interface / modules / zend_modules / library / Zend / Crypt / Password / Apache.php
blob7a0cd12fbb48749e9475f968ed7582af69ffb41c
1 <?php
2 /**
3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
8 */
10 namespace Zend\Crypt\Password;
12 use Traversable;
13 use Zend\Math\Rand;
14 use Zend\Crypt\Utils;
16 /**
17 * Apache password authentication
19 * @see http://httpd.apache.org/docs/2.2/misc/password_encryptions.html
21 class Apache implements PasswordInterface
23 const BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
24 const ALPHA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
26 /**
27 * @var array
29 protected $supportedFormat = array(
30 'crypt',
31 'sha1',
32 'md5',
33 'digest',
36 /**
37 * @var string
39 protected $format;
41 /**
42 * @var string AuthName (realm) for digest authentication
44 protected $authName;
46 /**
47 * @var string UserName
49 protected $userName;
51 /**
52 * Constructor
54 * @param array|Traversable $options
55 * @throws Exception\InvalidArgumentException
57 public function __construct($options = array())
59 if (empty($options)) {
60 return;
62 if (!is_array($options) && !$options instanceof Traversable) {
63 throw new Exception\InvalidArgumentException(
64 'The options parameter must be an array or a Traversable'
67 foreach ($options as $key => $value) {
68 switch (strtolower($key)) {
69 case 'format':
70 $this->setFormat($value);
71 break;
72 case 'authname':
73 $this->setAuthName($value);
74 break;
75 case 'username':
76 $this->setUserName($value);
77 break;
82 /**
83 * Generate the hash of a password
85 * @param string $password
86 * @throws Exception\RuntimeException
87 * @return string
89 public function create($password)
91 if (empty($this->format)) {
92 throw new Exception\RuntimeException(
93 'You must specify a password format'
96 switch ($this->format) {
97 case 'crypt':
98 $hash = crypt($password, Rand::getString(2, self::ALPHA64));
99 break;
100 case 'sha1':
101 $hash = '{SHA}' . base64_encode(sha1($password, true));
102 break;
103 case 'md5':
104 $hash = $this->apr1Md5($password);
105 break;
106 case 'digest':
107 if (empty($this->userName) || empty($this->authName)) {
108 throw new Exception\RuntimeException(
109 'You must specify UserName and AuthName (realm) to generate the digest'
112 $hash = md5($this->userName . ':' . $this->authName . ':' .$password);
113 break;
116 return $hash;
120 * Verify if a password is correct against a hash value
122 * @param string $password
123 * @param string $hash
124 * @return bool
126 public function verify($password, $hash)
128 if (substr($hash, 0, 5) === '{SHA}') {
129 $hash2 = '{SHA}' . base64_encode(sha1($password, true));
130 return Utils::compareStrings($hash, $hash2);
133 if (substr($hash, 0, 6) === '$apr1$') {
134 $token = explode('$', $hash);
135 if (empty($token[2])) {
136 throw new Exception\InvalidArgumentException(
137 'The APR1 password format is not valid'
140 $hash2 = $this->apr1Md5($password, $token[2]);
141 return Utils::compareStrings($hash, $hash2);
144 $bcryptPattern = '/\$2[ay]?\$[0-9]{2}\$[' . addcslashes(static::BASE64, '+/') . '\.]{53}/';
146 if (strlen($hash) > 13 && ! preg_match($bcryptPattern, $hash)) { // digest
147 if (empty($this->userName) || empty($this->authName)) {
148 throw new Exception\RuntimeException(
149 'You must specify UserName and AuthName (realm) to verify the digest'
152 $hash2 = md5($this->userName . ':' . $this->authName . ':' .$password);
153 return Utils::compareStrings($hash, $hash2);
156 return Utils::compareStrings($hash, crypt($password, $hash));
160 * Set the format of the password
162 * @param string $format
163 * @throws Exception\InvalidArgumentException
164 * @return Apache
166 public function setFormat($format)
168 $format = strtolower($format);
169 if (!in_array($format, $this->supportedFormat)) {
170 throw new Exception\InvalidArgumentException(sprintf(
171 'The format %s specified is not valid. The supported formats are: %s',
172 $format,
173 implode(',', $this->supportedFormat)
176 $this->format = $format;
178 return $this;
182 * Get the format of the password
184 * @return string
186 public function getFormat()
188 return $this->format;
192 * Set the AuthName (for digest authentication)
194 * @param string $name
195 * @return Apache
197 public function setAuthName($name)
199 $this->authName = $name;
201 return $this;
205 * Get the AuthName (for digest authentication)
207 * @return string
209 public function getAuthName()
211 return $this->authName;
215 * Set the username
217 * @param string $name
218 * @return Apache
220 public function setUserName($name)
222 $this->userName = $name;
224 return $this;
228 * Get the username
230 * @return string
232 public function getUserName()
234 return $this->userName;
238 * Convert a binary string using the alphabet "./0-9A-Za-z"
240 * @param string $value
241 * @return string
243 protected function toAlphabet64($value)
245 return strtr(strrev(substr(base64_encode($value), 2)), self::BASE64, self::ALPHA64);
249 * APR1 MD5 algorithm
251 * @param string $password
252 * @param null|string $salt
253 * @return string
255 protected function apr1Md5($password, $salt = null)
257 if (null === $salt) {
258 $salt = Rand::getString(8, self::ALPHA64);
259 } else {
260 if (strlen($salt) !== 8) {
261 throw new Exception\InvalidArgumentException(
262 'The salt value for APR1 algorithm must be 8 characters long'
265 for ($i = 0; $i < 8; $i++) {
266 if (strpos(self::ALPHA64, $salt[$i]) === false) {
267 throw new Exception\InvalidArgumentException(
268 'The salt value must be a string in the alphabet "./0-9A-Za-z"'
273 $len = strlen($password);
274 $text = $password . '$apr1$' . $salt;
275 $bin = pack("H32", md5($password . $salt . $password));
276 for ($i = $len; $i > 0; $i -= 16) {
277 $text .= substr($bin, 0, min(16, $i));
279 for ($i = $len; $i > 0; $i >>= 1) {
280 $text .= ($i & 1) ? chr(0) : $password[0];
282 $bin = pack("H32", md5($text));
283 for ($i = 0; $i < 1000; $i++) {
284 $new = ($i & 1) ? $password : $bin;
285 if ($i % 3) {
286 $new .= $salt;
288 if ($i % 7) {
289 $new .= $password;
291 $new .= ($i & 1) ? $bin : $password;
292 $bin = pack("H32", md5($new));
294 $tmp = '';
295 for ($i = 0; $i < 5; $i++) {
296 $k = $i + 6;
297 $j = $i + 12;
298 if ($j == 16) {
299 $j = 5;
301 $tmp = $bin[$i] . $bin[$k] . $bin[$j] . $tmp;
303 $tmp = chr(0) . chr(0) . $bin[11] . $tmp;
305 return '$apr1$' . $salt . '$' . $this->toAlphabet64($tmp);