PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/Zend/OpenId.php

https://bitbucket.org/freddixx/e-business-vcrm-plugin
PHP | 715 lines | 573 code | 26 blank | 116 comment | 129 complexity | 085ecc4e55554be72cc94938b6c6eae2 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_OpenId
  17. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: OpenId.php 9240 2008-04-18 13:06:29Z dmitry $
  20. */
  21. /**
  22. * @see Zend_OpenId_Exception
  23. */
  24. require_once "Zend/OpenId/Exception.php";
  25. /**
  26. * @see Zend_Controller_Response_Abstract
  27. */
  28. require_once "Zend/Controller/Response/Abstract.php";
  29. /**
  30. * Static class that contains common utility functions for
  31. * {@link Zend_OpenId_Consumer} and {@link Zend_OpenId_Provider}.
  32. *
  33. * This class implements common utility functions that are used by both
  34. * Consumer and Provider. They include functions for Diffie-Hellman keys
  35. * generation and exchange, URL normalization, HTTP redirection and some others.
  36. *
  37. * @category Zend
  38. * @package Zend_OpenId
  39. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  40. * @license http://framework.zend.com/license/new-bsd New BSD License
  41. */
  42. class Zend_OpenId
  43. {
  44. /**
  45. * Default Diffie-Hellman key generator (1024 bit)
  46. */
  47. const DH_P = 'dcf93a0b883972ec0e19989ac5a2ce310e1d37717e8d9571bb7623731866e61ef75a2e27898b057f9891c2e27a639c3f29b60814581cd3b2ca3986d2683705577d45c2e7e52dc81c7a171876e5cea74b1448bfdfaf18828efd2519f14e45e3826634af1949e5b535cc829a483b8a76223e5d490a257f05bdff16f2fb22c583ab';
  48. /**
  49. * Default Diffie-Hellman prime number (should be 2 or 5)
  50. */
  51. const DH_G = '02';
  52. /**
  53. * OpenID 2.0 namespace. All OpenID 2.0 messages MUST contain variable
  54. * openid.ns with its value.
  55. */
  56. const NS_2_0 = 'http://specs.openid.net/auth/2.0';
  57. /**
  58. * Allows enable/disable stoping execution of PHP script after redirect()
  59. */
  60. static public $exitOnRedirect = true;
  61. /**
  62. * Returns a full URL that was requested on current HTTP request.
  63. *
  64. * @return string
  65. */
  66. static public function selfUrl()
  67. {
  68. if (isset($_SERVER['SCRIPT_URI'])) {
  69. return $_SERVER['SCRIPT_URI'];
  70. }
  71. $url = '';
  72. $port = '';
  73. if (isset($_SERVER['HTTP_HOST'])) {
  74. if (($pos = strpos($_SERVER['HTTP_HOST'], ':')) === false) {
  75. if (isset($_SERVER['SERVER_PORT'])) {
  76. $port = ':' . $_SERVER['SERVER_PORT'];
  77. }
  78. $url = $_SERVER['HTTP_HOST'];
  79. } else {
  80. $url = substr($_SERVER['HTTP_HOST'], 0, $pos);
  81. $port = substr($_SERVER['HTTP_HOST'], $pos);
  82. }
  83. } else if (isset($_SERVER['SERVER_NAME'])) {
  84. $url = $_SERVER['SERVER_NAME'];
  85. if (isset($_SERVER['SERVER_PORT'])) {
  86. $port = ':' . $_SERVER['SERVER_PORT'];
  87. }
  88. }
  89. if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
  90. $url = 'https://' . $url;
  91. if ($port == ':443') {
  92. $port = '';
  93. }
  94. } else {
  95. $url = 'http://' . $url;
  96. if ($port == ':80') {
  97. $port = '';
  98. }
  99. }
  100. $url .= $port;
  101. if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
  102. $url .= $_SERVER['HTTP_X_REWRITE_URL'];
  103. } elseif (isset($_SERVER['REQUEST_URI'])) {
  104. $query = strpos($_SERVER['REQUEST_URI'], '?');
  105. if ($query === false) {
  106. $url .= $_SERVER['REQUEST_URI'];
  107. } else {
  108. $url .= substr($_SERVER['REQUEST_URI'], 0, $query);
  109. }
  110. } else if (isset($_SERVER['SCRIPT_URL'])) {
  111. $url .= $_SERVER['SCRIPT_URL'];
  112. } else if (isset($_SERVER['REDIRECT_URL'])) {
  113. $url .= $_SERVER['REDIRECT_URL'];
  114. } else if (isset($_SERVER['PHP_SELF'])) {
  115. $url .= $_SERVER['PHP_SELF'];
  116. } else if (isset($_SERVER['SCRIPT_NAME'])) {
  117. $url .= $_SERVER['SCRIPT_NAME'];
  118. if (isset($_SERVER['PATH_INFO'])) {
  119. $url .= $_SERVER['PATH_INFO'];
  120. }
  121. }
  122. return $url;
  123. }
  124. /**
  125. * Returns an absolute URL for the given one
  126. *
  127. * @param string $url absilute or relative URL
  128. * @return string
  129. */
  130. static public function absoluteUrl($url)
  131. {
  132. if (empty($url)) {
  133. return Zend_OpenId::selfUrl();
  134. } else if (!preg_match('|^([^:]+)://|', $url)) {
  135. if (preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?]*)?((?:[?](?:[^#]*))?(?:#.*)?)$|', Zend_OpenId::selfUrl(), $reg)) {
  136. $scheme = $reg[1];
  137. $auth = $reg[2];
  138. $host = $reg[3];
  139. $port = $reg[4];
  140. $path = $reg[5];
  141. $query = $reg[6];
  142. if ($url[0] == '/') {
  143. return $scheme
  144. . '://'
  145. . $auth
  146. . $host
  147. . (empty($port) ? '' : (':' . $port))
  148. . $url;
  149. } else {
  150. $dir = dirname($path);
  151. return $scheme
  152. . '://'
  153. . $auth
  154. . $host
  155. . (empty($port) ? '' : (':' . $port))
  156. . (strlen($dir) > 1 ? $dir : '')
  157. . '/'
  158. . $url;
  159. }
  160. }
  161. }
  162. return $url;
  163. }
  164. /**
  165. * Converts variable/value pairs into URL encoded query string
  166. *
  167. * @param array $params variable/value pairs
  168. * @return string URL encoded query string
  169. */
  170. static public function paramsToQuery($params)
  171. {
  172. foreach($params as $key => $value) {
  173. if (isset($query)) {
  174. $query .= '&' . $key . '=' . urlencode($value);
  175. } else {
  176. $query = $key . '=' . urlencode($value);
  177. }
  178. }
  179. return isset($query) ? $query : '';
  180. }
  181. /**
  182. * Normalizes URL according to RFC 3986 to use it in comparison operations.
  183. * The function gets URL argument by reference and modifies it.
  184. * It returns true on success and false of failure.
  185. *
  186. * @param string &$id url to be normalized
  187. * @return bool
  188. */
  189. static public function normalizeUrl(&$id)
  190. {
  191. // RFC 3986, 6.2.2. Syntax-Based Normalization
  192. // RFC 3986, 6.2.2.2 Percent-Encoding Normalization
  193. $i = 0;
  194. $n = strlen($id);
  195. $res = '';
  196. while ($i < $n) {
  197. if ($id[$i] == '%') {
  198. if ($i + 2 >= $n) {
  199. return false;
  200. }
  201. ++$i;
  202. if ($id[$i] >= '0' && $id[$i] <= '9') {
  203. $c = ord($id[$i]) - ord('0');
  204. } else if ($id[$i] >= 'A' && $id[$i] <= 'F') {
  205. $c = ord($id[$i]) - ord('A') + 10;
  206. } else if ($id[$i] >= 'a' && $id[$i] <= 'f') {
  207. $c = ord($id[$i]) - ord('a') + 10;
  208. } else {
  209. return false;
  210. }
  211. ++$i;
  212. if ($id[$i] >= '0' && $id[$i] <= '9') {
  213. $c = ($c << 4) | (ord($id[$i]) - ord('0'));
  214. } else if ($id[$i] >= 'A' && $id[$i] <= 'F') {
  215. $c = ($c << 4) | (ord($id[$i]) - ord('A') + 10);
  216. } else if ($id[$i] >= 'a' && $id[$i] <= 'f') {
  217. $c = ($c << 4) | (ord($id[$i]) - ord('a') + 10);
  218. } else {
  219. return false;
  220. }
  221. ++$i;
  222. $ch = chr($c);
  223. if (($ch >= 'A' && $ch <= 'Z') ||
  224. ($ch >= 'a' && $ch <= 'z') ||
  225. $ch == '-' ||
  226. $ch == '.' ||
  227. $ch == '_' ||
  228. $ch == '~') {
  229. $res .= $ch;
  230. } else {
  231. $res .= '%';
  232. if (($c >> 4) < 10) {
  233. $res .= chr(($c >> 4) + ord('0'));
  234. } else {
  235. $res .= chr(($c >> 4) - 10 + ord('A'));
  236. }
  237. $c = $c & 0xf;
  238. if ($c < 10) {
  239. $res .= chr($c + ord('0'));
  240. } else {
  241. $res .= chr($c - 10 + ord('A'));
  242. }
  243. }
  244. } else {
  245. $res .= $id[$i++];
  246. }
  247. }
  248. if (!preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?#]*)?((?:[?](?:[^#]*))?)((?:#.*)?)$|', $res, $reg)) {
  249. return false;
  250. }
  251. $scheme = $reg[1];
  252. $auth = $reg[2];
  253. $host = $reg[3];
  254. $port = $reg[4];
  255. $path = $reg[5];
  256. $query = $reg[6];
  257. $fragment = $reg[7]; /* strip it */
  258. if (empty($scheme) || empty($host)) {
  259. return false;
  260. }
  261. // RFC 3986, 6.2.2.1. Case Normalization
  262. $scheme = strtolower($scheme);
  263. $host = strtolower($host);
  264. // RFC 3986, 6.2.2.3. Path Segment Normalization
  265. if (!empty($path)) {
  266. $i = 0;
  267. $n = strlen($path);
  268. $res = "";
  269. while ($i < $n) {
  270. if ($path[$i] == '/') {
  271. ++$i;
  272. while ($i < $n && $path[$i] == '/') {
  273. ++$i;
  274. }
  275. if ($i < $n && $path[$i] == '.') {
  276. ++$i;
  277. if ($i < $n && $path[$i] == '.') {
  278. ++$i;
  279. if ($i == $n || $path[$i] == '/') {
  280. if (($pos = strrpos($res, '/')) !== false) {
  281. $res = substr($res, 0, $pos);
  282. }
  283. } else {
  284. $res .= '/..';
  285. }
  286. } else if ($i != $n && $path[$i] != '/') {
  287. $res .= '/.';
  288. }
  289. } else {
  290. $res .= '/';
  291. }
  292. } else {
  293. $res .= $path[$i++];
  294. }
  295. }
  296. $path = $res;
  297. }
  298. // RFC 3986,6.2.3. Scheme-Based Normalization
  299. if ($scheme == 'http') {
  300. if ($port == 80) {
  301. $port = '';
  302. }
  303. } else if ($scheme == 'https') {
  304. if ($port == 443) {
  305. $port = '';
  306. }
  307. }
  308. if (empty($path)) {
  309. $path = '/';
  310. }
  311. $id = $scheme
  312. . '://'
  313. . $auth
  314. . $host
  315. . (empty($port) ? '' : (':' . $port))
  316. . $path
  317. . $query;
  318. return true;
  319. }
  320. /**
  321. * Normalizes OpenID identifier that can be URL or XRI name.
  322. * Returns true on success and false of failure.
  323. *
  324. * Normalization is performed according to the following rules:
  325. * 1. If the user's input starts with one of the "xri://", "xri://$ip*",
  326. * or "xri://$dns*" prefixes, they MUST be stripped off, so that XRIs
  327. * are used in the canonical form, and URI-authority XRIs are further
  328. * considered URL identifiers.
  329. * 2. If the first character of the resulting string is an XRI Global
  330. * Context Symbol ("=", "@", "+", "$", "!"), then the input SHOULD be
  331. * treated as an XRI.
  332. * 3. Otherwise, the input SHOULD be treated as an http URL; if it does
  333. * not include a "http" or "https" scheme, the Identifier MUST be
  334. * prefixed with the string "http://".
  335. * 4. URL identifiers MUST then be further normalized by both following
  336. * redirects when retrieving their content and finally applying the
  337. * rules in Section 6 of [RFC3986] to the final destination URL.
  338. * @param string &$id identifier to be normalized
  339. * @return bool
  340. */
  341. static public function normalize(&$id)
  342. {
  343. $id = trim($id);
  344. if (strlen($id) === 0) {
  345. return true;
  346. }
  347. // 7.2.1
  348. if (strpos($id, 'xri://$ip*') === 0) {
  349. $id = substr($id, strlen('xri://$ip*'));
  350. } else if (strpos($id, 'xri://$dns*') === 0) {
  351. $id = substr($id, strlen('xri://$dns*'));
  352. } else if (strpos($id, 'xri://') === 0) {
  353. $id = substr($id, strlen('xri://'));
  354. }
  355. // 7.2.2
  356. if ($id[0] == '=' ||
  357. $id[0] == '@' ||
  358. $id[0] == '+' ||
  359. $id[0] == '$' ||
  360. $id[0] == '!') {
  361. return true;
  362. }
  363. // 7.2.3
  364. if (strpos($id, "://") === false) {
  365. $id = 'http://' . $id;
  366. }
  367. // 7.2.4
  368. return self::normalizeURL($id);
  369. }
  370. /**
  371. * Performs a HTTP redirection to specified URL with additional data.
  372. * It may generate redirected request using GET or POST HTTP method.
  373. * The function never returns.
  374. *
  375. * @param string $url URL to redirect to
  376. * @param array $params additional variable/value pairs to send
  377. * @param Zend_Controller_Response_Abstract $response
  378. * @param string $method redirection method ('GET' or 'POST')
  379. */
  380. static public function redirect($url, $params = null,
  381. Zend_Controller_Response_Abstract $response = null, $method = 'GET')
  382. {
  383. $url = Zend_OpenId::absoluteUrl($url);
  384. $body = "";
  385. if (null === $response) {
  386. require_once "Zend/Controller/Response/Http.php";
  387. $response = new Zend_Controller_Response_Http();
  388. }
  389. if ($method == 'POST') {
  390. $body = "<html><body onLoad=\"document.forms[0].submit();\">\n";
  391. $body .= "<form method=\"POST\" action=\"$url\">\n";
  392. if (is_array($params) && count($params) > 0) {
  393. foreach($params as $key => $value) {
  394. $body .= '<input type="hidden" name="' . $key . '" value="' . $value . "\">\n";
  395. }
  396. }
  397. $body .= "<input type=\"submit\" value=\"Continue OpenID transaction\">\n";
  398. $body .= "</form></body></html>\n";
  399. } else if (is_array($params) && count($params) > 0) {
  400. if (strpos($url, '?') === false) {
  401. $url .= '?' . self::paramsToQuery($params);
  402. } else {
  403. $url .= '&' . self::paramsToQuery($params);
  404. }
  405. }
  406. if (!empty($body)) {
  407. $response->setBody($body);
  408. } else if (!$response->canSendHeaders()) {
  409. $response->setBody("<script language=\"JavaScript\"" .
  410. " type=\"text/javascript\">window.location='$url';" .
  411. "</script>");
  412. } else {
  413. $response->setRedirect($url);
  414. }
  415. $response->sendResponse();
  416. if (self::$exitOnRedirect) {
  417. exit();
  418. }
  419. }
  420. /**
  421. * Produces string of random byte of given length.
  422. *
  423. * @param integer $len length of requested string
  424. * @return string RAW random binary string
  425. */
  426. static public function randomBytes($len)
  427. {
  428. $key = '';
  429. for($i=0; $i < $len; $i++) {
  430. $key .= chr(mt_rand(0, 255));
  431. }
  432. return $key;
  433. }
  434. /**
  435. * Generates a hash value (message digest) according to given algorithm.
  436. * It returns RAW binary string.
  437. *
  438. * This is a wrapper function that uses one of available internal function
  439. * dependent on given PHP configuration. It may use various functions from
  440. * ext/openssl, ext/hash, ext/mhash or ext/standard.
  441. *
  442. * @param string $func digest algorithm
  443. * @param string $data data to sign
  444. * @return string RAW digital signature
  445. * @throws Zend_OpenId_Exception
  446. */
  447. static public function digest($func, $data)
  448. {
  449. if (function_exists('openssl_digest')) {
  450. return openssl_digest($data, $func, true);
  451. } else if (function_exists('hash')) {
  452. return hash($func, $data, true);
  453. } else if ($func === 'sha1') {
  454. return sha1($data, true);
  455. } else if ($func === 'sha256') {
  456. if (function_exists('mhash')) {
  457. return mhash(MHASH_SHA256 , $data);
  458. }
  459. }
  460. throw new Zend_OpenId_Exception(
  461. 'Unsupported digest algorithm "' . $func . '".',
  462. Zend_OpenId_Exception::UNSUPPORTED_DIGEST);
  463. }
  464. /**
  465. * Generates a keyed hash value using the HMAC method. It uses ext/hash
  466. * if available or user-level PHP implementation, that is not significantly
  467. * slower.
  468. *
  469. * @param string $macFunc name of selected hashing algorithm (sha1, sha256)
  470. * @param string $data data to sign
  471. * @param string $secret shared secret key used for generating the HMAC
  472. * variant of the message digest
  473. * @return string RAW HMAC value
  474. */
  475. static public function hashHmac($macFunc, $data, $secret)
  476. {
  477. // require_once "Zend/Crypt/Hmac.php";
  478. // return Zend_Crypt_Hmac::compute($secret, $macFunc, $data, Zend_Crypt_Hmac::BINARY);
  479. if (function_exists('hash_hmac')) {
  480. return hash_hmac($macFunc, $data, $secret, 1);
  481. } else {
  482. if (strlen($secret) > 64) {
  483. $secret = self::digest($macFunc, $secret);
  484. }
  485. $secret = str_pad($secret, 64, chr(0x00));
  486. $ipad = str_repeat(chr(0x36), 64);
  487. $opad = str_repeat(chr(0x5c), 64);
  488. $hash1 = self::digest($macFunc, ($secret ^ $ipad) . $data);
  489. return self::digest($macFunc, ($secret ^ $opad) . $hash1);
  490. }
  491. }
  492. /**
  493. * Converts binary representation into ext/gmp or ext/bcmath big integer
  494. * representation.
  495. *
  496. * @param string $bin binary representation of big number
  497. * @return mixed
  498. * @throws Zend_OpenId_Exception
  499. */
  500. static protected function binToBigNum($bin)
  501. {
  502. if (extension_loaded('gmp')) {
  503. return gmp_init(bin2hex($bin), 16);
  504. } else if (extension_loaded('bcmath')) {
  505. $bn = 0;
  506. $len = strlen($bin);
  507. for ($i = 0; $i < $len; $i++) {
  508. $bn = bcmul($bn, 256);
  509. $bn = bcadd($bn, ord($bin[$i]));
  510. }
  511. return $bn;
  512. }
  513. throw new Zend_OpenId_Exception(
  514. 'The system doesn\'t have proper big integer extension',
  515. Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH);
  516. }
  517. /**
  518. * Converts internal ext/gmp or ext/bcmath big integer representation into
  519. * binary string.
  520. *
  521. * @param mixed $bn big number
  522. * @return string
  523. * @throws Zend_OpenId_Exception
  524. */
  525. static protected function bigNumToBin($bn)
  526. {
  527. if (extension_loaded('gmp')) {
  528. $s = gmp_strval($bn, 16);
  529. if (strlen($s) % 2 != 0) {
  530. $s = '0' . $s;
  531. } else if ($s[0] > '7') {
  532. $s = '00' . $s;
  533. }
  534. return pack("H*", $s);
  535. } else if (extension_loaded('bcmath')) {
  536. $cmp = bccomp($bn, 0);
  537. if ($cmp == 0) {
  538. return (chr(0));
  539. } else if ($cmp < 0) {
  540. throw new Zend_OpenId_Exception(
  541. 'Big integer arithmetic error',
  542. Zend_OpenId_Exception::ERROR_LONG_MATH);
  543. }
  544. $bin = "";
  545. while (bccomp($bn, 0) > 0) {
  546. $bin = chr(bcmod($bn, 256)) . $bin;
  547. $bn = bcdiv($bn, 256);
  548. }
  549. if (ord($bin[0]) > 127) {
  550. $bin = chr(0) . $bin;
  551. }
  552. return $bin;
  553. }
  554. throw new Zend_OpenId_Exception(
  555. 'The system doesn\'t have proper big integer extension',
  556. Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH);
  557. }
  558. /**
  559. * Performs the first step of a Diffie-Hellman key exchange by generating
  560. * private and public DH values based on given prime number $p and
  561. * generator $g. Both sides of key exchange MUST have the same prime number
  562. * and generator. In this case they will able to create a random shared
  563. * secret that is never send from one to the other.
  564. *
  565. * @param string $p prime number in binary representation
  566. * @param string $g generator in binary representation
  567. * @param string $priv_key private key in binary representation
  568. * @return mixed
  569. */
  570. static public function createDhKey($p, $g, $priv_key = null)
  571. {
  572. if (function_exists('openssl_dh_compute_key')) {
  573. $dh_details = array(
  574. 'p' => $p,
  575. 'g' => $g
  576. );
  577. if (!is_null($priv_key)) {
  578. $dh_details['priv_key'] = $priv_key;
  579. }
  580. return openssl_pkey_new(array('dh'=>$dh_details));
  581. } else {
  582. $bn_p = self::binToBigNum($p);
  583. $bn_g = self::binToBigNum($g);
  584. if (is_null($priv_key)) {
  585. $priv_key = self::randomBytes(strlen($p));
  586. }
  587. $bn_priv_key = self::binToBigNum($priv_key);
  588. if (extension_loaded('gmp')) {
  589. $bn_pub_key = gmp_powm($bn_g, $bn_priv_key, $bn_p);
  590. } else if (extension_loaded('bcmath')) {
  591. $bn_pub_key = bcpowmod($bn_g, $bn_priv_key, $bn_p);
  592. }
  593. $pub_key = self::bigNumToBin($bn_pub_key);
  594. return array(
  595. 'p' => $bn_p,
  596. 'g' => $bn_g,
  597. 'priv_key' => $bn_priv_key,
  598. 'pub_key' => $bn_pub_key,
  599. 'details' => array(
  600. 'p' => $p,
  601. 'g' => $g,
  602. 'priv_key' => $priv_key,
  603. 'pub_key' => $pub_key));
  604. }
  605. }
  606. /**
  607. * Returns an associative array with Diffie-Hellman key components in
  608. * binary representation. The array includes original prime number 'p' and
  609. * generator 'g', random private key 'priv_key' and corresponding public
  610. * key 'pub_key'.
  611. *
  612. * @param mixed $dh Diffie-Hellman key
  613. * @return array
  614. */
  615. static public function getDhKeyDetails($dh)
  616. {
  617. if (function_exists('openssl_dh_compute_key')) {
  618. $details = openssl_pkey_get_details($dh);
  619. if (isset($details['dh'])) {
  620. return $details['dh'];
  621. }
  622. } else {
  623. return $dh['details'];
  624. }
  625. }
  626. /**
  627. * Computes the shared secret from the private DH value $dh and the other
  628. * party's public value in $pub_key
  629. *
  630. * @param string $pub_key other party's public value
  631. * @param mixed $dh Diffie-Hellman key
  632. * @return string
  633. * @throws Zend_OpenId_Exception
  634. */
  635. static public function computeDhSecret($pub_key, $dh)
  636. {
  637. if (function_exists('openssl_dh_compute_key')) {
  638. $ret = openssl_dh_compute_key($pub_key, $dh);
  639. if (ord($ret[0]) > 127) {
  640. $ret = chr(0) . $ret;
  641. }
  642. return $ret;
  643. } else if (extension_loaded('gmp')) {
  644. $bn_pub_key = self::binToBigNum($pub_key);
  645. $bn_secret = gmp_powm($bn_pub_key, $dh['priv_key'], $dh['p']);
  646. return self::bigNumToBin($bn_secret);
  647. } else if (extension_loaded('bcmath')) {
  648. $bn_pub_key = self::binToBigNum($pub_key);
  649. $bn_secret = bcpowmod($bn_pub_key, $dh['priv_key'], $dh['p']);
  650. return self::bigNumToBin($bn_secret);
  651. }
  652. throw new Zend_OpenId_Exception(
  653. 'The system doesn\'t have proper big integer extension',
  654. Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH);
  655. }
  656. /**
  657. * Takes an arbitrary precision integer and returns its shortest big-endian
  658. * two's complement representation.
  659. *
  660. * Arbitrary precision integers MUST be encoded as big-endian signed two's
  661. * complement binary strings. Henceforth, "btwoc" is a function that takes
  662. * an arbitrary precision integer and returns its shortest big-endian two's
  663. * complement representation. All integers that are used with
  664. * Diffie-Hellman Key Exchange are positive. This means that the left-most
  665. * bit of the two's complement representation MUST be zero. If it is not,
  666. * implementations MUST add a zero byte at the front of the string.
  667. *
  668. * @param string $str binary representation of arbitrary precision integer
  669. * @return string big-endian signed representation
  670. */
  671. static public function btwoc($str)
  672. {
  673. if (ord($str[0]) > 127) {
  674. return chr(0) . $str;
  675. }
  676. return $str;
  677. }
  678. }