Completed
Push — master ( 20d8fe...40dd32 )
by Harald
09:20 queued 04:54
created

Hash   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 210
Duplicated Lines 4.76 %

Coupling/Cohesion

Components 0
Dependencies 2

Importance

Changes 0
Metric Value
dl 10
loc 210
rs 8.3673
c 0
b 0
f 0
wmc 45
lcom 0
cbo 2

7 Methods

Rating   Name   Duplication   Size   Complexity  
D encrypt() 5 40 10
C verify() 0 38 7
B needsRehash() 5 14 5
A getType() 0 20 4
B getRandomInt() 0 22 5
D getRandomString() 0 45 10
B getRandomBytes() 0 22 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Hash often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Hash, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
  * osCommerce Online Merchant
4
  *
5
  * @copyright (c) 2016 osCommerce; https://www.oscommerce.com
6
  * @license GPL; https://www.oscommerce.com/gpllicense.txt
7
  */
8
9
namespace OSC\OM;
10
11
use OSC\OM\OSCOM;
12
13
class Hash
14
{
15
    public static function encrypt($plain, $algo = null)
16
    {
17
        if (!isset($algo) || ($algo == 'default') || ($algo == 'bcrypt')) {
18 View Code Duplication
            if (!isset($algo) || ($algo == 'default')) {
0 ignored issues
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...
19
                $algo = PASSWORD_DEFAULT;
20
            } else {
21
                $algo = PASSWORD_BCRYPT;
22
            }
23
24
            return password_hash($plain, $algo);
25
        }
26
27
        if ($algo == 'phpass') {
28
            if (!class_exists('PasswordHash', false)) {
29
                include(OSCOM::getConfig('dir_root', 'Shop') . 'includes/third_party/PasswordHash.php');
30
            }
31
32
            $hasher = new \PasswordHash(10, true);
33
34
            return $hasher->HashPassword($plain);
35
        }
36
37
        if ($algo == 'salt') {
38
            $password = '';
39
40
            for ($i=0; $i<10; $i++) {
41
                $password .= static::getRandomInt();
42
            }
43
44
            $salt = substr(md5($password), 0, 2);
45
46
            $password = md5($salt . $plain) . ':' . $salt;
47
48
            return $password;
49
        }
50
51
        trigger_error('OSC\\OM\\Hash::encrypt() Algorithm "' . $algo . '" unknown.');
52
53
        return false;
54
    }
55
56
    public static function verify($plain, $hash)
57
    {
58
        $result = false;
59
60
        if ((strlen($plain) > 0) && (strlen($hash) > 0)) {
61
            switch (static::getType($hash)) {
62
                case 'phpass':
63
                    if (!class_exists('PasswordHash', false)) {
64
                        include(OSCOM::getConfig('dir_root', 'Shop') . 'includes/third_party/PasswordHash.php');
65
                    }
66
67
                    $hasher = new \PasswordHash(10, true);
68
69
                    $result = $hasher->CheckPassword($plain, $hash);
70
71
                    break;
72
73
                case 'salt':
74
                    // split apart the hash / salt
75
                    $stack = explode(':', $hash, 2);
76
77
                    if (count($stack) === 2) {
78
                        $result = (md5($stack[1] . $plain) == $stack[0]);
79
                    } else {
80
                        $result = false;
81
                    }
82
83
                    break;
84
85
                default:
86
                    $result = password_verify($plain, $hash);
87
88
                    break;
89
            }
90
        }
91
92
        return $result;
93
    }
94
95
    public static function needsRehash($hash, $algo = null)
96
    {
97 View Code Duplication
        if (!isset($algo) || ($algo == 'default')) {
0 ignored issues
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...
98
            $algo = PASSWORD_DEFAULT;
99
        } elseif ($algo == 'bcrypt') {
100
            $algo = PASSWORD_BCRYPT;
101
        }
102
103
        if (!is_int($algo)) {
104
            trigger_error('OSC\OM\Hash::needsRehash() Algorithm "' . $algo . '" not supported.');
105
        }
106
107
        return password_needs_rehash($hash, $algo);
108
    }
109
110
    public static function getType($hash)
111
    {
112
        $info = password_get_info($hash);
113
114
        if ($info['algo'] > 0) {
115
            return $info['algoName'];
116
        }
117
118
        if (substr($hash, 0, 3) == '$P$') {
119
            return 'phpass';
120
        }
121
122
        if (preg_match('/^[A-Z0-9]{32}\:[A-Z0-9]{2}$/i', $hash) === 1) {
123
            return 'salt';
124
        }
125
126
        trigger_error('OSC\OM\Hash::getType() hash type not found for "' . substr($hash, 0, 5) . '"');
127
128
        return '';
129
    }
130
131
    public static function getRandomInt($min = null, $max = null, $secure = true)
132
    {
133
        if (!isset($min)) {
134
            $min = 0;
135
        }
136
137
        if (!isset($max)) {
138
            $max = PHP_INT_MAX;
139
        }
140
141
        try {
142
            $result = random_int($min, $max);
143
        } catch (\Exception $e) {
144
            if ($secure === true) {
145
                throw $e;
146
            }
147
148
            $result = mt_rand($min, $max);
149
        }
150
151
        return $result;
152
    }
153
154
    public static function getRandomString($length, $type = 'mixed')
155
    {
156
        if (!in_array($type, [
157
            'mixed',
158
            'chars',
159
            'digits'
160
        ])) {
161
            trigger_error('Hash::getRandomString() $type not recognized: ' . $type, E_USER_ERROR);
162
163
            return false;
164
        }
165
166
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
167
        $digits = '0123456789';
168
169
        $base = '';
170
171
        if (($type == 'mixed') || ($type == 'chars')) {
172
            $base .= $chars;
173
        }
174
175
        if (($type == 'mixed') || ($type == 'digits')) {
176
            $base .= $digits;
177
        }
178
179
        $rand_value = '';
180
181
        do {
182
            $random = base64_encode(static::getRandomBytes($length));
183
184
            for ($i=0, $n=strlen($random); $i<$n; $i++) {
185
                $char = substr($random, $i, 1);
186
187
                if (strpos($base, $char) !== false) {
188
                    $rand_value .= $char;
189
                }
190
            }
191
        } while (strlen($rand_value) < $length);
192
193
        if (strlen($rand_value) > $length) {
194
            $rand_value = substr($rand_value, 0, $length);
195
        }
196
197
        return $rand_value;
198
    }
199
200
    public static function getRandomBytes($length, $secure = true)
201
    {
202
        try {
203
            $result = random_bytes($length);
204
        } catch (\Exception $e) {
205
            if ($secure === true) {
206
                throw $e;
207
            }
208
209
            $result = '';
210
211
            for ($i=0; $i<$length; $i+=16) {
212
                $random_state = md5(microtime() . $random_state);
0 ignored issues
show
Bug introduced by
The variable $random_state does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
213
214
                $result .= pack('H*', md5($random_state));
215
            }
216
217
            $result = substr($result, 0, $length);
218
        }
219
220
        return $result;
221
    }
222
}
223