PasswordHash.php Quellcode

PasswordHash.php
gehe zur Dokumentation dieser Datei
1 <?php
9 #
10 # Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
11 # the public domain. Revised in subsequent years, still public domain.
12 #
13 # There's absolutely no warranty.
14 #
15 # The homepage URL for this framework is:
16 #
17 # http://www.openwall.com/phpass/
18 #
19 # Please be sure to update the Version line if you edit this file in any way.
20 # It is suggested that you leave the main version number intact, but indicate
21 # your project name (after the slash) and add your own revision information.
22 #
23 # Please do not change the "private" password hashing method implemented in
24 # here, thereby making your hashes incompatible. However, if you must, please
25 # change the hash type identifier (the "$P$") to something different.
26 #
27 # Obviously, since this code is in the public domain, the above are not
28 # requirements (there can be none), but merely suggestions.
29 #
30 class PasswordHash {
31  var $itoa64;
35 
37  {
38  $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
39 
40  if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
42  $this->iteration_count_log2 = $iteration_count_log2;
43 
44  $this->portable_hashes = $portable_hashes;
45 
46  $this->random_state = microtime() . uniqid(rand(), TRUE); // removed getmypid() for compatibility reasons
47  }
48 
49  function get_random_bytes($count)
50  {
51  $output = '';
52  if ( @is_readable('/dev/urandom') &&
53  ($fh = @fopen('/dev/urandom', 'rb'))) {
54  $output = fread($fh, $count);
55  fclose($fh);
56  }
57 
58  if (strlen($output) < $count) {
59  $output = '';
60  for ($i = 0; $i < $count; $i += 16) {
61  $this->random_state =
62  md5(microtime() . $this->random_state);
63  $output .=
64  pack('H*', md5($this->random_state));
65  }
66  $output = substr($output, 0, $count);
67  }
68 
69  return $output;
70  }
71 
72  function encode64($input, $count)
73  {
74  $output = '';
75  $i = 0;
76  do {
77  $value = ord($input[$i++]);
78  $output .= $this->itoa64[$value & 0x3f];
79  if ($i < $count)
80  $value |= ord($input[$i]) << 8;
81  $output .= $this->itoa64[($value >> 6) & 0x3f];
82  if ($i++ >= $count)
83  break;
84  if ($i < $count)
85  $value |= ord($input[$i]) << 16;
86  $output .= $this->itoa64[($value >> 12) & 0x3f];
87  if ($i++ >= $count)
88  break;
89  $output .= $this->itoa64[($value >> 18) & 0x3f];
90  } while ($i < $count);
91 
92  return $output;
93  }
94 
95  function gensalt_private($input)
96  {
97  $output = '$P$';
98  $output .= $this->itoa64[min($this->iteration_count_log2 +
99  ((PHP_VERSION >= '5') ? 5 : 3), 30)];
100  $output .= $this->encode64($input, 6);
101 
102  return $output;
103  }
104 
105  function crypt_private($password, $setting)
106  {
107  $output = '*0';
108  if (substr($setting, 0, 2) == $output)
109  $output = '*1';
110 
111  $id = substr($setting, 0, 3);
112  # We use "$P$", phpBB3 uses "$H$" for the same thing
113  if ($id != '$P$' && $id != '$H$')
114  return $output;
115 
116  $count_log2 = strpos($this->itoa64, $setting[3]);
117  if ($count_log2 < 7 || $count_log2 > 30)
118  return $output;
119 
120  $count = 1 << $count_log2;
121 
122  $salt = substr($setting, 4, 8);
123  if (strlen($salt) != 8)
124  return $output;
125 
126  # We're kind of forced to use MD5 here since it's the only
127  # cryptographic primitive available in all versions of PHP
128  # currently in use. To implement our own low-level crypto
129  # in PHP would result in much worse performance and
130  # consequently in lower iteration counts and hashes that are
131  # quicker to crack (by non-PHP code).
132  if (PHP_VERSION >= '5') {
133  $hash = md5($salt . $password, TRUE);
134  do {
135  $hash = md5($hash . $password, TRUE);
136  } while (--$count);
137  } else {
138  $hash = pack('H*', md5($salt . $password));
139  do {
140  $hash = pack('H*', md5($hash . $password));
141  } while (--$count);
142  }
143 
144  $output = substr($setting, 0, 12);
145  $output .= $this->encode64($hash, 16);
146 
147  return $output;
148  }
149 
150  function gensalt_extended($input)
151  {
152  $count_log2 = min($this->iteration_count_log2 + 8, 24);
153  # This should be odd to not reveal weak DES keys, and the
154  # maximum valid value is (2**24 - 1) which is odd anyway.
155  $count = (1 << $count_log2) - 1;
156 
157  $output = '_';
158  $output .= $this->itoa64[$count & 0x3f];
159  $output .= $this->itoa64[($count >> 6) & 0x3f];
160  $output .= $this->itoa64[($count >> 12) & 0x3f];
161  $output .= $this->itoa64[($count >> 18) & 0x3f];
162 
163  $output .= $this->encode64($input, 3);
164 
165  return $output;
166  }
167 
168  function gensalt_blowfish($input)
169  {
170  # This one needs to use a different order of characters and a
171  # different encoding scheme from the one in encode64() above.
172  # We care because the last character in our encoded string will
173  # only represent 2 bits. While two known implementations of
174  # bcrypt will happily accept and correct a salt string which
175  # has the 4 unused bits set to non-zero, we do not want to take
176  # chances and we also do not want to waste an additional byte
177  # of entropy.
178  $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
179 
180  $output = '$2a$';
181  $output .= chr(ord('0') + $this->iteration_count_log2 / 10);
182  $output .= chr(ord('0') + $this->iteration_count_log2 % 10);
183  $output .= '$';
184 
185  $i = 0;
186  do {
187  $c1 = ord($input[$i++]);
188  $output .= $itoa64[$c1 >> 2];
189  $c1 = ($c1 & 0x03) << 4;
190  if ($i >= 16) {
191  $output .= $itoa64[$c1];
192  break;
193  }
194 
195  $c2 = ord($input[$i++]);
196  $c1 |= $c2 >> 4;
197  $output .= $itoa64[$c1];
198  $c1 = ($c2 & 0x0f) << 2;
199 
200  $c2 = ord($input[$i++]);
201  $c1 |= $c2 >> 6;
202  $output .= $itoa64[$c1];
203  $output .= $itoa64[$c2 & 0x3f];
204  } while (1);
205 
206  return $output;
207  }
208 
209  function HashPassword($password)
210  {
211  $random = '';
212 
213  if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
214  $random = $this->get_random_bytes(16);
215  $hash =
216  crypt($password, $this->gensalt_blowfish($random));
217  if (strlen($hash) == 60)
218  return $hash;
219  }
220 
221  if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
222  if (strlen($random) < 3)
223  $random = $this->get_random_bytes(3);
224  $hash =
225  crypt($password, $this->gensalt_extended($random));
226  if (strlen($hash) == 20)
227  return $hash;
228  }
229 
230  if (strlen($random) < 6)
231  $random = $this->get_random_bytes(6);
232  $hash =
233  $this->crypt_private($password,
234  $this->gensalt_private($random));
235  if (strlen($hash) == 34)
236  return $hash;
237 
238  # Returning '*' on error is safe here, but would _not_ be safe
239  # in a crypt(3)-like function used _both_ for generating new
240  # hashes and for validating passwords against existing hashes.
241  return '*';
242  }
243 
244  function CheckPassword($password, $stored_hash)
245  {
246  $hash = $this->crypt_private($password, $stored_hash);
247  if ($hash[0] == '*')
248  $hash = crypt($password, $stored_hash);
249 
250  return $hash == $stored_hash;
251  }
252 }
253 
254 




Korrekturen, Hinweise und Ergänzungen

Bitte scheuen Sie sich nicht und melden Sie, was auf dieser Seite sachlich falsch oder irreführend ist, was ergänzt werden sollte, was fehlt usw. Dazu bitte oben aus dem Menü Seite den Eintrag Support Forum wählen. Es ist eine kostenlose Anmeldung erforderlich, um Anmerkungen zu posten. Unpassende Postings, Spam usw. werden kommentarlos entfernt.