Mercurial > hg > vamp-website
comparison forum/Sources/Subs-OpenID.php @ 76:e3e11437ecea website
Add forum code
author | Chris Cannam |
---|---|
date | Sun, 07 Jul 2013 11:25:48 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
75:72f59aa7e503 | 76:e3e11437ecea |
---|---|
1 <?php | |
2 | |
3 /** | |
4 * Simple Machines Forum (SMF) | |
5 * | |
6 * @package SMF | |
7 * @author Simple Machines http://www.simplemachines.org | |
8 * @copyright 2011 Simple Machines | |
9 * @license http://www.simplemachines.org/about/smf/license.php BSD | |
10 * | |
11 * @version 2.0 | |
12 */ | |
13 | |
14 if (!defined('SMF')) | |
15 die('Hacking attempt...'); | |
16 | |
17 /* This file handles all of the OpenID interfacing and communications. | |
18 void smf_openID_validate(string openid_url, bool allow_immediate_validation = true) | |
19 - openid_uri is the URI given by the user | |
20 - Validates the URI and changes it to a fully canonicalize URL | |
21 - Determines the IDP server and delegation | |
22 - optional array of fields to restore when validation complete. | |
23 - Redirects the user to the IDP for validation | |
24 */ | |
25 | |
26 function smf_openID_validate($openid_uri, $return = false, $save_fields = array(), $return_action = null) | |
27 { | |
28 global $sourcedir, $scripturl, $boardurl, $modSettings; | |
29 | |
30 $openid_url = smf_openID_canonize($openid_uri); | |
31 | |
32 $response_data = smf_openID_getServerInfo($openid_url); | |
33 if ($response_data === false) | |
34 return 'no_data'; | |
35 | |
36 if (($assoc = smf_openID_getAssociation($response_data['server'])) == null) | |
37 $assoc = smf_openID_makeAssociation($response_data['server']); | |
38 | |
39 // Before we go wherever it is we are going, store the GET and POST data, because it might be useful when we get back. | |
40 $request_time = time(); | |
41 // Just in case they are doing something else at this time. | |
42 while (isset($_SESSION['openid']['saved_data'][$request_time])) | |
43 $request_time = md5($request_time); | |
44 | |
45 $_SESSION['openid']['saved_data'][$request_time] = array( | |
46 'get' => $_GET, | |
47 'post' => $_POST, | |
48 'openid_uri' => $openid_url, | |
49 'cookieTime' => $modSettings['cookieTime'], | |
50 ); | |
51 | |
52 $parameters = array( | |
53 'openid.mode=checkid_setup', | |
54 'openid.trust_root=' . urlencode($scripturl), | |
55 'openid.identity=' . urlencode(empty($response_data['delegate']) ? $openid_url : $response_data['delegate']), | |
56 'openid.assoc_handle=' . urlencode($assoc['handle']), | |
57 'openid.return_to=' . urlencode($scripturl . '?action=openidreturn&sa=' . (!empty($return_action) ? $return_action : $_REQUEST['action']) . '&t=' . $request_time . (!empty($save_fields) ? '&sf=' . base64_encode(serialize($save_fields)) : '')), | |
58 ); | |
59 | |
60 // If they are logging in but don't yet have an account or they are registering, let's request some additional information | |
61 if (($_REQUEST['action'] == 'login2' && !smf_openid_member_exists($openid_url)) || ($_REQUEST['action'] == 'register' || $_REQUEST['action'] == 'register2')) | |
62 { | |
63 // Email is required. | |
64 $parameters[] = 'openid.sreg.required=email'; | |
65 // The rest is just optional. | |
66 $parameters[] = 'openid.sreg.optional=nickname,dob,gender'; | |
67 } | |
68 | |
69 $redir_url = $response_data['server'] . '?' . implode('&', $parameters); | |
70 | |
71 if ($return) | |
72 return $redir_url; | |
73 else | |
74 redirectexit($redir_url); | |
75 } | |
76 | |
77 // Revalidate a user using OpenID. Note that this function will not return when authentication is required. | |
78 function smf_openID_revalidate() | |
79 { | |
80 global $user_settings; | |
81 | |
82 if (isset($_SESSION['openid_revalidate_time']) && $_SESSION['openid_revalidate_time'] > time() - 60) | |
83 { | |
84 unset($_SESSION['openid_revalidate_time']); | |
85 return true; | |
86 } | |
87 else | |
88 smf_openID_validate($user_settings['openid_uri'], false, null, 'revalidate'); | |
89 | |
90 // We shouldn't get here. | |
91 trigger_error('Hacking attempt...', E_USER_ERROR); | |
92 } | |
93 | |
94 function smf_openID_getAssociation($server, $handle = null, $no_delete = false) | |
95 { | |
96 global $smcFunc; | |
97 | |
98 if (!$no_delete) | |
99 { | |
100 // Delete the already expired associations. | |
101 $smcFunc['db_query']('openid_delete_assoc_old', ' | |
102 DELETE FROM {db_prefix}openid_assoc | |
103 WHERE expires <= {int:current_time}', | |
104 array( | |
105 'current_time' => time(), | |
106 ) | |
107 ); | |
108 } | |
109 | |
110 // Get the association that has the longest lifetime from now. | |
111 $request = $smcFunc['db_query']('openid_select_assoc', ' | |
112 SELECT server_url, handle, secret, issued, expires, assoc_type | |
113 FROM {db_prefix}openid_assoc | |
114 WHERE server_url = {string:server_url}' . ($handle === null ? '' : ' | |
115 AND handle = {string:handle}') . ' | |
116 ORDER BY expires DESC', | |
117 array( | |
118 'server_url' => $server, | |
119 'handle' => $handle, | |
120 ) | |
121 ); | |
122 | |
123 if ($smcFunc['db_num_rows']($request) == 0) | |
124 return null; | |
125 | |
126 $return = $smcFunc['db_fetch_assoc']($request); | |
127 $smcFunc['db_free_result']($request); | |
128 | |
129 return $return; | |
130 } | |
131 | |
132 function smf_openID_makeAssociation($server) | |
133 { | |
134 global $smcFunc, $modSettings, $p; | |
135 | |
136 $parameters = array( | |
137 'openid.mode=associate', | |
138 ); | |
139 | |
140 // We'll need to get our keys for the Diffie-Hellman key exchange. | |
141 $dh_keys = smf_openID_setup_DH(); | |
142 | |
143 // If we don't support DH we'll have to see if the provider will accept no encryption. | |
144 if ($dh_keys === false) | |
145 $parameters[] = 'openid.session_type='; | |
146 else | |
147 { | |
148 $parameters[] = 'openid.session_type=DH-SHA1'; | |
149 $parameters[] = 'openid.dh_consumer_public=' . urlencode(base64_encode(long_to_binary($dh_keys['public']))); | |
150 $parameters[] = 'openid.assoc_type=HMAC-SHA1'; | |
151 } | |
152 | |
153 // The data to post to the server. | |
154 $post_data = implode('&', $parameters); | |
155 $data = fetch_web_data($server, $post_data); | |
156 | |
157 // Parse the data given. | |
158 preg_match_all('~^([^:]+):(.+)$~m', $data, $matches); | |
159 $assoc_data = array(); | |
160 | |
161 foreach ($matches[1] as $key => $match) | |
162 $assoc_data[$match] = $matches[2][$key]; | |
163 | |
164 if (!isset($assoc_data['assoc_type']) || (empty($assoc_data['mac_key']) && empty($assoc_data['enc_mac_key']))) | |
165 fatal_lang_error('openid_server_bad_response'); | |
166 | |
167 // Clean things up a bit. | |
168 $handle = isset($assoc_data['assoc_handle']) ? $assoc_data['assoc_handle'] : ''; | |
169 $issued = time(); | |
170 $expires = $issued + min((int)$assoc_data['expires_in'], 60); | |
171 $assoc_type = isset($assoc_data['assoc_type']) ? $assoc_data['assoc_type'] : ''; | |
172 | |
173 // !!! Is this really needed? | |
174 foreach (array('dh_server_public', 'enc_mac_key') as $key) | |
175 if (isset($assoc_data[$key])) | |
176 $assoc_data[$key] = str_replace(' ', '+', $assoc_data[$key]); | |
177 | |
178 // Figure out the Diffie-Hellman secret. | |
179 if (!empty($assoc_data['enc_mac_key'])) | |
180 { | |
181 $dh_secret = bcpowmod(binary_to_long(base64_decode($assoc_data['dh_server_public'])), $dh_keys['private'], $p); | |
182 $secret = base64_encode(binary_xor(sha1_raw(long_to_binary($dh_secret)), base64_decode($assoc_data['enc_mac_key']))); | |
183 } | |
184 else | |
185 $secret = $assoc_data['mac_key']; | |
186 | |
187 // Store the data | |
188 $smcFunc['db_insert']('replace', | |
189 '{db_prefix}openid_assoc', | |
190 array('server_url' => 'string', 'handle' => 'string', 'secret' => 'string', 'issued' => 'int', 'expires' => 'int', 'assoc_type' => 'string'), | |
191 array($server, $handle, $secret, $issued, $expires, $assoc_type), | |
192 array('server_url', 'handle') | |
193 ); | |
194 | |
195 return array( | |
196 'server' => $server, | |
197 'handle' => $assoc_data['assoc_handle'], | |
198 'secret' => $secret, | |
199 'issued' => $issued, | |
200 'expires' => $expires, | |
201 'assoc_type' => $assoc_data['assoc_type'], | |
202 ); | |
203 } | |
204 | |
205 function smf_openID_removeAssociation($handle) | |
206 { | |
207 global $smcFunc; | |
208 | |
209 $smcFunc['db_query']('openid_remove_association', ' | |
210 DELETE FROM {db_prefix}openid_assoc | |
211 WHERE handle = {string:handle}', | |
212 array( | |
213 'handle' => $handle, | |
214 ) | |
215 ); | |
216 } | |
217 | |
218 function smf_openID_return() | |
219 { | |
220 global $smcFunc, $user_info, $user_profile, $sourcedir, $modSettings, $context, $sc, $user_settings; | |
221 | |
222 // Is OpenID even enabled? | |
223 if (empty($modSettings['enableOpenID'])) | |
224 fatal_lang_error('no_access', false); | |
225 | |
226 if (!isset($_GET['openid_mode'])) | |
227 fatal_lang_error('openid_return_no_mode', false); | |
228 | |
229 // !!! Check for error status! | |
230 if ($_GET['openid_mode'] != 'id_res') | |
231 fatal_lang_error('openid_not_resolved'); | |
232 | |
233 // SMF has this annoying habit of removing the + from the base64 encoding. So lets put them back. | |
234 foreach (array('openid_assoc_handle', 'openid_invalidate_handle', 'openid_sig', 'sf') as $key) | |
235 if (isset($_GET[$key])) | |
236 $_GET[$key] = str_replace(' ', '+', $_GET[$key]); | |
237 | |
238 // Did they tell us to remove any associations? | |
239 if (!empty($_GET['openid_invalidate_handle'])) | |
240 smf_openid_removeAssociation($_GET['openid_invalidate_handle']); | |
241 | |
242 $server_info = smf_openid_getServerInfo($_GET['openid_identity']); | |
243 | |
244 // Get the association data. | |
245 $assoc = smf_openID_getAssociation($server_info['server'], $_GET['openid_assoc_handle'], true); | |
246 if ($assoc === null) | |
247 fatal_lang_error('openid_no_assoc'); | |
248 | |
249 $secret = base64_decode($assoc['secret']); | |
250 | |
251 $signed = explode(',', $_GET['openid_signed']); | |
252 $verify_str = ''; | |
253 foreach ($signed as $sign) | |
254 { | |
255 $verify_str .= $sign . ':' . strtr($_GET['openid_' . str_replace('.', '_', $sign)], array('&' => '&')) . "\n"; | |
256 } | |
257 | |
258 $verify_str = base64_encode(sha1_hmac($verify_str, $secret)); | |
259 | |
260 if ($verify_str != $_GET['openid_sig']) | |
261 { | |
262 fatal_lang_error('openid_sig_invalid', 'critical'); | |
263 } | |
264 | |
265 if (!isset($_SESSION['openid']['saved_data'][$_GET['t']])) | |
266 fatal_lang_error('openid_load_data'); | |
267 | |
268 $openid_uri = $_SESSION['openid']['saved_data'][$_GET['t']]['openid_uri']; | |
269 $modSettings['cookieTime'] = $_SESSION['openid']['saved_data'][$_GET['t']]['cookieTime']; | |
270 | |
271 if (empty($openid_uri)) | |
272 fatal_lang_error('openid_load_data'); | |
273 | |
274 // Any save fields to restore? | |
275 $context['openid_save_fields'] = isset($_GET['sf']) ? unserialize(base64_decode($_GET['sf'])) : array(); | |
276 | |
277 // Is there a user with this OpenID_uri? | |
278 $result = $smcFunc['db_query']('', ' | |
279 SELECT passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt, | |
280 openid_uri | |
281 FROM {db_prefix}members | |
282 WHERE openid_uri = {string:openid_uri}', | |
283 array( | |
284 'openid_uri' => $openid_uri, | |
285 ) | |
286 ); | |
287 | |
288 $member_found = $smcFunc['db_num_rows']($result); | |
289 | |
290 if (!$member_found && isset($_GET['sa']) && $_GET['sa'] == 'change_uri' && !empty($_SESSION['new_openid_uri']) && $_SESSION['new_openid_uri'] == $openid_uri) | |
291 { | |
292 // Update the member. | |
293 updateMemberData($user_settings['id_member'], array('openid_uri' => $openid_uri)); | |
294 | |
295 unset($_SESSION['new_openid_uri']); | |
296 $_SESSION['openid'] = array( | |
297 'verified' => true, | |
298 'openid_uri' => $openid_uri, | |
299 ); | |
300 | |
301 // Send them back to profile. | |
302 redirectexit('action=profile;area=authentication;updated'); | |
303 } | |
304 elseif (!$member_found) | |
305 { | |
306 // Store the received openid info for the user when returned to the registration page. | |
307 $_SESSION['openid'] = array( | |
308 'verified' => true, | |
309 'openid_uri' => $openid_uri, | |
310 ); | |
311 if (isset($_GET['openid_sreg_nickname'])) | |
312 $_SESSION['openid']['nickname'] = $_GET['openid_sreg_nickname']; | |
313 if (isset($_GET['openid_sreg_email'])) | |
314 $_SESSION['openid']['email'] = $_GET['openid_sreg_email']; | |
315 if (isset($_GET['openid_sreg_dob'])) | |
316 $_SESSION['openid']['dob'] = $_GET['openid_sreg_dob']; | |
317 if (isset($_GET['openid_sreg_gender'])) | |
318 $_SESSION['openid']['gender'] = $_GET['openid_sreg_gender']; | |
319 | |
320 // Were we just verifying the registration state? | |
321 if (isset($_GET['sa']) && $_GET['sa'] == 'register2') | |
322 { | |
323 require_once($sourcedir . '/Register.php'); | |
324 return Register2(true); | |
325 } | |
326 else | |
327 redirectexit('action=register'); | |
328 } | |
329 elseif (isset($_GET['sa']) && $_GET['sa'] == 'revalidate' && $user_settings['openid_uri'] == $openid_uri) | |
330 { | |
331 $_SESSION['openid_revalidate_time'] = time(); | |
332 | |
333 // Restore the get data. | |
334 require_once($sourcedir . '/Subs-Auth.php'); | |
335 $_SESSION['openid']['saved_data'][$_GET['t']]['get']['openid_restore_post'] = $_GET['t']; | |
336 $query_string = construct_query_string($_SESSION['openid']['saved_data'][$_GET['t']]['get']); | |
337 | |
338 redirectexit($query_string); | |
339 } | |
340 else | |
341 { | |
342 $user_settings = $smcFunc['db_fetch_assoc']($result); | |
343 $smcFunc['db_free_result']($result); | |
344 | |
345 $user_settings['passwd'] = sha1(strtolower($user_settings['member_name']) . $secret); | |
346 $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4); | |
347 | |
348 updateMemberData($user_settings['id_member'], array('passwd' => $user_settings['passwd'], 'password_salt' => $user_settings['password_salt'])); | |
349 | |
350 // Cleanup on Aisle 5. | |
351 $_SESSION['openid'] = array( | |
352 'verified' => true, | |
353 'openid_uri' => $openid_uri, | |
354 ); | |
355 | |
356 require_once($sourcedir . '/LogInOut.php'); | |
357 | |
358 if (!checkActivation()) | |
359 return; | |
360 | |
361 DoLogin(); | |
362 } | |
363 } | |
364 | |
365 function smf_openID_canonize($uri) | |
366 { | |
367 // !!! Add in discovery. | |
368 | |
369 if (strpos($uri, 'http://') !== 0 && strpos($uri, 'https://') !== 0) | |
370 $uri = 'http://' . $uri; | |
371 | |
372 if (strpos(substr($uri, strpos($uri, '://') + 3), '/') === false) | |
373 $uri .= '/'; | |
374 | |
375 return $uri; | |
376 } | |
377 | |
378 function smf_openid_member_exists($url) | |
379 { | |
380 global $smcFunc; | |
381 | |
382 $request = $smcFunc['db_query']('openid_member_exists', ' | |
383 SELECT mem.id_member, mem.member_name | |
384 FROM {db_prefix}members AS mem | |
385 WHERE mem.openid_uri = {string:openid_uri}', | |
386 array( | |
387 'openid_uri' => $url, | |
388 ) | |
389 ); | |
390 $member = $smcFunc['db_fetch_assoc']($request); | |
391 $smcFunc['db_free_result']($request); | |
392 | |
393 return $member; | |
394 } | |
395 | |
396 // Prepare for a Diffie-Hellman key exchange. | |
397 function smf_openID_setup_DH($regenerate = false) | |
398 { | |
399 global $p, $g; | |
400 | |
401 // First off, do we have BC Math available? | |
402 if (!function_exists('bcpow')) | |
403 return false; | |
404 | |
405 // Defined in OpenID spec. | |
406 $p = '155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443'; | |
407 $g = '2'; | |
408 | |
409 // Make sure the scale is set. | |
410 bcscale(0); | |
411 | |
412 return smf_openID_get_keys($regenerate); | |
413 } | |
414 | |
415 function smf_openID_get_keys($regenerate) | |
416 { | |
417 global $modSettings, $p, $g; | |
418 | |
419 // Ok lets take the easy way out, are their any keys already defined for us? They are changed in the daily maintenance scheduled task. | |
420 if (!empty($modSettings['dh_keys']) && !$regenerate) | |
421 { | |
422 // Sweeeet! | |
423 list ($public, $private) = explode("\n", $modSettings['dh_keys']); | |
424 return array( | |
425 'public' => base64_decode($public), | |
426 'private' => base64_decode($private), | |
427 ); | |
428 } | |
429 | |
430 // Dang it, now I have to do math. And it's not just ordinary math, its the evil big interger math. This will take a few seconds. | |
431 $private = smf_openid_generate_private_key(); | |
432 $public = bcpowmod($g, $private, $p); | |
433 | |
434 // Now that we did all that work, lets save it so we don't have to keep doing it. | |
435 $keys = array('dh_keys' => base64_encode($public) . "\n" . base64_encode($private)); | |
436 updateSettings($keys); | |
437 | |
438 return array( | |
439 'public' => $public, | |
440 'private' => $private, | |
441 ); | |
442 } | |
443 | |
444 function smf_openid_generate_private_key() | |
445 { | |
446 global $p; | |
447 static $cache = array(); | |
448 | |
449 $byte_string = long_to_binary($p); | |
450 | |
451 if (isset($cache[$byte_string])) | |
452 list ($dup, $num_bytes) = $cache[$byte_string]; | |
453 else | |
454 { | |
455 $num_bytes = strlen($byte_string) - ($byte_string[0] == "\x00" ? 1 : 0); | |
456 | |
457 $max_rand = bcpow(256, $num_bytes); | |
458 | |
459 $dup = bcmod($max_rand, $num_bytes); | |
460 | |
461 $cache[$byte_string] = array($dup, $num_bytes); | |
462 } | |
463 | |
464 do | |
465 { | |
466 $str = ''; | |
467 for ($i = 0; $i < $num_bytes; $i += 4) | |
468 $str .= pack('L', mt_rand()); | |
469 | |
470 $bytes = "\x00" . $str; | |
471 | |
472 $num = binary_to_long($bytes); | |
473 } while (bccomp($num, $dup) < 0); | |
474 | |
475 return bcadd(bcmod($num, $p), 1); | |
476 } | |
477 | |
478 function smf_openID_getServerInfo($openid_url) | |
479 { | |
480 global $sourcedir; | |
481 | |
482 require_once($sourcedir . '/Subs-Package.php'); | |
483 | |
484 // Get the html and parse it for the openid variable which will tell us where to go. | |
485 $webdata = fetch_web_data($openid_url); | |
486 | |
487 if (empty($webdata)) | |
488 return false; | |
489 | |
490 $response_data = array(); | |
491 | |
492 // Some OpenID servers have strange but still valid HTML which makes our job hard. | |
493 if (preg_match_all('~<link([\s\S]*?)/?>~i', $webdata, $link_matches) == 0) | |
494 fatal_lang_error('openid_server_bad_response'); | |
495 | |
496 foreach ($link_matches[1] as $link_match) | |
497 { | |
498 if (preg_match('~rel="([\s\S]*?)"~i', $link_match, $rel_match) == 0 || preg_match('~href="([\s\S]*?)"~i', $link_match, $href_match) == 0) | |
499 continue; | |
500 | |
501 $rels = preg_split('~\s+~', $rel_match[1]); | |
502 foreach ($rels as $rel) | |
503 if (preg_match('~openid2?\.(server|delegate|provider)~i', $rel, $match) != 0) | |
504 $response_data[$match[1]] = $href_match[1]; | |
505 } | |
506 | |
507 if (empty($response_data['server'])) | |
508 if (empty($response_data['provider'])) | |
509 fatal_lang_error('openid_server_bad_response'); | |
510 else | |
511 $response_data['server'] = $response_data['provider']; | |
512 | |
513 return $response_data; | |
514 } | |
515 | |
516 function sha1_hmac($data, $key) | |
517 { | |
518 | |
519 if (strlen($key) > 64) | |
520 $key = sha1_raw($key); | |
521 | |
522 // Pad the key if need be. | |
523 $key = str_pad($key, 64, chr(0x00)); | |
524 $ipad = str_repeat(chr(0x36), 64); | |
525 $opad = str_repeat(chr(0x5c), 64); | |
526 $hash1 = sha1_raw(($key ^ $ipad) . $data); | |
527 $hmac = sha1_raw(($key ^ $opad) . $hash1); | |
528 return $hmac; | |
529 } | |
530 | |
531 function sha1_raw($text) | |
532 { | |
533 if (version_compare(PHP_VERSION, '5.0.0') >= 0) | |
534 return sha1($text, true); | |
535 | |
536 $hex = sha1($text); | |
537 $raw = ''; | |
538 for ($i = 0; $i < 40; $i += 2) | |
539 { | |
540 $hexcode = substr($hex, $i, 2); | |
541 $charcode = (int) base_convert($hexcode, 16, 10); | |
542 $raw .= chr($charcode); | |
543 } | |
544 | |
545 return $raw; | |
546 } | |
547 | |
548 function binary_to_long($str) | |
549 { | |
550 $bytes = array_merge(unpack('C*', $str)); | |
551 | |
552 $n = 0; | |
553 | |
554 foreach ($bytes as $byte) | |
555 { | |
556 $n = bcmul($n, 256); | |
557 $n = bcadd($n, $byte); | |
558 } | |
559 | |
560 return $n; | |
561 } | |
562 | |
563 function long_to_binary($value) | |
564 { | |
565 $cmp = bccomp($value, 0); | |
566 if ($cmp < 0) | |
567 fatal_error('Only non-negative integers allowed.'); | |
568 | |
569 if ($cmp == 0) | |
570 return "\x00"; | |
571 | |
572 $bytes = array(); | |
573 | |
574 while (bccomp($value, 0) > 0) | |
575 { | |
576 array_unshift($bytes, bcmod($value, 256)); | |
577 $value = bcdiv($value, 256); | |
578 } | |
579 | |
580 if ($bytes && ($bytes[0] > 127)) | |
581 array_unshift($bytes, 0); | |
582 | |
583 $return = ''; | |
584 foreach ($bytes as $byte) | |
585 $return .= pack('C', $byte); | |
586 | |
587 return $return; | |
588 } | |
589 | |
590 function binary_xor($num1, $num2) | |
591 { | |
592 $return = ''; | |
593 | |
594 for ($i = 0; $i < strlen($num2); $i++) | |
595 $return .= $num1[$i] ^ $num2[$i]; | |
596 | |
597 return $return; | |
598 } | |
599 | |
600 // PHP 4 didn't have bcpowmod. | |
601 if (!function_exists('bcpowmod') && function_exists('bcpow')) | |
602 { | |
603 function bcpowmod($num1, $num2, $num3) | |
604 { | |
605 return bcmod(bcpow($num1, $num2), $num3); | |
606 } | |
607 } | |
608 | |
609 ?> |