[ Index ]

PHP Cross Reference of Joomla 1.5.26 DE

title

Body

[close]

/libraries/openid/Auth/OpenID/ -> Association.php (source)

   1  <?php
   2  
   3  /**
   4   * This module contains code for dealing with associations between
   5   * consumers and servers.
   6   *
   7   * PHP versions 4 and 5
   8   *
   9   * LICENSE: See the COPYING file included in this distribution.
  10   *
  11   * @package OpenID
  12   * @author JanRain, Inc. <openid@janrain.com>
  13   * @copyright 2005-2008 Janrain, Inc.
  14   * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
  15   */
  16  
  17  // Do not allow direct access
  18  defined( '_JEXEC' ) or die( 'Restricted access' );
  19  
  20  /**
  21   * @access private
  22   */
  23  require_once 'Auth/OpenID/CryptUtil.php';
  24  
  25  /**
  26   * @access private
  27   */
  28  require_once 'Auth/OpenID/KVForm.php';
  29  
  30  /**
  31   * @access private
  32   */
  33  require_once 'Auth/OpenID/HMAC.php';
  34  
  35  /**
  36   * This class represents an association between a server and a
  37   * consumer.  In general, users of this library will never see
  38   * instances of this object.  The only exception is if you implement a
  39   * custom {@link Auth_OpenID_OpenIDStore}.
  40   *
  41   * If you do implement such a store, it will need to store the values
  42   * of the handle, secret, issued, lifetime, and assoc_type instance
  43   * variables.
  44   *
  45   * @package OpenID
  46   */
  47  class Auth_OpenID_Association {
  48  
  49      /**
  50       * This is a HMAC-SHA1 specific value.
  51       *
  52       * @access private
  53       */
  54      var $SIG_LENGTH = 20;
  55  
  56      /**
  57       * The ordering and name of keys as stored by serialize.
  58       *
  59       * @access private
  60       */
  61      var $assoc_keys = array(
  62                              'version',
  63                              'handle',
  64                              'secret',
  65                              'issued',
  66                              'lifetime',
  67                              'assoc_type'
  68                              );
  69  
  70      var $_macs = array(
  71                         'HMAC-SHA1' => 'Auth_OpenID_HMACSHA1',
  72                         'HMAC-SHA256' => 'Auth_OpenID_HMACSHA256'
  73                         );
  74  
  75      /**
  76       * This is an alternate constructor (factory method) used by the
  77       * OpenID consumer library to create associations.  OpenID store
  78       * implementations shouldn't use this constructor.
  79       *
  80       * @access private
  81       *
  82       * @param integer $expires_in This is the amount of time this
  83       * association is good for, measured in seconds since the
  84       * association was issued.
  85       *
  86       * @param string $handle This is the handle the server gave this
  87       * association.
  88       *
  89       * @param string secret This is the shared secret the server
  90       * generated for this association.
  91       *
  92       * @param assoc_type This is the type of association this
  93       * instance represents.  The only valid values of this field at
  94       * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may
  95       * be defined in the future.
  96       *
  97       * @return association An {@link Auth_OpenID_Association}
  98       * instance.
  99       */
 100      function fromExpiresIn($expires_in, $handle, $secret, $assoc_type)
 101      {
 102          $issued = time();
 103          $lifetime = $expires_in;
 104          return new Auth_OpenID_Association($handle, $secret,
 105                                             $issued, $lifetime, $assoc_type);
 106      }
 107  
 108      /**
 109       * This is the standard constructor for creating an association.
 110       * The library should create all of the necessary associations, so
 111       * this constructor is not part of the external API.
 112       *
 113       * @access private
 114       *
 115       * @param string $handle This is the handle the server gave this
 116       * association.
 117       *
 118       * @param string $secret This is the shared secret the server
 119       * generated for this association.
 120       *
 121       * @param integer $issued This is the time this association was
 122       * issued, in seconds since 00:00 GMT, January 1, 1970.  (ie, a
 123       * unix timestamp)
 124       *
 125       * @param integer $lifetime This is the amount of time this
 126       * association is good for, measured in seconds since the
 127       * association was issued.
 128       *
 129       * @param string $assoc_type This is the type of association this
 130       * instance represents.  The only valid values of this field at
 131       * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may
 132       * be defined in the future.
 133       */
 134      function Auth_OpenID_Association(
 135          $handle, $secret, $issued, $lifetime, $assoc_type)
 136      {
 137          if (!in_array($assoc_type,
 138                        Auth_OpenID_getSupportedAssociationTypes())) {
 139              $fmt = 'Unsupported association type (%s)';
 140              trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR);
 141          }
 142  
 143          $this->handle = $handle;
 144          $this->secret = $secret;
 145          $this->issued = $issued;
 146          $this->lifetime = $lifetime;
 147          $this->assoc_type = $assoc_type;
 148      }
 149  
 150      /**
 151       * This returns the number of seconds this association is still
 152       * valid for, or 0 if the association is no longer valid.
 153       *
 154       * @return integer $seconds The number of seconds this association
 155       * is still valid for, or 0 if the association is no longer valid.
 156       */
 157      function getExpiresIn($now = null)
 158      {
 159          if ($now == null) {
 160              $now = time();
 161          }
 162  
 163          return max(0, $this->issued + $this->lifetime - $now);
 164      }
 165  
 166      /**
 167       * This checks to see if two {@link Auth_OpenID_Association}
 168       * instances represent the same association.
 169       *
 170       * @return bool $result true if the two instances represent the
 171       * same association, false otherwise.
 172       */
 173      function equal($other)
 174      {
 175          return ((gettype($this) == gettype($other))
 176                  && ($this->handle == $other->handle)
 177                  && ($this->secret == $other->secret)
 178                  && ($this->issued == $other->issued)
 179                  && ($this->lifetime == $other->lifetime)
 180                  && ($this->assoc_type == $other->assoc_type));
 181      }
 182  
 183      /**
 184       * Convert an association to KV form.
 185       *
 186       * @return string $result String in KV form suitable for
 187       * deserialization by deserialize.
 188       */
 189      function serialize()
 190      {
 191          $data = array(
 192                       'version' => '2',
 193                       'handle' => $this->handle,
 194                       'secret' => base64_encode($this->secret),
 195                       'issued' => strval(intval($this->issued)),
 196                       'lifetime' => strval(intval($this->lifetime)),
 197                       'assoc_type' => $this->assoc_type
 198                       );
 199  
 200          assert(array_keys($data) == $this->assoc_keys);
 201  
 202          return Auth_OpenID_KVForm::fromArray($data, $strict = true);
 203      }
 204  
 205      /**
 206       * Parse an association as stored by serialize().  This is the
 207       * inverse of serialize.
 208       *
 209       * @param string $assoc_s Association as serialized by serialize()
 210       * @return Auth_OpenID_Association $result instance of this class
 211       */
 212      function deserialize($class_name, $assoc_s)
 213      {
 214          $pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true);
 215          $keys = array();
 216          $values = array();
 217          foreach ($pairs as $key => $value) {
 218              if (is_array($value)) {
 219                  list($key, $value) = $value;
 220              }
 221              $keys[] = $key;
 222              $values[] = $value;
 223          }
 224  
 225          $class_vars = get_class_vars($class_name);
 226          $class_assoc_keys = $class_vars['assoc_keys'];
 227  
 228          sort($keys);
 229          sort($class_assoc_keys);
 230  
 231          if ($keys != $class_assoc_keys) {
 232              trigger_error('Unexpected key values: ' . var_export($keys, true),
 233                            E_USER_WARNING);
 234              return null;
 235          }
 236  
 237          $version = $pairs['version'];
 238          $handle = $pairs['handle'];
 239          $secret = $pairs['secret'];
 240          $issued = $pairs['issued'];
 241          $lifetime = $pairs['lifetime'];
 242          $assoc_type = $pairs['assoc_type'];
 243  
 244          if ($version != '2') {
 245              trigger_error('Unknown version: ' . $version, E_USER_WARNING);
 246              return null;
 247          }
 248  
 249          $issued = intval($issued);
 250          $lifetime = intval($lifetime);
 251          $secret = base64_decode($secret);
 252  
 253          return new $class_name(
 254              $handle, $secret, $issued, $lifetime, $assoc_type);
 255      }
 256  
 257      /**
 258       * Generate a signature for a sequence of (key, value) pairs
 259       *
 260       * @access private
 261       * @param array $pairs The pairs to sign, in order.  This is an
 262       * array of two-tuples.
 263       * @return string $signature The binary signature of this sequence
 264       * of pairs
 265       */
 266      function sign($pairs)
 267      {
 268          $kv = Auth_OpenID_KVForm::fromArray($pairs);
 269  
 270          /* Invalid association types should be caught at constructor */
 271          $callback = $this->_macs[$this->assoc_type];
 272  
 273          return call_user_func_array($callback, array($this->secret, $kv));
 274      }
 275  
 276      /**
 277       * Generate a signature for some fields in a dictionary
 278       *
 279       * @access private
 280       * @param array $fields The fields to sign, in order; this is an
 281       * array of strings.
 282       * @param array $data Dictionary of values to sign (an array of
 283       * string => string pairs).
 284       * @return string $signature The signature, base64 encoded
 285       */
 286      function signMessage($message)
 287      {
 288          if ($message->hasKey(Auth_OpenID_OPENID_NS, 'sig') ||
 289              $message->hasKey(Auth_OpenID_OPENID_NS, 'signed')) {
 290              // Already has a sig
 291              return null;
 292          }
 293  
 294          $extant_handle = $message->getArg(Auth_OpenID_OPENID_NS,
 295                                            'assoc_handle');
 296  
 297          if ($extant_handle && ($extant_handle != $this->handle)) {
 298              // raise ValueError("Message has a different association handle")
 299              return null;
 300          }
 301  
 302          $signed_message = $message;
 303          $signed_message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle',
 304                                  $this->handle);
 305  
 306          $message_keys = array_keys($signed_message->toPostArgs());
 307          $signed_list = array();
 308          $signed_prefix = 'openid.';
 309  
 310          foreach ($message_keys as $k) {
 311              if (strpos($k, $signed_prefix) === 0) {
 312                  $signed_list[] = substr($k, strlen($signed_prefix));
 313              }
 314          }
 315  
 316          $signed_list[] = 'signed';
 317          sort($signed_list);
 318  
 319          $signed_message->setArg(Auth_OpenID_OPENID_NS, 'signed',
 320                                  implode(',', $signed_list));
 321          $sig = $this->getMessageSignature($signed_message);
 322          $signed_message->setArg(Auth_OpenID_OPENID_NS, 'sig', $sig);
 323          return $signed_message;
 324      }
 325  
 326      /**
 327       * Given a {@link Auth_OpenID_Message}, return the key/value pairs
 328       * to be signed according to the signed list in the message.  If
 329       * the message lacks a signed list, return null.
 330       *
 331       * @access private
 332       */
 333      function _makePairs(&$message)
 334      {
 335          $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
 336          if (!$signed || Auth_OpenID::isFailure($signed)) {
 337              // raise ValueError('Message has no signed list: %s' % (message,))
 338              return null;
 339          }
 340  
 341          $signed_list = explode(',', $signed);
 342          $pairs = array();
 343          $data = $message->toPostArgs();
 344          foreach ($signed_list as $field) {
 345              $pairs[] = array($field, Auth_OpenID::arrayGet($data,
 346                                                             'openid.' .
 347                                                             $field, ''));
 348          }
 349          return $pairs;
 350      }
 351  
 352      /**
 353       * Given an {@link Auth_OpenID_Message}, return the signature for
 354       * the signed list in the message.
 355       *
 356       * @access private
 357       */
 358      function getMessageSignature(&$message)
 359      {
 360          $pairs = $this->_makePairs($message);
 361          return base64_encode($this->sign($pairs));
 362      }
 363  
 364      /**
 365       * Confirm that the signature of these fields matches the
 366       * signature contained in the data.
 367       *
 368       * @access private
 369       */
 370      function checkMessageSignature(&$message)
 371      {
 372          $sig = $message->getArg(Auth_OpenID_OPENID_NS,
 373                                  'sig');
 374  
 375          if (!$sig || Auth_OpenID::isFailure($sig)) {
 376              return false;
 377          }
 378  
 379          $calculated_sig = $this->getMessageSignature($message);
 380          return $calculated_sig == $sig;
 381      }
 382  }
 383  
 384  function Auth_OpenID_getSecretSize($assoc_type)
 385  {
 386      if ($assoc_type == 'HMAC-SHA1') {
 387          return 20;
 388      } else if ($assoc_type == 'HMAC-SHA256') {
 389          return 32;
 390      } else {
 391          return null;
 392      }
 393  }
 394  
 395  function Auth_OpenID_getAllAssociationTypes()
 396  {
 397      return array('HMAC-SHA1', 'HMAC-SHA256');
 398  }
 399  
 400  function Auth_OpenID_getSupportedAssociationTypes()
 401  {
 402      $a = array('HMAC-SHA1');
 403  
 404      if (Auth_OpenID_HMACSHA256_SUPPORTED) {
 405          $a[] = 'HMAC-SHA256';
 406      }
 407  
 408      return $a;
 409  }
 410  
 411  function Auth_OpenID_getSessionTypes($assoc_type)
 412  {
 413      $assoc_to_session = array(
 414         'HMAC-SHA1' => array('DH-SHA1', 'no-encryption'));
 415  
 416      if (Auth_OpenID_HMACSHA256_SUPPORTED) {
 417          $assoc_to_session['HMAC-SHA256'] =
 418              array('DH-SHA256', 'no-encryption');
 419      }
 420  
 421      return Auth_OpenID::arrayGet($assoc_to_session, $assoc_type, array());
 422  }
 423  
 424  function Auth_OpenID_checkSessionType($assoc_type, $session_type)
 425  {
 426      if (!in_array($session_type,
 427                    Auth_OpenID_getSessionTypes($assoc_type))) {
 428          return false;
 429      }
 430  
 431      return true;
 432  }
 433  
 434  function Auth_OpenID_getDefaultAssociationOrder()
 435  {
 436      $order = array();
 437  
 438      if (!Auth_OpenID_noMathSupport()) {
 439          $order[] = array('HMAC-SHA1', 'DH-SHA1');
 440  
 441          if (Auth_OpenID_HMACSHA256_SUPPORTED) {
 442              $order[] = array('HMAC-SHA256', 'DH-SHA256');
 443          }
 444      }
 445  
 446      $order[] = array('HMAC-SHA1', 'no-encryption');
 447  
 448      if (Auth_OpenID_HMACSHA256_SUPPORTED) {
 449          $order[] = array('HMAC-SHA256', 'no-encryption');
 450      }
 451  
 452      return $order;
 453  }
 454  
 455  function Auth_OpenID_getOnlyEncryptedOrder()
 456  {
 457      $result = array();
 458  
 459      foreach (Auth_OpenID_getDefaultAssociationOrder() as $pair) {
 460          list($assoc, $session) = $pair;
 461  
 462          if ($session != 'no-encryption') {
 463              if (Auth_OpenID_HMACSHA256_SUPPORTED &&
 464                  ($assoc == 'HMAC-SHA256')) {
 465                  $result[] = $pair;
 466              } else if ($assoc != 'HMAC-SHA256') {
 467                  $result[] = $pair;
 468              }
 469          }
 470      }
 471  
 472      return $result;
 473  }
 474  
 475  function &Auth_OpenID_getDefaultNegotiator()
 476  {
 477      $x = new Auth_OpenID_SessionNegotiator(
 478                   Auth_OpenID_getDefaultAssociationOrder());
 479      return $x;
 480  }
 481  
 482  function &Auth_OpenID_getEncryptedNegotiator()
 483  {
 484      $x = new Auth_OpenID_SessionNegotiator(
 485                   Auth_OpenID_getOnlyEncryptedOrder());
 486      return $x;
 487  }
 488  
 489  /**
 490   * A session negotiator controls the allowed and preferred association
 491   * types and association session types. Both the {@link
 492   * Auth_OpenID_Consumer} and {@link Auth_OpenID_Server} use
 493   * negotiators when creating associations.
 494   *
 495   * You can create and use negotiators if you:
 496  
 497   * - Do not want to do Diffie-Hellman key exchange because you use
 498   * transport-layer encryption (e.g. SSL)
 499   *
 500   * - Want to use only SHA-256 associations
 501   *
 502   * - Do not want to support plain-text associations over a non-secure
 503   * channel
 504   *
 505   * It is up to you to set a policy for what kinds of associations to
 506   * accept. By default, the library will make any kind of association
 507   * that is allowed in the OpenID 2.0 specification.
 508   *
 509   * Use of negotiators in the library
 510   * =================================
 511   *
 512   * When a consumer makes an association request, it calls {@link
 513   * getAllowedType} to get the preferred association type and
 514   * association session type.
 515   *
 516   * The server gets a request for a particular association/session type
 517   * and calls {@link isAllowed} to determine if it should create an
 518   * association. If it is supported, negotiation is complete. If it is
 519   * not, the server calls {@link getAllowedType} to get an allowed
 520   * association type to return to the consumer.
 521   *
 522   * If the consumer gets an error response indicating that the
 523   * requested association/session type is not supported by the server
 524   * that contains an assocation/session type to try, it calls {@link
 525   * isAllowed} to determine if it should try again with the given
 526   * combination of association/session type.
 527   *
 528   * @package OpenID
 529   */
 530  class Auth_OpenID_SessionNegotiator {
 531      function Auth_OpenID_SessionNegotiator($allowed_types)
 532      {
 533          $this->allowed_types = array();
 534          $this->setAllowedTypes($allowed_types);
 535      }
 536  
 537      /**
 538       * Set the allowed association types, checking to make sure each
 539       * combination is valid.
 540       *
 541       * @access private
 542       */
 543      function setAllowedTypes($allowed_types)
 544      {
 545          foreach ($allowed_types as $pair) {
 546              list($assoc_type, $session_type) = $pair;
 547              if (!Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
 548                  return false;
 549              }
 550          }
 551  
 552          $this->allowed_types = $allowed_types;
 553          return true;
 554      }
 555  
 556      /**
 557       * Add an association type and session type to the allowed types
 558       * list. The assocation/session pairs are tried in the order that
 559       * they are added.
 560       *
 561       * @access private
 562       */
 563      function addAllowedType($assoc_type, $session_type = null)
 564      {
 565          if ($this->allowed_types === null) {
 566              $this->allowed_types = array();
 567          }
 568  
 569          if ($session_type === null) {
 570              $available = Auth_OpenID_getSessionTypes($assoc_type);
 571  
 572              if (!$available) {
 573                  return false;
 574              }
 575  
 576              foreach ($available as $session_type) {
 577                  $this->addAllowedType($assoc_type, $session_type);
 578              }
 579          } else {
 580              if (Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
 581                  $this->allowed_types[] = array($assoc_type, $session_type);
 582              } else {
 583                  return false;
 584              }
 585          }
 586  
 587          return true;
 588      }
 589  
 590      // Is this combination of association type and session type allowed?
 591      function isAllowed($assoc_type, $session_type)
 592      {
 593          $assoc_good = in_array(array($assoc_type, $session_type),
 594                                 $this->allowed_types);
 595  
 596          $matches = in_array($session_type,
 597                              Auth_OpenID_getSessionTypes($assoc_type));
 598  
 599          return ($assoc_good && $matches);
 600      }
 601  
 602      /**
 603       * Get a pair of assocation type and session type that are
 604       * supported.
 605       */
 606      function getAllowedType()
 607      {
 608          if (!$this->allowed_types) {
 609              return array(null, null);
 610          }
 611  
 612          return $this->allowed_types[0];
 613      }
 614  }
 615  
 616  ?>


Generated: Wed Mar 28 15:54:07 2012 Cross-referenced by PHPXref 0.7.1