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')) { |
|
|
|
|
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')) { |
|
|
|
|
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); |
|
|
|
|
213
|
|
|
|
214
|
|
|
$result .= pack('H*', md5($random_state)); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
$result = substr($result, 0, $length); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
return $result; |
221
|
|
|
} |
222
|
|
|
} |
223
|
|
|
|
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.