|
TYPO3 API
SVNRelease
|
00001 <?php 00002 00003 /** 00004 * This module contains code for dealing with associations between 00005 * consumers and servers. 00006 * 00007 * PHP versions 4 and 5 00008 * 00009 * LICENSE: See the COPYING file included in this distribution. 00010 * 00011 * @package OpenID 00012 * @author JanRain, Inc. <openid@janrain.com> 00013 * @copyright 2005-2008 Janrain, Inc. 00014 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache 00015 */ 00016 00017 /** 00018 * @access private 00019 */ 00020 require_once 'Auth/OpenID/CryptUtil.php'; 00021 00022 /** 00023 * @access private 00024 */ 00025 require_once 'Auth/OpenID/KVForm.php'; 00026 00027 /** 00028 * @access private 00029 */ 00030 require_once 'Auth/OpenID/HMAC.php'; 00031 00032 /** 00033 * This class represents an association between a server and a 00034 * consumer. In general, users of this library will never see 00035 * instances of this object. The only exception is if you implement a 00036 * custom {@link Auth_OpenID_OpenIDStore}. 00037 * 00038 * If you do implement such a store, it will need to store the values 00039 * of the handle, secret, issued, lifetime, and assoc_type instance 00040 * variables. 00041 * 00042 * @package OpenID 00043 */ 00044 class Auth_OpenID_Association { 00045 00046 /** 00047 * This is a HMAC-SHA1 specific value. 00048 * 00049 * @access private 00050 */ 00051 var $SIG_LENGTH = 20; 00052 00053 /** 00054 * The ordering and name of keys as stored by serialize. 00055 * 00056 * @access private 00057 */ 00058 var $assoc_keys = array( 00059 'version', 00060 'handle', 00061 'secret', 00062 'issued', 00063 'lifetime', 00064 'assoc_type' 00065 ); 00066 00067 var $_macs = array( 00068 'HMAC-SHA1' => 'Auth_OpenID_HMACSHA1', 00069 'HMAC-SHA256' => 'Auth_OpenID_HMACSHA256' 00070 ); 00071 00072 /** 00073 * This is an alternate constructor (factory method) used by the 00074 * OpenID consumer library to create associations. OpenID store 00075 * implementations shouldn't use this constructor. 00076 * 00077 * @access private 00078 * 00079 * @param integer $expires_in This is the amount of time this 00080 * association is good for, measured in seconds since the 00081 * association was issued. 00082 * 00083 * @param string $handle This is the handle the server gave this 00084 * association. 00085 * 00086 * @param string secret This is the shared secret the server 00087 * generated for this association. 00088 * 00089 * @param assoc_type This is the type of association this 00090 * instance represents. The only valid values of this field at 00091 * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may 00092 * be defined in the future. 00093 * 00094 * @return association An {@link Auth_OpenID_Association} 00095 * instance. 00096 */ 00097 function fromExpiresIn($expires_in, $handle, $secret, $assoc_type) 00098 { 00099 $issued = time(); 00100 $lifetime = $expires_in; 00101 return new Auth_OpenID_Association($handle, $secret, 00102 $issued, $lifetime, $assoc_type); 00103 } 00104 00105 /** 00106 * This is the standard constructor for creating an association. 00107 * The library should create all of the necessary associations, so 00108 * this constructor is not part of the external API. 00109 * 00110 * @access private 00111 * 00112 * @param string $handle This is the handle the server gave this 00113 * association. 00114 * 00115 * @param string $secret This is the shared secret the server 00116 * generated for this association. 00117 * 00118 * @param integer $issued This is the time this association was 00119 * issued, in seconds since 00:00 GMT, January 1, 1970. (ie, a 00120 * unix timestamp) 00121 * 00122 * @param integer $lifetime This is the amount of time this 00123 * association is good for, measured in seconds since the 00124 * association was issued. 00125 * 00126 * @param string $assoc_type This is the type of association this 00127 * instance represents. The only valid values of this field at 00128 * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may 00129 * be defined in the future. 00130 */ 00131 function Auth_OpenID_Association( 00132 $handle, $secret, $issued, $lifetime, $assoc_type) 00133 { 00134 if (!in_array($assoc_type, 00135 Auth_OpenID_getSupportedAssociationTypes())) { 00136 $fmt = 'Unsupported association type (%s)'; 00137 trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR); 00138 } 00139 00140 $this->handle = $handle; 00141 $this->secret = $secret; 00142 $this->issued = $issued; 00143 $this->lifetime = $lifetime; 00144 $this->assoc_type = $assoc_type; 00145 } 00146 00147 /** 00148 * This returns the number of seconds this association is still 00149 * valid for, or 0 if the association is no longer valid. 00150 * 00151 * @return integer $seconds The number of seconds this association 00152 * is still valid for, or 0 if the association is no longer valid. 00153 */ 00154 function getExpiresIn($now = null) 00155 { 00156 if ($now == null) { 00157 $now = time(); 00158 } 00159 00160 return max(0, $this->issued + $this->lifetime - $now); 00161 } 00162 00163 /** 00164 * This checks to see if two {@link Auth_OpenID_Association} 00165 * instances represent the same association. 00166 * 00167 * @return bool $result true if the two instances represent the 00168 * same association, false otherwise. 00169 */ 00170 function equal($other) 00171 { 00172 return ((gettype($this) == gettype($other)) 00173 && ($this->handle == $other->handle) 00174 && ($this->secret == $other->secret) 00175 && ($this->issued == $other->issued) 00176 && ($this->lifetime == $other->lifetime) 00177 && ($this->assoc_type == $other->assoc_type)); 00178 } 00179 00180 /** 00181 * Convert an association to KV form. 00182 * 00183 * @return string $result String in KV form suitable for 00184 * deserialization by deserialize. 00185 */ 00186 function serialize() 00187 { 00188 $data = array( 00189 'version' => '2', 00190 'handle' => $this->handle, 00191 'secret' => base64_encode($this->secret), 00192 'issued' => strval(intval($this->issued)), 00193 'lifetime' => strval(intval($this->lifetime)), 00194 'assoc_type' => $this->assoc_type 00195 ); 00196 00197 assert(array_keys($data) == $this->assoc_keys); 00198 00199 return Auth_OpenID_KVForm::fromArray($data, $strict = true); 00200 } 00201 00202 /** 00203 * Parse an association as stored by serialize(). This is the 00204 * inverse of serialize. 00205 * 00206 * @param string $assoc_s Association as serialized by serialize() 00207 * @return Auth_OpenID_Association $result instance of this class 00208 */ 00209 function deserialize($class_name, $assoc_s) 00210 { 00211 $pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true); 00212 $keys = array(); 00213 $values = array(); 00214 foreach ($pairs as $key => $value) { 00215 if (is_array($value)) { 00216 list($key, $value) = $value; 00217 } 00218 $keys[] = $key; 00219 $values[] = $value; 00220 } 00221 00222 $class_vars = get_class_vars($class_name); 00223 $class_assoc_keys = $class_vars['assoc_keys']; 00224 00225 sort($keys); 00226 sort($class_assoc_keys); 00227 00228 if ($keys != $class_assoc_keys) { 00229 trigger_error('Unexpected key values: ' . var_export($keys, true), 00230 E_USER_WARNING); 00231 return null; 00232 } 00233 00234 $version = $pairs['version']; 00235 $handle = $pairs['handle']; 00236 $secret = $pairs['secret']; 00237 $issued = $pairs['issued']; 00238 $lifetime = $pairs['lifetime']; 00239 $assoc_type = $pairs['assoc_type']; 00240 00241 if ($version != '2') { 00242 trigger_error('Unknown version: ' . $version, E_USER_WARNING); 00243 return null; 00244 } 00245 00246 $issued = intval($issued); 00247 $lifetime = intval($lifetime); 00248 $secret = base64_decode($secret); 00249 00250 return new $class_name( 00251 $handle, $secret, $issued, $lifetime, $assoc_type); 00252 } 00253 00254 /** 00255 * Generate a signature for a sequence of (key, value) pairs 00256 * 00257 * @access private 00258 * @param array $pairs The pairs to sign, in order. This is an 00259 * array of two-tuples. 00260 * @return string $signature The binary signature of this sequence 00261 * of pairs 00262 */ 00263 function sign($pairs) 00264 { 00265 $kv = Auth_OpenID_KVForm::fromArray($pairs); 00266 00267 /* Invalid association types should be caught at constructor */ 00268 $callback = $this->_macs[$this->assoc_type]; 00269 00270 return call_user_func_array($callback, array($this->secret, $kv)); 00271 } 00272 00273 /** 00274 * Generate a signature for some fields in a dictionary 00275 * 00276 * @access private 00277 * @param array $fields The fields to sign, in order; this is an 00278 * array of strings. 00279 * @param array $data Dictionary of values to sign (an array of 00280 * string => string pairs). 00281 * @return string $signature The signature, base64 encoded 00282 */ 00283 function signMessage($message) 00284 { 00285 if ($message->hasKey(Auth_OpenID_OPENID_NS, 'sig') || 00286 $message->hasKey(Auth_OpenID_OPENID_NS, 'signed')) { 00287 // Already has a sig 00288 return null; 00289 } 00290 00291 $extant_handle = $message->getArg(Auth_OpenID_OPENID_NS, 00292 'assoc_handle'); 00293 00294 if ($extant_handle && ($extant_handle != $this->handle)) { 00295 // raise ValueError("Message has a different association handle") 00296 return null; 00297 } 00298 00299 $signed_message = $message; 00300 $signed_message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle', 00301 $this->handle); 00302 00303 $message_keys = array_keys($signed_message->toPostArgs()); 00304 $signed_list = array(); 00305 $signed_prefix = 'openid.'; 00306 00307 foreach ($message_keys as $k) { 00308 if (strpos($k, $signed_prefix) === 0) { 00309 $signed_list[] = substr($k, strlen($signed_prefix)); 00310 } 00311 } 00312 00313 $signed_list[] = 'signed'; 00314 sort($signed_list); 00315 00316 $signed_message->setArg(Auth_OpenID_OPENID_NS, 'signed', 00317 implode(',', $signed_list)); 00318 $sig = $this->getMessageSignature($signed_message); 00319 $signed_message->setArg(Auth_OpenID_OPENID_NS, 'sig', $sig); 00320 return $signed_message; 00321 } 00322 00323 /** 00324 * Given a {@link Auth_OpenID_Message}, return the key/value pairs 00325 * to be signed according to the signed list in the message. If 00326 * the message lacks a signed list, return null. 00327 * 00328 * @access private 00329 */ 00330 function _makePairs($message) 00331 { 00332 $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed'); 00333 if (!$signed || Auth_OpenID::isFailure($signed)) { 00334 // raise ValueError('Message has no signed list: %s' % (message,)) 00335 return null; 00336 } 00337 00338 $signed_list = explode(',', $signed); 00339 $pairs = array(); 00340 $data = $message->toPostArgs(); 00341 foreach ($signed_list as $field) { 00342 $pairs[] = array($field, Auth_OpenID::arrayGet($data, 00343 'openid.' . 00344 $field, '')); 00345 } 00346 return $pairs; 00347 } 00348 00349 /** 00350 * Given an {@link Auth_OpenID_Message}, return the signature for 00351 * the signed list in the message. 00352 * 00353 * @access private 00354 */ 00355 function getMessageSignature($message) 00356 { 00357 $pairs = $this->_makePairs($message); 00358 return base64_encode($this->sign($pairs)); 00359 } 00360 00361 /** 00362 * Confirm that the signature of these fields matches the 00363 * signature contained in the data. 00364 * 00365 * @access private 00366 */ 00367 function checkMessageSignature($message) 00368 { 00369 $sig = $message->getArg(Auth_OpenID_OPENID_NS, 00370 'sig'); 00371 00372 if (!$sig || Auth_OpenID::isFailure($sig)) { 00373 return false; 00374 } 00375 00376 $calculated_sig = $this->getMessageSignature($message); 00377 return $calculated_sig == $sig; 00378 } 00379 } 00380 00381 function Auth_OpenID_getSecretSize($assoc_type) 00382 { 00383 if ($assoc_type == 'HMAC-SHA1') { 00384 return 20; 00385 } else if ($assoc_type == 'HMAC-SHA256') { 00386 return 32; 00387 } else { 00388 return null; 00389 } 00390 } 00391 00392 function Auth_OpenID_getAllAssociationTypes() 00393 { 00394 return array('HMAC-SHA1', 'HMAC-SHA256'); 00395 } 00396 00397 function Auth_OpenID_getSupportedAssociationTypes() 00398 { 00399 $a = array('HMAC-SHA1'); 00400 00401 if (Auth_OpenID_HMACSHA256_SUPPORTED) { 00402 $a[] = 'HMAC-SHA256'; 00403 } 00404 00405 return $a; 00406 } 00407 00408 function Auth_OpenID_getSessionTypes($assoc_type) 00409 { 00410 $assoc_to_session = array( 00411 'HMAC-SHA1' => array('DH-SHA1', 'no-encryption')); 00412 00413 if (Auth_OpenID_HMACSHA256_SUPPORTED) { 00414 $assoc_to_session['HMAC-SHA256'] = 00415 array('DH-SHA256', 'no-encryption'); 00416 } 00417 00418 return Auth_OpenID::arrayGet($assoc_to_session, $assoc_type, array()); 00419 } 00420 00421 function Auth_OpenID_checkSessionType($assoc_type, $session_type) 00422 { 00423 if (!in_array($session_type, 00424 Auth_OpenID_getSessionTypes($assoc_type))) { 00425 return false; 00426 } 00427 00428 return true; 00429 } 00430 00431 function Auth_OpenID_getDefaultAssociationOrder() 00432 { 00433 $order = array(); 00434 00435 if (!Auth_OpenID_noMathSupport()) { 00436 $order[] = array('HMAC-SHA1', 'DH-SHA1'); 00437 00438 if (Auth_OpenID_HMACSHA256_SUPPORTED) { 00439 $order[] = array('HMAC-SHA256', 'DH-SHA256'); 00440 } 00441 } 00442 00443 $order[] = array('HMAC-SHA1', 'no-encryption'); 00444 00445 if (Auth_OpenID_HMACSHA256_SUPPORTED) { 00446 $order[] = array('HMAC-SHA256', 'no-encryption'); 00447 } 00448 00449 return $order; 00450 } 00451 00452 function Auth_OpenID_getOnlyEncryptedOrder() 00453 { 00454 $result = array(); 00455 00456 foreach (Auth_OpenID_getDefaultAssociationOrder() as $pair) { 00457 list($assoc, $session) = $pair; 00458 00459 if ($session != 'no-encryption') { 00460 if (Auth_OpenID_HMACSHA256_SUPPORTED && 00461 ($assoc == 'HMAC-SHA256')) { 00462 $result[] = $pair; 00463 } else if ($assoc != 'HMAC-SHA256') { 00464 $result[] = $pair; 00465 } 00466 } 00467 } 00468 00469 return $result; 00470 } 00471 00472 function &Auth_OpenID_getDefaultNegotiator() 00473 { 00474 $x = new Auth_OpenID_SessionNegotiator( 00475 Auth_OpenID_getDefaultAssociationOrder()); 00476 return $x; 00477 } 00478 00479 function &Auth_OpenID_getEncryptedNegotiator() 00480 { 00481 $x = new Auth_OpenID_SessionNegotiator( 00482 Auth_OpenID_getOnlyEncryptedOrder()); 00483 return $x; 00484 } 00485 00486 /** 00487 * A session negotiator controls the allowed and preferred association 00488 * types and association session types. Both the {@link 00489 * Auth_OpenID_Consumer} and {@link Auth_OpenID_Server} use 00490 * negotiators when creating associations. 00491 * 00492 * You can create and use negotiators if you: 00493 00494 * - Do not want to do Diffie-Hellman key exchange because you use 00495 * transport-layer encryption (e.g. SSL) 00496 * 00497 * - Want to use only SHA-256 associations 00498 * 00499 * - Do not want to support plain-text associations over a non-secure 00500 * channel 00501 * 00502 * It is up to you to set a policy for what kinds of associations to 00503 * accept. By default, the library will make any kind of association 00504 * that is allowed in the OpenID 2.0 specification. 00505 * 00506 * Use of negotiators in the library 00507 * ================================= 00508 * 00509 * When a consumer makes an association request, it calls {@link 00510 * getAllowedType} to get the preferred association type and 00511 * association session type. 00512 * 00513 * The server gets a request for a particular association/session type 00514 * and calls {@link isAllowed} to determine if it should create an 00515 * association. If it is supported, negotiation is complete. If it is 00516 * not, the server calls {@link getAllowedType} to get an allowed 00517 * association type to return to the consumer. 00518 * 00519 * If the consumer gets an error response indicating that the 00520 * requested association/session type is not supported by the server 00521 * that contains an assocation/session type to try, it calls {@link 00522 * isAllowed} to determine if it should try again with the given 00523 * combination of association/session type. 00524 * 00525 * @package OpenID 00526 */ 00527 class Auth_OpenID_SessionNegotiator { 00528 function Auth_OpenID_SessionNegotiator($allowed_types) 00529 { 00530 $this->allowed_types = array(); 00531 $this->setAllowedTypes($allowed_types); 00532 } 00533 00534 /** 00535 * Set the allowed association types, checking to make sure each 00536 * combination is valid. 00537 * 00538 * @access private 00539 */ 00540 function setAllowedTypes($allowed_types) 00541 { 00542 foreach ($allowed_types as $pair) { 00543 list($assoc_type, $session_type) = $pair; 00544 if (!Auth_OpenID_checkSessionType($assoc_type, $session_type)) { 00545 return false; 00546 } 00547 } 00548 00549 $this->allowed_types = $allowed_types; 00550 return true; 00551 } 00552 00553 /** 00554 * Add an association type and session type to the allowed types 00555 * list. The assocation/session pairs are tried in the order that 00556 * they are added. 00557 * 00558 * @access private 00559 */ 00560 function addAllowedType($assoc_type, $session_type = null) 00561 { 00562 if ($this->allowed_types === null) { 00563 $this->allowed_types = array(); 00564 } 00565 00566 if ($session_type === null) { 00567 $available = Auth_OpenID_getSessionTypes($assoc_type); 00568 00569 if (!$available) { 00570 return false; 00571 } 00572 00573 foreach ($available as $session_type) { 00574 $this->addAllowedType($assoc_type, $session_type); 00575 } 00576 } else { 00577 if (Auth_OpenID_checkSessionType($assoc_type, $session_type)) { 00578 $this->allowed_types[] = array($assoc_type, $session_type); 00579 } else { 00580 return false; 00581 } 00582 } 00583 00584 return true; 00585 } 00586 00587 // Is this combination of association type and session type allowed? 00588 function isAllowed($assoc_type, $session_type) 00589 { 00590 $assoc_good = in_array(array($assoc_type, $session_type), 00591 $this->allowed_types); 00592 00593 $matches = in_array($session_type, 00594 Auth_OpenID_getSessionTypes($assoc_type)); 00595 00596 return ($assoc_good && $matches); 00597 } 00598 00599 /** 00600 * Get a pair of assocation type and session type that are 00601 * supported. 00602 */ 00603 function getAllowedType() 00604 { 00605 if (!$this->allowed_types) { 00606 return array(null, null); 00607 } 00608 00609 return $this->allowed_types[0]; 00610 } 00611 } 00612 00613 ?>
1.8.0