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

Hash::getRandomBytes()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 3
nop 2
dl 0
loc 22
rs 8.9197
c 0
b 0
f 0
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