Completed
Push — master ( b3af43...6273ca )
by Todd
19:01
created

PhpbbPassword::cryptPrivate()   C

Complexity

Conditions 8
Paths 5

Size

Total Lines 47
Code Lines 25

Duplication

Lines 11
Ratio 23.4 %

Code Coverage

Tests 16
CRAP Score 9.2979
Metric Value
dl 11
loc 47
ccs 16
cts 22
cp 0.7273
rs 5.7377
cc 8
eloc 25
nc 5
nop 2
crap 9.2979
1
<?php
2
/**
3
 * @author Todd Burry <[email protected]>
4
 * @copyright 2005 phpBB Group (Original source code)
5
 * @copyright 2009-2014 Vanilla Forums Inc. (Source code changes)
6
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
7
 *
8
 * This class adapts functions from phpBB 3 (/includes/functions.php). Any source code that was taken from the
9
 * phpBB project is copyright 2005 phpBB Group. Any source code changes are copyright 2009-2014 Vanilla Forums Inc.
10
 *
11
 */
12
13
namespace Garden\Password;
14
15
/**
16
 * Implements phpBB's password hashing algorithm.
17
 */
18
class PhpbbPassword implements IPassword {
19
    const ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
20
21
    /**
22
     * Check for a correct password.
23
     *
24
     * @param string $password The password in plain text.
25
     * @param string $hash The stored password hash.
26
     * @return bool Returns true if the password is correct, false if not.
27
     */
28 3
    public function verify($password, $hash) {
29 3
        if (strlen($hash) == 34) {
30 2
            return ($this->cryptPrivate($password, $hash) === $hash) ? true : false;
31
        }
32
33 1
        return (md5($password) === $hash) ? true : false;
34
    }
35
36
    /**
37
     * The crypt function/replacement.
38
     *
39
     * @param string $password The password to encrypt.
40
     * @param string $setting The hash prefix setting. It should start with $H$.
41
     * @return string The encypted password.
42
     */
43 3
    private function cryptPrivate($password, $setting) {
44 3
        $itoa64 = PhpbbPassword::ITOA64;
45 3
        $output = '*';
46
47
        // Check for correct hash
48 3
        if (substr($setting, 0, 3) != '$H$') {
49
            return $output;
50
        }
51
52 3
        $count_log2 = strpos($itoa64, $setting[3]);
53
54 3
        if ($count_log2 < 7 || $count_log2 > 30) {
55
            return $output;
56
        }
57
58 3
        $count = 1 << $count_log2;
59 3
        $salt = substr($setting, 4, 8);
60
61 3
        if (strlen($salt) != 8) {
62
            return $output;
63
        }
64
65
        /**
66
         * We're kind of forced to use MD5 here since it's the only
67
         * cryptographic primitive available in all versions of PHP
68
         * currently in use.  To implement our own low-level crypto
69
         * in PHP would result in much worse performance and
70
         * consequently in lower iteration counts and hashes that are
71
         * quicker to crack (by non-PHP code).
72
         */
73 3 View Code Duplication
        if (PHP_VERSION >= 5) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
74 3
            $hash = md5($salt.$password, true);
75
            do {
76 3
                $hash = md5($hash.$password, true);
77 3
            } while (--$count);
78
        } else {
79
            $hash = pack('H*', md5($salt.$password));
80
            do {
81
                $hash = pack('H*', md5($hash.$password));
82
            } while (--$count);
83
        }
84
85 3
        $output = substr($setting, 0, 12);
86 3
        $output .= $this->encode64($hash, 16);
87
88 3
        return $output;
89
    }
90
91
    /**
92
     * Encode hash.
93
     *
94
     * @param string $input The input to encode.
95
     * @param int $count The number of characters to encode.
96
     * @return string The encoded string.
97
     */
98 3
    protected function encode64($input, $count) {
99 3
        $itoa64 = PhpbbPassword::ITOA64;
100 3
        $output = '';
101 3
        $i = 0;
102
103
        do {
104 3
            $value = ord($input[$i++]);
105 3
            $output .= $itoa64[$value & 0x3f];
106
107 3
            if ($i < $count) {
108 3
                $value |= ord($input[$i]) << 8;
109
            }
110
111 3
            $output .= $itoa64[($value >> 6) & 0x3f];
112
113 3
            if ($i++ >= $count) {
114 3
                break;
115
            }
116
117 3
            if ($i < $count) {
118 3
                $value |= ord($input[$i]) << 16;
119
            }
120
121 3
            $output .= $itoa64[($value >> 12) & 0x3f];
122
123 3
            if ($i++ >= $count) {
124
                break;
125
            }
126
127 3
            $output .= $itoa64[($value >> 18) & 0x3f];
128 3
        } while ($i < $count);
129
130 3
        return $output;
131
    }
132
133
    /**
134
     * Hashes a plaintext password.
135
     *
136
     * @param string $password The password to hash.
137
     * @return string Returns the hashed password.
138
     */
139 3
    public function hash($password) {
140 3
        $random = openssl_random_pseudo_bytes(6);
141
142 3
        $hash = $this->cryptPrivate($password, $this->gensaltPrivate($random));
143 3
        if (strlen($hash) == 34) {
144 3
            return $hash;
145
        }
146
147
        return null;
148
    }
149
150
    /**
151
     * Generate a password salt based on the given input string.
152
     *
153
     * @param string $input The input string to generate the salt from.
154
     * @return string Returns the password salt prefixed with `$P$`.
155
     */
156 3
    private function gensaltPrivate($input) {
157 3
        $itoa64 = PhpbbPassword::ITOA64;
158
159 3
        $output = '$H$';
160 3
        $output .= $itoa64[min(8 + ((PHP_VERSION >= '5') ? 5 : 3), 30)];
161 3
        $output .= $this->encode64($input, 6);
162
163 3
        return $output;
164
    }
165
166
    /**
167
     * Checks if a given password hash needs to be re-hashed to to a stronger algorithm.
168
     *
169
     * @param string $hash The hash to check.
170
     * @return bool Returns `true`
171
     */
172 1
    public function needsRehash($hash) {
173 1
        return $hash === '*';
174
    }
175
}
176