WordPress.org

Make WordPress Core

Ticket #2394: class-phpass.php

File class-phpass.php, 6.4 KB (added by pishmishy, 13 years ago)

phpass file for wp-includes

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