File indexing completed on 2024-12-22 05:37:17
0001 <?php 0002 0003 /** 0004 * Zend Framework 0005 * 0006 * LICENSE 0007 * 0008 * This source file is subject to the new BSD license that is bundled 0009 * with this package in the file LICENSE.txt. 0010 * It is also available through the world-wide-web at this URL: 0011 * http://framework.zend.com/license/new-bsd 0012 * If you did not receive a copy of the license and are unable to 0013 * obtain it through the world-wide-web, please send an email 0014 * to license@zend.com so we can send you a copy immediately. 0015 * 0016 * @category Zend 0017 * @package Zend_OpenId 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** 0024 * @see Zend_Controller_Response_Abstract 0025 */ 0026 // require_once "Zend/Controller/Response/Abstract.php"; 0027 0028 /** @see Zend_Crypt_Math */ 0029 // require_once 'Zend/Crypt/Math.php'; 0030 0031 /** 0032 * Static class that contains common utility functions for 0033 * {@link Zend_OpenId_Consumer} and {@link Zend_OpenId_Provider}. 0034 * 0035 * This class implements common utility functions that are used by both 0036 * Consumer and Provider. They include functions for Diffie-Hellman keys 0037 * generation and exchange, URL normalization, HTTP redirection and some others. 0038 * 0039 * @category Zend 0040 * @package Zend_OpenId 0041 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0042 * @license http://framework.zend.com/license/new-bsd New BSD License 0043 */ 0044 class Zend_OpenId 0045 { 0046 /** 0047 * Default Diffie-Hellman key generator (1024 bit) 0048 */ 0049 const DH_P = 'dcf93a0b883972ec0e19989ac5a2ce310e1d37717e8d9571bb7623731866e61ef75a2e27898b057f9891c2e27a639c3f29b60814581cd3b2ca3986d2683705577d45c2e7e52dc81c7a171876e5cea74b1448bfdfaf18828efd2519f14e45e3826634af1949e5b535cc829a483b8a76223e5d490a257f05bdff16f2fb22c583ab'; 0050 0051 /** 0052 * Default Diffie-Hellman prime number (should be 2 or 5) 0053 */ 0054 const DH_G = '02'; 0055 0056 /** 0057 * OpenID 2.0 namespace. All OpenID 2.0 messages MUST contain variable 0058 * openid.ns with its value. 0059 */ 0060 const NS_2_0 = 'http://specs.openid.net/auth/2.0'; 0061 0062 /** 0063 * Allows enable/disable stoping execution of PHP script after redirect() 0064 */ 0065 static public $exitOnRedirect = true; 0066 0067 /** 0068 * Alternative request URL that can be used to override the default 0069 * selfUrl() response 0070 */ 0071 static public $selfUrl = null; 0072 0073 /** 0074 * Sets alternative request URL that can be used to override the default 0075 * selfUrl() response 0076 * 0077 * @param string $selfUrl the URL to be set 0078 * @return string the old value of overriding URL 0079 */ 0080 static public function setSelfUrl($selfUrl = null) 0081 { 0082 $ret = self::$selfUrl; 0083 self::$selfUrl = $selfUrl; 0084 return $ret; 0085 } 0086 0087 /** 0088 * Returns a full URL that was requested on current HTTP request. 0089 * 0090 * @return string 0091 */ 0092 static public function selfUrl() 0093 { 0094 if (self::$selfUrl !== null) { 0095 return self::$selfUrl; 0096 } if (isset($_SERVER['SCRIPT_URI'])) { 0097 return $_SERVER['SCRIPT_URI']; 0098 } 0099 $url = ''; 0100 $port = ''; 0101 if (isset($_SERVER['HTTP_HOST'])) { 0102 if (($pos = strpos($_SERVER['HTTP_HOST'], ':')) === false) { 0103 if (isset($_SERVER['SERVER_PORT'])) { 0104 $port = ':' . $_SERVER['SERVER_PORT']; 0105 } 0106 $url = $_SERVER['HTTP_HOST']; 0107 } else { 0108 $url = substr($_SERVER['HTTP_HOST'], 0, $pos); 0109 $port = substr($_SERVER['HTTP_HOST'], $pos); 0110 } 0111 } else if (isset($_SERVER['SERVER_NAME'])) { 0112 $url = $_SERVER['SERVER_NAME']; 0113 if (isset($_SERVER['SERVER_PORT'])) { 0114 $port = ':' . $_SERVER['SERVER_PORT']; 0115 } 0116 } 0117 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { 0118 $url = 'https://' . $url; 0119 if ($port == ':443') { 0120 $port = ''; 0121 } 0122 } else { 0123 $url = 'http://' . $url; 0124 if ($port == ':80') { 0125 $port = ''; 0126 } 0127 } 0128 0129 $url .= $port; 0130 if (isset($_SERVER['HTTP_X_ORIGINAL_URL'])) { 0131 // IIS with Microsoft Rewrite Module 0132 $url .= $_SERVER['HTTP_X_ORIGINAL_URL']; 0133 } elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) { 0134 // IIS with ISAPI_Rewrite 0135 $url .= $_SERVER['HTTP_X_REWRITE_URL']; 0136 } elseif (isset($_SERVER['REQUEST_URI'])) { 0137 $query = strpos($_SERVER['REQUEST_URI'], '?'); 0138 if ($query === false) { 0139 $url .= $_SERVER['REQUEST_URI']; 0140 } else { 0141 $url .= substr($_SERVER['REQUEST_URI'], 0, $query); 0142 } 0143 } else if (isset($_SERVER['SCRIPT_URL'])) { 0144 $url .= $_SERVER['SCRIPT_URL']; 0145 } else if (isset($_SERVER['REDIRECT_URL'])) { 0146 $url .= $_SERVER['REDIRECT_URL']; 0147 } else if (isset($_SERVER['PHP_SELF'])) { 0148 $url .= $_SERVER['PHP_SELF']; 0149 } else if (isset($_SERVER['SCRIPT_NAME'])) { 0150 $url .= $_SERVER['SCRIPT_NAME']; 0151 if (isset($_SERVER['PATH_INFO'])) { 0152 $url .= $_SERVER['PATH_INFO']; 0153 } 0154 } 0155 return $url; 0156 } 0157 0158 /** 0159 * Returns an absolute URL for the given one 0160 * 0161 * @param string $url absilute or relative URL 0162 * @return string 0163 */ 0164 static public function absoluteUrl($url) 0165 { 0166 if (empty($url)) { 0167 return Zend_OpenId::selfUrl(); 0168 } else if (!preg_match('|^([^:]+)://|', $url)) { 0169 if (preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?]*)?((?:[?](?:[^#]*))?(?:#.*)?)$|', Zend_OpenId::selfUrl(), $reg)) { 0170 $scheme = $reg[1]; 0171 $auth = $reg[2]; 0172 $host = $reg[3]; 0173 $port = $reg[4]; 0174 $path = $reg[5]; 0175 $query = $reg[6]; 0176 if ($url[0] == '/') { 0177 return $scheme 0178 . '://' 0179 . $auth 0180 . $host 0181 . (empty($port) ? '' : (':' . $port)) 0182 . $url; 0183 } else { 0184 $dir = dirname($path); 0185 return $scheme 0186 . '://' 0187 . $auth 0188 . $host 0189 . (empty($port) ? '' : (':' . $port)) 0190 . (strlen($dir) > 1 ? $dir : '') 0191 . '/' 0192 . $url; 0193 } 0194 } 0195 } 0196 return $url; 0197 } 0198 0199 /** 0200 * Converts variable/value pairs into URL encoded query string 0201 * 0202 * @param array $params variable/value pairs 0203 * @return string URL encoded query string 0204 */ 0205 static public function paramsToQuery($params) 0206 { 0207 foreach($params as $key => $value) { 0208 if (isset($query)) { 0209 $query .= '&' . $key . '=' . urlencode($value); 0210 } else { 0211 $query = $key . '=' . urlencode($value); 0212 } 0213 } 0214 return isset($query) ? $query : ''; 0215 } 0216 0217 /** 0218 * Normalizes URL according to RFC 3986 to use it in comparison operations. 0219 * The function gets URL argument by reference and modifies it. 0220 * It returns true on success and false of failure. 0221 * 0222 * @param string &$id url to be normalized 0223 * @return bool 0224 */ 0225 static public function normalizeUrl(&$id) 0226 { 0227 // RFC 3986, 6.2.2. Syntax-Based Normalization 0228 0229 // RFC 3986, 6.2.2.2 Percent-Encoding Normalization 0230 $i = 0; 0231 $n = strlen($id); 0232 $res = ''; 0233 while ($i < $n) { 0234 if ($id[$i] == '%') { 0235 if ($i + 2 >= $n) { 0236 return false; 0237 } 0238 ++$i; 0239 if ($id[$i] >= '0' && $id[$i] <= '9') { 0240 $c = ord($id[$i]) - ord('0'); 0241 } else if ($id[$i] >= 'A' && $id[$i] <= 'F') { 0242 $c = ord($id[$i]) - ord('A') + 10; 0243 } else if ($id[$i] >= 'a' && $id[$i] <= 'f') { 0244 $c = ord($id[$i]) - ord('a') + 10; 0245 } else { 0246 return false; 0247 } 0248 ++$i; 0249 if ($id[$i] >= '0' && $id[$i] <= '9') { 0250 $c = ($c << 4) | (ord($id[$i]) - ord('0')); 0251 } else if ($id[$i] >= 'A' && $id[$i] <= 'F') { 0252 $c = ($c << 4) | (ord($id[$i]) - ord('A') + 10); 0253 } else if ($id[$i] >= 'a' && $id[$i] <= 'f') { 0254 $c = ($c << 4) | (ord($id[$i]) - ord('a') + 10); 0255 } else { 0256 return false; 0257 } 0258 ++$i; 0259 $ch = chr($c); 0260 if (($ch >= 'A' && $ch <= 'Z') || 0261 ($ch >= 'a' && $ch <= 'z') || 0262 $ch == '-' || 0263 $ch == '.' || 0264 $ch == '_' || 0265 $ch == '~') { 0266 $res .= $ch; 0267 } else { 0268 $res .= '%'; 0269 if (($c >> 4) < 10) { 0270 $res .= chr(($c >> 4) + ord('0')); 0271 } else { 0272 $res .= chr(($c >> 4) - 10 + ord('A')); 0273 } 0274 $c = $c & 0xf; 0275 if ($c < 10) { 0276 $res .= chr($c + ord('0')); 0277 } else { 0278 $res .= chr($c - 10 + ord('A')); 0279 } 0280 } 0281 } else { 0282 $res .= $id[$i++]; 0283 } 0284 } 0285 0286 if (!preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?#]*)?((?:[?](?:[^#]*))?)((?:#.*)?)$|', $res, $reg)) { 0287 return false; 0288 } 0289 $scheme = $reg[1]; 0290 $auth = $reg[2]; 0291 $host = $reg[3]; 0292 $port = $reg[4]; 0293 $path = $reg[5]; 0294 $query = $reg[6]; 0295 $fragment = $reg[7]; /* strip it */ /* ZF-4358 Fragment retained under OpenID 2.0 */ 0296 0297 if (empty($scheme) || empty($host)) { 0298 return false; 0299 } 0300 0301 // RFC 3986, 6.2.2.1. Case Normalization 0302 $scheme = strtolower($scheme); 0303 $host = strtolower($host); 0304 0305 // RFC 3986, 6.2.2.3. Path Segment Normalization 0306 if (!empty($path)) { 0307 $i = 0; 0308 $n = strlen($path); 0309 $res = ""; 0310 while ($i < $n) { 0311 if ($path[$i] == '/') { 0312 ++$i; 0313 while ($i < $n && $path[$i] == '/') { 0314 ++$i; 0315 } 0316 if ($i < $n && $path[$i] == '.') { 0317 ++$i; 0318 if ($i < $n && $path[$i] == '.') { 0319 ++$i; 0320 if ($i == $n || $path[$i] == '/') { 0321 if (($pos = strrpos($res, '/')) !== false) { 0322 $res = substr($res, 0, $pos); 0323 } 0324 } else { 0325 $res .= '/..'; 0326 } 0327 } else if ($i != $n && $path[$i] != '/') { 0328 $res .= '/.'; 0329 } 0330 } else { 0331 $res .= '/'; 0332 } 0333 } else { 0334 $res .= $path[$i++]; 0335 } 0336 } 0337 $path = $res; 0338 } 0339 0340 // RFC 3986,6.2.3. Scheme-Based Normalization 0341 if ($scheme == 'http') { 0342 if ($port == 80) { 0343 $port = ''; 0344 } 0345 } else if ($scheme == 'https') { 0346 if ($port == 443) { 0347 $port = ''; 0348 } 0349 } 0350 if (empty($path)) { 0351 $path = '/'; 0352 } 0353 0354 $id = $scheme 0355 . '://' 0356 . $auth 0357 . $host 0358 . (empty($port) ? '' : (':' . $port)) 0359 . $path 0360 . $query 0361 . $fragment; 0362 return true; 0363 } 0364 0365 /** 0366 * Normalizes OpenID identifier that can be URL or XRI name. 0367 * Returns true on success and false of failure. 0368 * 0369 * Normalization is performed according to the following rules: 0370 * 1. If the user's input starts with one of the "xri://", "xri://$ip*", 0371 * or "xri://$dns*" prefixes, they MUST be stripped off, so that XRIs 0372 * are used in the canonical form, and URI-authority XRIs are further 0373 * considered URL identifiers. 0374 * 2. If the first character of the resulting string is an XRI Global 0375 * Context Symbol ("=", "@", "+", "$", "!"), then the input SHOULD be 0376 * treated as an XRI. 0377 * 3. Otherwise, the input SHOULD be treated as an http URL; if it does 0378 * not include a "http" or "https" scheme, the Identifier MUST be 0379 * prefixed with the string "http://". 0380 * 4. URL identifiers MUST then be further normalized by both following 0381 * redirects when retrieving their content and finally applying the 0382 * rules in Section 6 of [RFC3986] to the final destination URL. 0383 * @param string &$id identifier to be normalized 0384 * @return bool 0385 */ 0386 static public function normalize(&$id) 0387 { 0388 $id = trim($id); 0389 if (strlen($id) === 0) { 0390 return true; 0391 } 0392 0393 // 7.2.1 0394 if (strpos($id, 'xri://$ip*') === 0) { 0395 $id = substr($id, strlen('xri://$ip*')); 0396 } else if (strpos($id, 'xri://$dns*') === 0) { 0397 $id = substr($id, strlen('xri://$dns*')); 0398 } else if (strpos($id, 'xri://') === 0) { 0399 $id = substr($id, strlen('xri://')); 0400 } 0401 0402 // 7.2.2 0403 if ($id[0] == '=' || 0404 $id[0] == '@' || 0405 $id[0] == '+' || 0406 $id[0] == '$' || 0407 $id[0] == '!') { 0408 return true; 0409 } 0410 0411 // 7.2.3 0412 if (strpos($id, "://") === false) { 0413 $id = 'http://' . $id; 0414 } 0415 0416 // 7.2.4 0417 return self::normalizeURL($id); 0418 } 0419 0420 /** 0421 * Performs a HTTP redirection to specified URL with additional data. 0422 * It may generate redirected request using GET or POST HTTP method. 0423 * The function never returns. 0424 * 0425 * @param string $url URL to redirect to 0426 * @param array $params additional variable/value pairs to send 0427 * @param Zend_Controller_Response_Abstract $response 0428 * @param string $method redirection method ('GET' or 'POST') 0429 */ 0430 static public function redirect($url, $params = null, 0431 Zend_Controller_Response_Abstract $response = null, $method = 'GET') 0432 { 0433 $url = Zend_OpenId::absoluteUrl($url); 0434 $body = ""; 0435 if (null === $response) { 0436 // require_once "Zend/Controller/Response/Http.php"; 0437 $response = new Zend_Controller_Response_Http(); 0438 } 0439 0440 if ($method == 'POST') { 0441 $body = "<html><body onLoad=\"document.forms[0].submit();\">\n"; 0442 $body .= "<form method=\"POST\" action=\"$url\">\n"; 0443 if (is_array($params) && count($params) > 0) { 0444 foreach($params as $key => $value) { 0445 $body .= '<input type="hidden" name="' . $key . '" value="' . $value . "\">\n"; 0446 } 0447 } 0448 $body .= "<input type=\"submit\" value=\"Continue OpenID transaction\">\n"; 0449 $body .= "</form></body></html>\n"; 0450 } else if (is_array($params) && count($params) > 0) { 0451 if (strpos($url, '?') === false) { 0452 $url .= '?' . self::paramsToQuery($params); 0453 } else { 0454 $url .= '&' . self::paramsToQuery($params); 0455 } 0456 } 0457 if (!empty($body)) { 0458 $response->setBody($body); 0459 } else if (!$response->canSendHeaders()) { 0460 $response->setBody("<script language=\"JavaScript\"" . 0461 " type=\"text/javascript\">window.location='$url';" . 0462 "</script>"); 0463 } else { 0464 $response->setRedirect($url); 0465 } 0466 $response->sendResponse(); 0467 if (self::$exitOnRedirect) { 0468 exit(); 0469 } 0470 } 0471 0472 /** 0473 * Produces string of random byte of given length. 0474 * 0475 * @param integer $len length of requested string 0476 * @return string RAW random binary string 0477 */ 0478 static public function randomBytes($len) 0479 { 0480 return (string) Zend_Crypt_Math::randBytes($len); 0481 } 0482 0483 /** 0484 * Generates a hash value (message digest) according to given algorithm. 0485 * It returns RAW binary string. 0486 * 0487 * This is a wrapper function that uses one of available internal function 0488 * dependent on given PHP configuration. It may use various functions from 0489 * ext/openssl, ext/hash, ext/mhash or ext/standard. 0490 * 0491 * @param string $func digest algorithm 0492 * @param string $data data to sign 0493 * @return string RAW digital signature 0494 * @throws Zend_OpenId_Exception 0495 */ 0496 static public function digest($func, $data) 0497 { 0498 if (function_exists('openssl_digest')) { 0499 return openssl_digest($data, $func, true); 0500 } else if (function_exists('hash')) { 0501 return hash($func, $data, true); 0502 } else if ($func === 'sha1') { 0503 return sha1($data, true); 0504 } else if ($func === 'sha256') { 0505 if (function_exists('mhash')) { 0506 return mhash(MHASH_SHA256 , $data); 0507 } 0508 } 0509 // require_once "Zend/OpenId/Exception.php"; 0510 throw new Zend_OpenId_Exception( 0511 'Unsupported digest algorithm "' . $func . '".', 0512 Zend_OpenId_Exception::UNSUPPORTED_DIGEST); 0513 } 0514 0515 /** 0516 * Generates a keyed hash value using the HMAC method. It uses ext/hash 0517 * if available or user-level PHP implementation, that is not significantly 0518 * slower. 0519 * 0520 * @param string $macFunc name of selected hashing algorithm (sha1, sha256) 0521 * @param string $data data to sign 0522 * @param string $secret shared secret key used for generating the HMAC 0523 * variant of the message digest 0524 * @return string RAW HMAC value 0525 */ 0526 static public function hashHmac($macFunc, $data, $secret) 0527 { 0528 // // require_once "Zend/Crypt/Hmac.php"; 0529 // return Zend_Crypt_Hmac::compute($secret, $macFunc, $data, Zend_Crypt_Hmac::BINARY); 0530 if (function_exists('hash_hmac')) { 0531 return hash_hmac($macFunc, $data, $secret, true); 0532 } else { 0533 if (Zend_OpenId::strlen($secret) > 64) { 0534 $secret = self::digest($macFunc, $secret); 0535 } 0536 $secret = str_pad($secret, 64, chr(0x00)); 0537 $ipad = str_repeat(chr(0x36), 64); 0538 $opad = str_repeat(chr(0x5c), 64); 0539 $hash1 = self::digest($macFunc, ($secret ^ $ipad) . $data); 0540 return self::digest($macFunc, ($secret ^ $opad) . $hash1); 0541 } 0542 } 0543 0544 /** 0545 * Converts binary representation into ext/gmp or ext/bcmath big integer 0546 * representation. 0547 * 0548 * @param string $bin binary representation of big number 0549 * @return mixed 0550 * @throws Zend_OpenId_Exception 0551 */ 0552 static protected function binToBigNum($bin) 0553 { 0554 if (extension_loaded('gmp')) { 0555 return gmp_init(bin2hex($bin), 16); 0556 } else if (extension_loaded('bcmath')) { 0557 $bn = 0; 0558 $len = Zend_OpenId::strlen($bin); 0559 for ($i = 0; $i < $len; $i++) { 0560 $bn = bcmul($bn, 256); 0561 $bn = bcadd($bn, ord($bin[$i])); 0562 } 0563 return $bn; 0564 } 0565 // require_once "Zend/OpenId/Exception.php"; 0566 throw new Zend_OpenId_Exception( 0567 'The system doesn\'t have proper big integer extension', 0568 Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH); 0569 } 0570 0571 /** 0572 * Converts internal ext/gmp or ext/bcmath big integer representation into 0573 * binary string. 0574 * 0575 * @param mixed $bn big number 0576 * @return string 0577 * @throws Zend_OpenId_Exception 0578 */ 0579 static protected function bigNumToBin($bn) 0580 { 0581 if (extension_loaded('gmp')) { 0582 $s = gmp_strval($bn, 16); 0583 if (strlen($s) % 2 != 0) { 0584 $s = '0' . $s; 0585 } else if ($s[0] > '7') { 0586 $s = '00' . $s; 0587 } 0588 return pack("H*", $s); 0589 } else if (extension_loaded('bcmath')) { 0590 $cmp = bccomp($bn, 0); 0591 if ($cmp == 0) { 0592 return "\0"; 0593 } else if ($cmp < 0) { 0594 // require_once "Zend/OpenId/Exception.php"; 0595 throw new Zend_OpenId_Exception( 0596 'Big integer arithmetic error', 0597 Zend_OpenId_Exception::ERROR_LONG_MATH); 0598 } 0599 $bin = ""; 0600 while (bccomp($bn, 0) > 0) { 0601 $bin = chr(bcmod($bn, 256)) . $bin; 0602 $bn = bcdiv($bn, 256); 0603 } 0604 if (ord($bin[0]) > 127) { 0605 $bin = "\0" . $bin; 0606 } 0607 return $bin; 0608 } 0609 // require_once "Zend/OpenId/Exception.php"; 0610 throw new Zend_OpenId_Exception( 0611 'The system doesn\'t have proper big integer extension', 0612 Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH); 0613 } 0614 0615 /** 0616 * Performs the first step of a Diffie-Hellman key exchange by generating 0617 * private and public DH values based on given prime number $p and 0618 * generator $g. Both sides of key exchange MUST have the same prime number 0619 * and generator. In this case they will able to create a random shared 0620 * secret that is never send from one to the other. 0621 * 0622 * @param string $p prime number in binary representation 0623 * @param string $g generator in binary representation 0624 * @param string $priv_key private key in binary representation 0625 * @return mixed 0626 */ 0627 static public function createDhKey($p, $g, $priv_key = null) 0628 { 0629 if (function_exists('openssl_dh_compute_key')) { 0630 $dh_details = array( 0631 'p' => $p, 0632 'g' => $g 0633 ); 0634 if ($priv_key !== null) { 0635 $dh_details['priv_key'] = $priv_key; 0636 } 0637 return openssl_pkey_new(array('dh'=>$dh_details)); 0638 } else { 0639 $bn_p = self::binToBigNum($p); 0640 $bn_g = self::binToBigNum($g); 0641 if ($priv_key === null) { 0642 $priv_key = self::randomBytes(Zend_OpenId::strlen($p)); 0643 } 0644 $bn_priv_key = self::binToBigNum($priv_key); 0645 if (extension_loaded('gmp')) { 0646 $bn_pub_key = gmp_powm($bn_g, $bn_priv_key, $bn_p); 0647 } else if (extension_loaded('bcmath')) { 0648 $bn_pub_key = bcpowmod($bn_g, $bn_priv_key, $bn_p); 0649 } 0650 $pub_key = self::bigNumToBin($bn_pub_key); 0651 0652 return array( 0653 'p' => $bn_p, 0654 'g' => $bn_g, 0655 'priv_key' => $bn_priv_key, 0656 'pub_key' => $bn_pub_key, 0657 'details' => array( 0658 'p' => $p, 0659 'g' => $g, 0660 'priv_key' => $priv_key, 0661 'pub_key' => $pub_key)); 0662 } 0663 } 0664 0665 /** 0666 * Returns an associative array with Diffie-Hellman key components in 0667 * binary representation. The array includes original prime number 'p' and 0668 * generator 'g', random private key 'priv_key' and corresponding public 0669 * key 'pub_key'. 0670 * 0671 * @param mixed $dh Diffie-Hellman key 0672 * @return array 0673 */ 0674 static public function getDhKeyDetails($dh) 0675 { 0676 if (function_exists('openssl_dh_compute_key')) { 0677 $details = openssl_pkey_get_details($dh); 0678 if (isset($details['dh'])) { 0679 return $details['dh']; 0680 } 0681 } else { 0682 return $dh['details']; 0683 } 0684 } 0685 0686 /** 0687 * Computes the shared secret from the private DH value $dh and the other 0688 * party's public value in $pub_key 0689 * 0690 * @param string $pub_key other party's public value 0691 * @param mixed $dh Diffie-Hellman key 0692 * @return string 0693 * @throws Zend_OpenId_Exception 0694 */ 0695 static public function computeDhSecret($pub_key, $dh) 0696 { 0697 if (function_exists('openssl_dh_compute_key')) { 0698 $ret = openssl_dh_compute_key($pub_key, $dh); 0699 if (ord($ret[0]) > 127) { 0700 $ret = "\0" . $ret; 0701 } 0702 return $ret; 0703 } else if (extension_loaded('gmp')) { 0704 $bn_pub_key = self::binToBigNum($pub_key); 0705 $bn_secret = gmp_powm($bn_pub_key, $dh['priv_key'], $dh['p']); 0706 return self::bigNumToBin($bn_secret); 0707 } else if (extension_loaded('bcmath')) { 0708 $bn_pub_key = self::binToBigNum($pub_key); 0709 $bn_secret = bcpowmod($bn_pub_key, $dh['priv_key'], $dh['p']); 0710 return self::bigNumToBin($bn_secret); 0711 } 0712 // require_once "Zend/OpenId/Exception.php"; 0713 throw new Zend_OpenId_Exception( 0714 'The system doesn\'t have proper big integer extension', 0715 Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH); 0716 } 0717 0718 /** 0719 * Takes an arbitrary precision integer and returns its shortest big-endian 0720 * two's complement representation. 0721 * 0722 * Arbitrary precision integers MUST be encoded as big-endian signed two's 0723 * complement binary strings. Henceforth, "btwoc" is a function that takes 0724 * an arbitrary precision integer and returns its shortest big-endian two's 0725 * complement representation. All integers that are used with 0726 * Diffie-Hellman Key Exchange are positive. This means that the left-most 0727 * bit of the two's complement representation MUST be zero. If it is not, 0728 * implementations MUST add a zero byte at the front of the string. 0729 * 0730 * @param string $str binary representation of arbitrary precision integer 0731 * @return string big-endian signed representation 0732 */ 0733 static public function btwoc($str) 0734 { 0735 if (ord($str[0]) > 127) { 0736 return "\0" . $str; 0737 } 0738 return $str; 0739 } 0740 0741 /** 0742 * Returns lenght of binary string in bytes 0743 * 0744 * @param string $str 0745 * @return int the string lenght 0746 */ 0747 static public function strlen($str) 0748 { 0749 if (extension_loaded('mbstring') && 0750 (((int)ini_get('mbstring.func_overload')) & 2)) { 0751 return mb_strlen($str, 'latin1'); 0752 } else { 0753 return strlen($str); 0754 } 0755 } 0756 0757 }