XenforoPassword::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 6
ccs 0
cts 6
cp 0
crap 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Todd Burry <[email protected]>
4
 * @copyright 2009-2014 Vanilla Forums Inc.
5
 * @license MIT
6
 */
7
8
namespace Garden\Password;
9
10
/**
11
 * Implements the password hashing algorithm of Xenforo.
12
 */
13
class XenforoPassword implements IPassword {
14
15
    /**
16
     * @var string The name of the hashing function to use.
17
     */
18
    protected $hashFunction;
19
20
    /**
21
     * Initialize an instance of this class.
22
     *
23
     * @param string $hashFunction The name of the hash function to use.
24
     * This is an function name that can be passed to {@link hash()}.
25
     * @see hash()
26
     */
27
    public function __construct($hashFunction = '') {
28
        if (!$hashFunction) {
29
            $hashFunction = 'sha256';
30
        }
31
        $this->hashFunction = $hashFunction;
32
    }
33
34
    /**
35
     * {@inheritdoc}
36
     */
37 3
    public function hash($password) {
38 3
        $salt = base64_encode(openssl_random_pseudo_bytes(12));
39
        $result = [
40 3
            'hashFunc' => $this->hashFunction,
41 3
            'hash' => $this->hashRaw($password, $salt, $this->hashFunction),
42
            'salt' => $salt
43 3
        ];
44
45 3
        return serialize($result);
46
    }
47
48
    /**
49
     * Hashes a password with a given salt.
50
     *
51
     * @param string $password The password to hash.
52
     * @param string $salt The password salt.
53
     * @param string $function The hashing function to use.
54
     * @return string Returns the password hash.
55
     */
56 4
    protected function hashRaw($password, $salt, $function = null) {
57 4
        if (!$function) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $function of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
58 1
            $function = $this->hashFunction;
59 1
        }
60
61 4
        $calc_hash = hash($function, hash($function, $password).$salt);
62
63 4
        return $calc_hash;
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 1 View Code Duplication
    public function needsRehash($hash) {
70 1
        list($stored_hash, $stored_salt) = $this->splitHash($hash);
71
72
        // Unsalted hashes should be rehashed.
73 1
        if ($stored_hash === false || $stored_salt === false) {
74
            return true;
75
        }
76
77 1
        return false;
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83 3
    public function verify($password, $hash) {
84 3
        list($stored_hash, $function, $stored_salt) = $this->splitHash($hash);
85
86 3
        $calc_hash = $this->hashRaw($password, $stored_salt, $function);
87 3
        $result = $calc_hash === $stored_hash;
88
89 3
        return $result;
90
    }
91
92
    /**
93
     * Split the hash into its calculated hash and salt.
94
     *
95
     * @param string $hash The hash to split.
96
     * @return array An array in the form [$hash, $hashFunc, $salt].
97
     */
98 4
    protected function splitHash($hash) {
99 4
        $parts = @unserialize($hash);
100
101 4
        if (!is_array($parts)) {
102 1
            $result = ['', '', ''];
103 1
        } else {
104 3
            $parts = array_merge(['hash' => '', 'hashFunc' => '', 'salt' => ''], $parts);
105
106 3
            if (!$parts['hashFunc']) {
107
                switch (strlen($parts['hash'])) {
108
                    case 32:
109
                        $parts['hashFunc'] = 'md5';
110
                        break;
111
                    case 40:
112
                        $parts['hashFunc'] = 'sha1';
113
                        break;
114
                }
115
            }
116
117 3
            $result = [$parts['hash'], $parts['hashFunc'], $parts['salt']];
118
        }
119 4
        return $result;
120
    }
121
}
122