1
+ <?php
2
+ /**
3
+ * First Global Data Corp.
4
+ *
5
+ * @category PasswordHash
6
+ * @package Api\WebServiceBundle\Tests\Controller
7
+ * @author Anit Shrestha Manandhar <[email protected] >
8
+ * @license http://firstglobalmoney.com/license description
9
+ * @version v1.0.0
10
+ * @link (remittanceController, http://firsglobaldata.com)
11
+ */
12
+
13
+ namespace Library {
14
+
15
+ //define('PASSWORD_BCRYPT', 1);
16
+ // define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
17
+
18
+ /**
19
+ * PasswordHash
20
+ *
21
+ * @category DEX_API
22
+ * @package Api\WebServiceBundle\Tests\Controller
23
+ * @author Anit Shrestha Manandhar <[email protected] >
24
+ * @license http://firstglobalmoney.com/license Usage License
25
+ * @version v1.0.0
26
+ * @link http://firsglobaldata.com
27
+ */
28
+ class PasswordHash
29
+ {
30
+ /**
31
+ * Hash the password using the specified algorithm
32
+ *
33
+ * @param string $password The password to hash
34
+ * @param int $algo The algorithm to use (Defined by PASSWORD_* constants)
35
+ * @param array $options The options for the algorithm to use
36
+ *
37
+ * @return string|false The hashed password, or false on error.
38
+ */
39
+ function password_hash ($ password , $ algo , array $ options = array ()) {
40
+ if (!function_exists ('crypt ' )) {
41
+ trigger_error ("Crypt must be loaded for password_hash to function " , E_USER_WARNING );
42
+ return null ;
43
+ }
44
+ if (!is_string ($ password )) {
45
+ trigger_error ("password_hash(): Password must be a string " , E_USER_WARNING );
46
+ return null ;
47
+ }
48
+ if (!is_int ($ algo )) {
49
+ trigger_error ("password_hash() expects parameter 2 to be long, " . gettype ($ algo ) . " given " , E_USER_WARNING );
50
+ return null ;
51
+ }
52
+ $ resultLength = 0 ;
53
+ switch ($ algo ) {
54
+ case PASSWORD_BCRYPT :
55
+ // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
56
+ $ cost = 10 ;
57
+ if (isset ($ options ['cost ' ])) {
58
+ $ cost = $ options ['cost ' ];
59
+ if ($ cost < 4 || $ cost > 31 ) {
60
+ trigger_error (sprintf ("password_hash(): Invalid bcrypt cost parameter specified: %d " , $ cost ), E_USER_WARNING );
61
+ return null ;
62
+ }
63
+ }
64
+ // The length of salt to generate
65
+ $ raw_salt_len = 16 ;
66
+ // The length required in the final serialization
67
+ $ required_salt_len = 22 ;
68
+ $ hash_format = sprintf ("$2y$%02d$ " , $ cost );
69
+ // The expected length of the final crypt() output
70
+ $ resultLength = 60 ;
71
+ break ;
72
+ default :
73
+ trigger_error (sprintf ("password_hash(): Unknown password hashing algorithm: %s " , $ algo ), E_USER_WARNING );
74
+ return null ;
75
+ }
76
+ $ salt_requires_encoding = false ;
77
+ if (isset ($ options ['salt ' ])) {
78
+ switch (gettype ($ options ['salt ' ])) {
79
+ case 'NULL ' :
80
+ case 'boolean ' :
81
+ case 'integer ' :
82
+ case 'double ' :
83
+ case 'string ' :
84
+ $ salt = (string ) $ options ['salt ' ];
85
+ break ;
86
+ case 'object ' :
87
+ if (method_exists ($ options ['salt ' ], '__tostring ' )) {
88
+ $ salt = (string ) $ options ['salt ' ];
89
+ break ;
90
+ }
91
+ case 'array ' :
92
+ case 'resource ' :
93
+ default :
94
+ trigger_error ('password_hash(): Non-string salt parameter supplied ' , E_USER_WARNING );
95
+ return null ;
96
+ }
97
+ if ($ this ->_strlen ($ salt ) < $ required_salt_len ) {
98
+ trigger_error (sprintf ("password_hash(): Provided salt is too short: %d expecting %d " , $ this ->_strlen ($ salt ), $ required_salt_len ), E_USER_WARNING );
99
+ return null ;
100
+ } elseif (0 == preg_match ('#^[a-zA-Z0-9./]+$#D ' , $ salt )) {
101
+ $ salt_requires_encoding = true ;
102
+ }
103
+ } else {
104
+ $ buffer = '' ;
105
+ $ buffer_valid = false ;
106
+ if (function_exists ('mcrypt_create_iv ' ) && !defined ('PHALANGER ' )) {
107
+ $ buffer = mcrypt_create_iv ($ raw_salt_len , MCRYPT_DEV_URANDOM );
108
+ if ($ buffer ) {
109
+ $ buffer_valid = true ;
110
+ }
111
+ }
112
+ if (!$ buffer_valid && function_exists ('openssl_random_pseudo_bytes ' )) {
113
+ $ buffer = openssl_random_pseudo_bytes ($ raw_salt_len );
114
+ if ($ buffer ) {
115
+ $ buffer_valid = true ;
116
+ }
117
+ }
118
+ if (!$ buffer_valid && @is_readable ('/dev/urandom ' )) {
119
+ $ f = fopen ('/dev/urandom ' , 'r ' );
120
+ $ read = $ this ->_strlen ($ buffer );
121
+ while ($ read < $ raw_salt_len ) {
122
+ $ buffer .= fread ($ f , $ raw_salt_len - $ read );
123
+ $ read = $ this ->_strlen ($ buffer );
124
+ }
125
+ fclose ($ f );
126
+ if ($ read >= $ raw_salt_len ) {
127
+ $ buffer_valid = true ;
128
+ }
129
+ }
130
+ if (!$ buffer_valid || $ this ->_strlen ($ buffer ) < $ raw_salt_len ) {
131
+ $ bl = $ this ->_strlen ($ buffer );
132
+ for ($ i = 0 ; $ i < $ raw_salt_len ; $ i ++) {
133
+ if ($ i < $ bl ) {
134
+ $ buffer [$ i ] = $ buffer [$ i ] ^ chr (mt_rand (0 , 255 ));
135
+ } else {
136
+ $ buffer .= chr (mt_rand (0 , 255 ));
137
+ }
138
+ }
139
+ }
140
+ $ salt = $ buffer ;
141
+ $ salt_requires_encoding = true ;
142
+ }
143
+ if ($ salt_requires_encoding ) {
144
+ // encode string with the Base64 variant used by crypt
145
+ $ base64_digits =
146
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ ' ;
147
+ $ bcrypt64_digits =
148
+ './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ' ;
149
+
150
+ $ base64_string = base64_encode ($ salt );
151
+ $ salt = strtr (rtrim ($ base64_string , '= ' ), $ base64_digits , $ bcrypt64_digits );
152
+ }
153
+ $ salt = $ this ->_substr ($ salt , 0 , $ required_salt_len );
154
+
155
+ $ hash = $ hash_format . $ salt ;
156
+
157
+ $ ret = crypt ($ password , $ hash );
158
+
159
+ if (!is_string ($ ret ) || $ this ->_strlen ($ ret ) != $ resultLength ) {
160
+ return false ;
161
+ }
162
+
163
+ return $ ret ;
164
+ }
165
+
166
+ /**
167
+ * Get information about the password hash. Returns an array of the information
168
+ * that was used to generate the password hash.
169
+ *
170
+ * array(
171
+ * 'algo' => 1,
172
+ * 'algoName' => 'bcrypt',
173
+ * 'options' => array(
174
+ * 'cost' => 10,
175
+ * ),
176
+ * )
177
+ *
178
+ * @param string $hash The password hash to extract info from
179
+ *
180
+ * @return array The array of information about the hash.
181
+ */
182
+ function password_get_info ($ hash ) {
183
+ $ return = array (
184
+ 'algo ' => 0 ,
185
+ 'algoName ' => 'unknown ' ,
186
+ 'options ' => array (),
187
+ );
188
+ if ($ this ->_substr ($ hash , 0 , 4 ) == '$2y$ ' && $ this ->_strlen ($ hash ) == 60 ) {
189
+ $ return ['algo ' ] = PASSWORD_BCRYPT ;
190
+ $ return ['algoName ' ] = 'bcrypt ' ;
191
+ list ($ cost ) = sscanf ($ hash , "$2y$%d$ " );
192
+ $ return ['options ' ]['cost ' ] = $ cost ;
193
+ }
194
+ return $ return ;
195
+ }
196
+
197
+ /**
198
+ * Determine if the password hash needs to be rehashed according to the options provided
199
+ *
200
+ * If the answer is true, after validating the password using password_verify, rehash it.
201
+ *
202
+ * @param string $hash The hash to test
203
+ * @param int $algo The algorithm used for new password hashes
204
+ * @param array $options The options array passed to password_hash
205
+ *
206
+ * @return boolean True if the password needs to be rehashed.
207
+ */
208
+ function password_needs_rehash ($ hash , $ algo , array $ options = array ()) {
209
+ $ info = password_get_info ($ hash );
210
+ if ($ info ['algo ' ] != $ algo ) {
211
+ return true ;
212
+ }
213
+ switch ($ algo ) {
214
+ case PASSWORD_BCRYPT :
215
+ $ cost = isset ($ options ['cost ' ]) ? $ options ['cost ' ] : 10 ;
216
+ if ($ cost != $ info ['options ' ]['cost ' ]) {
217
+ return true ;
218
+ }
219
+ break ;
220
+ }
221
+ return false ;
222
+ }
223
+
224
+ /**
225
+ * Verify a password against a hash using a timing attack resistant approach
226
+ *
227
+ * @param string $password The password to verify
228
+ * @param string $hash The hash to verify against
229
+ *
230
+ * @return boolean If the password matches the hash
231
+ */
232
+ function password_verify ($ password , $ hash ) {
233
+
234
+ if (!function_exists ('crypt ' )) {
235
+ trigger_error ("Crypt must be loaded for password_verify to function " , E_USER_WARNING );
236
+ return false ;
237
+ }
238
+ $ ret = crypt ($ password , $ hash );
239
+ if (!is_string ($ ret ) || $ this ->_strlen ($ ret ) != $ this ->_strlen ($ hash ) || $ this ->_strlen ($ ret ) <= 13 ) {
240
+ return false ;
241
+ }
242
+
243
+ $ status = 0 ;
244
+ for ($ i = 0 ; $ i < $ this ->_strlen ($ ret ); $ i ++) {
245
+ $ status |= (ord ($ ret [$ i ]) ^ ord ($ hash [$ i ]));
246
+ }
247
+
248
+ return $ status ===0 ;
249
+
250
+ }
251
+
252
+ /**
253
+ * Count the number of bytes in a string
254
+ *
255
+ * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension.
256
+ * In this case, strlen() will count the number of *characters* based on the internal encoding. A
257
+ * sequence of bytes might be regarded as a single multibyte character.
258
+ *
259
+ * @param string $binary_string The input string
260
+ *
261
+ * @internal
262
+ * @return int The number of bytes
263
+ */
264
+ function _strlen ($ binary_string ) {
265
+ if (function_exists ('mb_strlen ' )) {
266
+ return mb_strlen ($ binary_string , '8bit ' );
267
+ }
268
+ return strlen ($ binary_string );
269
+ }
270
+
271
+ /**
272
+ * Get a substring based on byte limits
273
+ *
274
+ * @see _strlen()
275
+ *
276
+ * @param string $binary_string The input string
277
+ * @param int $start
278
+ * @param int $length
279
+ *
280
+ * @internal
281
+ * @return string The substring
282
+ */
283
+ function _substr ($ binary_string , $ start , $ length ) {
284
+ if (function_exists ('mb_substr ' )) {
285
+ return mb_substr ($ binary_string , $ start , $ length , '8bit ' );
286
+ }
287
+ return substr ($ binary_string , $ start , $ length );
288
+ }
289
+ }
290
+ }
291
+
292
+ // namespace PasswordCompat\binary {
293
+ // /**
294
+ // * Count the number of bytes in a string
295
+ // *
296
+ // * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension.
297
+ // * In this case, strlen() will count the number of *characters* based on the internal encoding. A
298
+ // * sequence of bytes might be regarded as a single multibyte character.
299
+ // *
300
+ // * @param string $binary_string The input string
301
+ // *
302
+ // * @internal
303
+ // * @return int The number of bytes
304
+ // */
305
+ // function _strlen($binary_string) {
306
+ // if (function_exists('mb_strlen')) {
307
+ // return mb_strlen($binary_string, '8bit');
308
+ // }
309
+ // return strlen($binary_string);
310
+ // }
311
+
312
+ // /**
313
+ // * Get a substring based on byte limits
314
+ // *
315
+ // * @see _strlen()
316
+ // *
317
+ // * @param string $binary_string The input string
318
+ // * @param int $start
319
+ // * @param int $length
320
+ // *
321
+ // * @internal
322
+ // * @return string The substring
323
+ // */
324
+ // function _substr($binary_string, $start, $length) {
325
+ // if (function_exists('mb_substr')) {
326
+ // return mb_substr($binary_string, $start, $length, '8bit');
327
+ // }
328
+ // return substr($binary_string, $start, $length);
329
+ // }
330
+
331
+ // }
0 commit comments