00001 <?php
00002
00026 require_once "Zend/Controller/Response/Abstract.php";
00027
00041 class Zend_OpenId
00042 {
00046 const DH_P = 'dcf93a0b883972ec0e19989ac5a2ce310e1d37717e8d9571bb7623731866e61ef75a2e27898b057f9891c2e27a639c3f29b60814581cd3b2ca3986d2683705577d45c2e7e52dc81c7a171876e5cea74b1448bfdfaf18828efd2519f14e45e3826634af1949e5b535cc829a483b8a76223e5d490a257f05bdff16f2fb22c583ab';
00047
00051 const DH_G = '02';
00052
00057 const NS_2_0 = 'http://specs.openid.net/auth/2.0';
00058
00062 static public $exitOnRedirect = true;
00063
00068 static public $selfUrl = null;
00069
00077 static public function setSelfUrl($selfUrl = null)
00078 {
00079 $ret = self::$selfUrl;
00080 self::$selfUrl = $selfUrl;
00081 return $ret;
00082 }
00083
00089 static public function selfUrl()
00090 {
00091 if (self::$selfUrl !== null) {
00092 return self::$selfUrl;
00093 } if (isset($_SERVER['SCRIPT_URI'])) {
00094 return $_SERVER['SCRIPT_URI'];
00095 }
00096 $url = '';
00097 $port = '';
00098 if (isset($_SERVER['HTTP_HOST'])) {
00099 if (($pos = strpos($_SERVER['HTTP_HOST'], ':')) === false) {
00100 if (isset($_SERVER['SERVER_PORT'])) {
00101 $port = ':' . $_SERVER['SERVER_PORT'];
00102 }
00103 $url = $_SERVER['HTTP_HOST'];
00104 } else {
00105 $url = substr($_SERVER['HTTP_HOST'], 0, $pos);
00106 $port = substr($_SERVER['HTTP_HOST'], $pos);
00107 }
00108 } else if (isset($_SERVER['SERVER_NAME'])) {
00109 $url = $_SERVER['SERVER_NAME'];
00110 if (isset($_SERVER['SERVER_PORT'])) {
00111 $port = ':' . $_SERVER['SERVER_PORT'];
00112 }
00113 }
00114 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
00115 $url = 'https://' . $url;
00116 if ($port == ':443') {
00117 $port = '';
00118 }
00119 } else {
00120 $url = 'http://' . $url;
00121 if ($port == ':80') {
00122 $port = '';
00123 }
00124 }
00125
00126 $url .= $port;
00127 if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
00128 $url .= $_SERVER['HTTP_X_REWRITE_URL'];
00129 } elseif (isset($_SERVER['REQUEST_URI'])) {
00130 $query = strpos($_SERVER['REQUEST_URI'], '?');
00131 if ($query === false) {
00132 $url .= $_SERVER['REQUEST_URI'];
00133 } else {
00134 $url .= substr($_SERVER['REQUEST_URI'], 0, $query);
00135 }
00136 } else if (isset($_SERVER['SCRIPT_URL'])) {
00137 $url .= $_SERVER['SCRIPT_URL'];
00138 } else if (isset($_SERVER['REDIRECT_URL'])) {
00139 $url .= $_SERVER['REDIRECT_URL'];
00140 } else if (isset($_SERVER['PHP_SELF'])) {
00141 $url .= $_SERVER['PHP_SELF'];
00142 } else if (isset($_SERVER['SCRIPT_NAME'])) {
00143 $url .= $_SERVER['SCRIPT_NAME'];
00144 if (isset($_SERVER['PATH_INFO'])) {
00145 $url .= $_SERVER['PATH_INFO'];
00146 }
00147 }
00148 return $url;
00149 }
00150
00157 static public function absoluteUrl($url)
00158 {
00159 if (empty($url)) {
00160 return Zend_OpenId::selfUrl();
00161 } else if (!preg_match('|^([^:]+)://|', $url)) {
00162 if (preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?]*)?((?:[?](?:[^#]*))?(?:#.*)?)$|', Zend_OpenId::selfUrl(), $reg)) {
00163 $scheme = $reg[1];
00164 $auth = $reg[2];
00165 $host = $reg[3];
00166 $port = $reg[4];
00167 $path = $reg[5];
00168 $query = $reg[6];
00169 if ($url[0] == '/') {
00170 return $scheme
00171 . '://'
00172 . $auth
00173 . $host
00174 . (empty($port) ? '' : (':' . $port))
00175 . $url;
00176 } else {
00177 $dir = dirname($path);
00178 return $scheme
00179 . '://'
00180 . $auth
00181 . $host
00182 . (empty($port) ? '' : (':' . $port))
00183 . (strlen($dir) > 1 ? $dir : '')
00184 . '/'
00185 . $url;
00186 }
00187 }
00188 }
00189 return $url;
00190 }
00191
00198 static public function paramsToQuery($params)
00199 {
00200 foreach($params as $key => $value) {
00201 if (isset($query)) {
00202 $query .= '&' . $key . '=' . urlencode($value);
00203 } else {
00204 $query = $key . '=' . urlencode($value);
00205 }
00206 }
00207 return isset($query) ? $query : '';
00208 }
00209
00218 static public function normalizeUrl(&$id)
00219 {
00220
00221
00222
00223 $i = 0;
00224 $n = strlen($id);
00225 $res = '';
00226 while ($i < $n) {
00227 if ($id[$i] == '%') {
00228 if ($i + 2 >= $n) {
00229 return false;
00230 }
00231 ++$i;
00232 if ($id[$i] >= '0' && $id[$i] <= '9') {
00233 $c = ord($id[$i]) - ord('0');
00234 } else if ($id[$i] >= 'A' && $id[$i] <= 'F') {
00235 $c = ord($id[$i]) - ord('A') + 10;
00236 } else if ($id[$i] >= 'a' && $id[$i] <= 'f') {
00237 $c = ord($id[$i]) - ord('a') + 10;
00238 } else {
00239 return false;
00240 }
00241 ++$i;
00242 if ($id[$i] >= '0' && $id[$i] <= '9') {
00243 $c = ($c << 4) | (ord($id[$i]) - ord('0'));
00244 } else if ($id[$i] >= 'A' && $id[$i] <= 'F') {
00245 $c = ($c << 4) | (ord($id[$i]) - ord('A') + 10);
00246 } else if ($id[$i] >= 'a' && $id[$i] <= 'f') {
00247 $c = ($c << 4) | (ord($id[$i]) - ord('a') + 10);
00248 } else {
00249 return false;
00250 }
00251 ++$i;
00252 $ch = chr($c);
00253 if (($ch >= 'A' && $ch <= 'Z') ||
00254 ($ch >= 'a' && $ch <= 'z') ||
00255 $ch == '-' ||
00256 $ch == '.' ||
00257 $ch == '_' ||
00258 $ch == '~') {
00259 $res .= $ch;
00260 } else {
00261 $res .= '%';
00262 if (($c >> 4) < 10) {
00263 $res .= chr(($c >> 4) + ord('0'));
00264 } else {
00265 $res .= chr(($c >> 4) - 10 + ord('A'));
00266 }
00267 $c = $c & 0xf;
00268 if ($c < 10) {
00269 $res .= chr($c + ord('0'));
00270 } else {
00271 $res .= chr($c - 10 + ord('A'));
00272 }
00273 }
00274 } else {
00275 $res .= $id[$i++];
00276 }
00277 }
00278
00279 if (!preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?#]*)?((?:[?](?:[^#]*))?)((?:#.*)?)$|', $res, $reg)) {
00280 return false;
00281 }
00282 $scheme = $reg[1];
00283 $auth = $reg[2];
00284 $host = $reg[3];
00285 $port = $reg[4];
00286 $path = $reg[5];
00287 $query = $reg[6];
00288 $fragment = $reg[7];
00289
00290 if (empty($scheme) || empty($host)) {
00291 return false;
00292 }
00293
00294
00295 $scheme = strtolower($scheme);
00296 $host = strtolower($host);
00297
00298
00299 if (!empty($path)) {
00300 $i = 0;
00301 $n = strlen($path);
00302 $res = "";
00303 while ($i < $n) {
00304 if ($path[$i] == '/') {
00305 ++$i;
00306 while ($i < $n && $path[$i] == '/') {
00307 ++$i;
00308 }
00309 if ($i < $n && $path[$i] == '.') {
00310 ++$i;
00311 if ($i < $n && $path[$i] == '.') {
00312 ++$i;
00313 if ($i == $n || $path[$i] == '/') {
00314 if (($pos = strrpos($res, '/')) !== false) {
00315 $res = substr($res, 0, $pos);
00316 }
00317 } else {
00318 $res .= '/..';
00319 }
00320 } else if ($i != $n && $path[$i] != '/') {
00321 $res .= '/.';
00322 }
00323 } else {
00324 $res .= '/';
00325 }
00326 } else {
00327 $res .= $path[$i++];
00328 }
00329 }
00330 $path = $res;
00331 }
00332
00333
00334 if ($scheme == 'http') {
00335 if ($port == 80) {
00336 $port = '';
00337 }
00338 } else if ($scheme == 'https') {
00339 if ($port == 443) {
00340 $port = '';
00341 }
00342 }
00343 if (empty($path)) {
00344 $path = '/';
00345 }
00346
00347 $id = $scheme
00348 . '://'
00349 . $auth
00350 . $host
00351 . (empty($port) ? '' : (':' . $port))
00352 . $path
00353 . $query;
00354 return true;
00355 }
00356
00378 static public function normalize(&$id)
00379 {
00380 $id = trim($id);
00381 if (strlen($id) === 0) {
00382 return true;
00383 }
00384
00385
00386 if (strpos($id, 'xri://$ip*') === 0) {
00387 $id = substr($id, strlen('xri://$ip*'));
00388 } else if (strpos($id, 'xri://$dns*') === 0) {
00389 $id = substr($id, strlen('xri://$dns*'));
00390 } else if (strpos($id, 'xri://') === 0) {
00391 $id = substr($id, strlen('xri://'));
00392 }
00393
00394
00395 if ($id[0] == '=' ||
00396 $id[0] == '@' ||
00397 $id[0] == '+' ||
00398 $id[0] == '$' ||
00399 $id[0] == '!') {
00400 return true;
00401 }
00402
00403
00404 if (strpos($id, "://") === false) {
00405 $id = 'http://' . $id;
00406 }
00407
00408
00409 return self::normalizeURL($id);
00410 }
00411
00422 static public function redirect($url, $params = null,
00423 Zend_Controller_Response_Abstract $response = null, $method = 'GET')
00424 {
00425 $url = Zend_OpenId::absoluteUrl($url);
00426 $body = "";
00427 if (null === $response) {
00428 require_once "Zend/Controller/Response/Http.php";
00429 $response = new Zend_Controller_Response_Http();
00430 }
00431
00432 if ($method == 'POST') {
00433 $body = "<html><body onLoad=\"document.forms[0].submit();\">\n";
00434 $body .= "<form method=\"POST\" action=\"$url\">\n";
00435 if (is_array($params) && count($params) > 0) {
00436 foreach($params as $key => $value) {
00437 $body .= '<input type="hidden" name="' . $key . '" value="' . $value . "\">\n";
00438 }
00439 }
00440 $body .= "<input type=\"submit\" value=\"Continue OpenID transaction\">\n";
00441 $body .= "</form></body></html>\n";
00442 } else if (is_array($params) && count($params) > 0) {
00443 if (strpos($url, '?') === false) {
00444 $url .= '?' . self::paramsToQuery($params);
00445 } else {
00446 $url .= '&' . self::paramsToQuery($params);
00447 }
00448 }
00449 if (!empty($body)) {
00450 $response->setBody($body);
00451 } else if (!$response->canSendHeaders()) {
00452 $response->setBody("<script language=\"JavaScript\"" .
00453 " type=\"text/javascript\">window.location='$url';" .
00454 "</script>");
00455 } else {
00456 $response->setRedirect($url);
00457 }
00458 $response->sendResponse();
00459 if (self::$exitOnRedirect) {
00460 exit();
00461 }
00462 }
00463
00470 static public function randomBytes($len)
00471 {
00472 $key = '';
00473 for($i=0; $i < $len; $i++) {
00474 $key .= chr(mt_rand(0, 255));
00475 }
00476 return $key;
00477 }
00478
00492 static public function digest($func, $data)
00493 {
00494 if (function_exists('openssl_digest')) {
00495 return openssl_digest($data, $func, true);
00496 } else if (function_exists('hash')) {
00497 return hash($func, $data, true);
00498 } else if ($func === 'sha1') {
00499 return sha1($data, true);
00500 } else if ($func === 'sha256') {
00501 if (function_exists('mhash')) {
00502 return mhash(MHASH_SHA256 , $data);
00503 }
00504 }
00505 require_once "Zend/OpenId/Exception.php";
00506 throw new Zend_OpenId_Exception(
00507 'Unsupported digest algorithm "' . $func . '".',
00508 Zend_OpenId_Exception::UNSUPPORTED_DIGEST);
00509 }
00510
00522 static public function hashHmac($macFunc, $data, $secret)
00523 {
00524
00525
00526 if (function_exists('hash_hmac')) {
00527 return hash_hmac($macFunc, $data, $secret, 1);
00528 } else {
00529 if (Zend_OpenId::strlen($secret) > 64) {
00530 $secret = self::digest($macFunc, $secret);
00531 }
00532 $secret = str_pad($secret, 64, chr(0x00));
00533 $ipad = str_repeat(chr(0x36), 64);
00534 $opad = str_repeat(chr(0x5c), 64);
00535 $hash1 = self::digest($macFunc, ($secret ^ $ipad) . $data);
00536 return self::digest($macFunc, ($secret ^ $opad) . $hash1);
00537 }
00538 }
00539
00548 static protected function binToBigNum($bin)
00549 {
00550 if (extension_loaded('gmp')) {
00551 return gmp_init(bin2hex($bin), 16);
00552 } else if (extension_loaded('bcmath')) {
00553 $bn = 0;
00554 $len = Zend_OpenId::strlen($bin);
00555 for ($i = 0; $i < $len; $i++) {
00556 $bn = bcmul($bn, 256);
00557 $bn = bcadd($bn, ord($bin[$i]));
00558 }
00559 return $bn;
00560 }
00561 require_once "Zend/OpenId/Exception.php";
00562 throw new Zend_OpenId_Exception(
00563 'The system doesn\'t have proper big integer extension',
00564 Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH);
00565 }
00566
00575 static protected function bigNumToBin($bn)
00576 {
00577 if (extension_loaded('gmp')) {
00578 $s = gmp_strval($bn, 16);
00579 if (strlen($s) % 2 != 0) {
00580 $s = '0' . $s;
00581 } else if ($s[0] > '7') {
00582 $s = '00' . $s;
00583 }
00584 return pack("H*", $s);
00585 } else if (extension_loaded('bcmath')) {
00586 $cmp = bccomp($bn, 0);
00587 if ($cmp == 0) {
00588 return (chr(0));
00589 } else if ($cmp < 0) {
00590 require_once "Zend/OpenId/Exception.php";
00591 throw new Zend_OpenId_Exception(
00592 'Big integer arithmetic error',
00593 Zend_OpenId_Exception::ERROR_LONG_MATH);
00594 }
00595 $bin = "";
00596 while (bccomp($bn, 0) > 0) {
00597 $bin = chr(bcmod($bn, 256)) . $bin;
00598 $bn = bcdiv($bn, 256);
00599 }
00600 if (ord($bin[0]) > 127) {
00601 $bin = chr(0) . $bin;
00602 }
00603 return $bin;
00604 }
00605 require_once "Zend/OpenId/Exception.php";
00606 throw new Zend_OpenId_Exception(
00607 'The system doesn\'t have proper big integer extension',
00608 Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH);
00609 }
00610
00623 static public function createDhKey($p, $g, $priv_key = null)
00624 {
00625 if (function_exists('openssl_dh_compute_key')) {
00626 $dh_details = array(
00627 'p' => $p,
00628 'g' => $g
00629 );
00630 if ($priv_key !== null) {
00631 $dh_details['priv_key'] = $priv_key;
00632 }
00633 return openssl_pkey_new(array('dh'=>$dh_details));
00634 } else {
00635 $bn_p = self::binToBigNum($p);
00636 $bn_g = self::binToBigNum($g);
00637 if ($priv_key === null) {
00638 $priv_key = self::randomBytes(Zend_OpenId::strlen($p));
00639 }
00640 $bn_priv_key = self::binToBigNum($priv_key);
00641 if (extension_loaded('gmp')) {
00642 $bn_pub_key = gmp_powm($bn_g, $bn_priv_key, $bn_p);
00643 } else if (extension_loaded('bcmath')) {
00644 $bn_pub_key = bcpowmod($bn_g, $bn_priv_key, $bn_p);
00645 }
00646 $pub_key = self::bigNumToBin($bn_pub_key);
00647
00648 return array(
00649 'p' => $bn_p,
00650 'g' => $bn_g,
00651 'priv_key' => $bn_priv_key,
00652 'pub_key' => $bn_pub_key,
00653 'details' => array(
00654 'p' => $p,
00655 'g' => $g,
00656 'priv_key' => $priv_key,
00657 'pub_key' => $pub_key));
00658 }
00659 }
00660
00670 static public function getDhKeyDetails($dh)
00671 {
00672 if (function_exists('openssl_dh_compute_key')) {
00673 $details = openssl_pkey_get_details($dh);
00674 if (isset($details['dh'])) {
00675 return $details['dh'];
00676 }
00677 } else {
00678 return $dh['details'];
00679 }
00680 }
00681
00691 static public function computeDhSecret($pub_key, $dh)
00692 {
00693 if (function_exists('openssl_dh_compute_key')) {
00694 $ret = openssl_dh_compute_key($pub_key, $dh);
00695 if (ord($ret[0]) > 127) {
00696 $ret = chr(0) . $ret;
00697 }
00698 return $ret;
00699 } else if (extension_loaded('gmp')) {
00700 $bn_pub_key = self::binToBigNum($pub_key);
00701 $bn_secret = gmp_powm($bn_pub_key, $dh['priv_key'], $dh['p']);
00702 return self::bigNumToBin($bn_secret);
00703 } else if (extension_loaded('bcmath')) {
00704 $bn_pub_key = self::binToBigNum($pub_key);
00705 $bn_secret = bcpowmod($bn_pub_key, $dh['priv_key'], $dh['p']);
00706 return self::bigNumToBin($bn_secret);
00707 }
00708 require_once "Zend/OpenId/Exception.php";
00709 throw new Zend_OpenId_Exception(
00710 'The system doesn\'t have proper big integer extension',
00711 Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH);
00712 }
00713
00729 static public function btwoc($str)
00730 {
00731 if (ord($str[0]) > 127) {
00732 return chr(0) . $str;
00733 }
00734 return $str;
00735 }
00736
00743 static public function strlen($str)
00744 {
00745 if (extension_loaded('mbstring') &&
00746 (((int)ini_get('mbstring.func_overload')) & 2)) {
00747 return mb_strlen($str, 'latin1');
00748 } else {
00749 return strlen($str);
00750 }
00751 }
00752
00753 }