| 42 | aurelien | 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 Janrain, Inc.
 | 
        
           |  |  | 14 |  * @license http://www.gnu.org/copyleft/lesser.html LGPL
 | 
        
           |  |  | 15 |  */
 | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | /**
 | 
        
           |  |  | 18 |  * @access private
 | 
        
           |  |  | 19 |  */
 | 
        
           |  |  | 20 | require_once 'Auth/OpenID/CryptUtil.php';
 | 
        
           |  |  | 21 |   | 
        
           |  |  | 22 | /**
 | 
        
           |  |  | 23 |  * @access private
 | 
        
           |  |  | 24 |  */
 | 
        
           |  |  | 25 | require_once 'Auth/OpenID/KVForm.php';
 | 
        
           |  |  | 26 |   | 
        
           |  |  | 27 | /**
 | 
        
           |  |  | 28 |  * This class represents an association between a server and a
 | 
        
           |  |  | 29 |  * consumer.  In general, users of this library will never see
 | 
        
           |  |  | 30 |  * instances of this object.  The only exception is if you implement a
 | 
        
           |  |  | 31 |  * custom {@link Auth_OpenID_OpenIDStore}.
 | 
        
           |  |  | 32 |  *
 | 
        
           |  |  | 33 |  * If you do implement such a store, it will need to store the values
 | 
        
           |  |  | 34 |  * of the handle, secret, issued, lifetime, and assoc_type instance
 | 
        
           |  |  | 35 |  * variables.
 | 
        
           |  |  | 36 |  *
 | 
        
           |  |  | 37 |  * @package OpenID
 | 
        
           |  |  | 38 |  */
 | 
        
           |  |  | 39 | class Auth_OpenID_Association {
 | 
        
           |  |  | 40 |   | 
        
           |  |  | 41 |     /**
 | 
        
           |  |  | 42 |      * This is a HMAC-SHA1 specific value.
 | 
        
           |  |  | 43 |      *
 | 
        
           |  |  | 44 |      * @access private
 | 
        
           |  |  | 45 |      */
 | 
        
           |  |  | 46 |     var $SIG_LENGTH = 20;
 | 
        
           |  |  | 47 |   | 
        
           |  |  | 48 |     /**
 | 
        
           |  |  | 49 |      * The ordering and name of keys as stored by serialize.
 | 
        
           |  |  | 50 |      *
 | 
        
           |  |  | 51 |      * @access private
 | 
        
           |  |  | 52 |      */
 | 
        
           |  |  | 53 |     var $assoc_keys = array(
 | 
        
           |  |  | 54 |                             'version',
 | 
        
           |  |  | 55 |                             'handle',
 | 
        
           |  |  | 56 |                             'secret',
 | 
        
           |  |  | 57 |                             'issued',
 | 
        
           |  |  | 58 |                             'lifetime',
 | 
        
           |  |  | 59 |                             'assoc_type'
 | 
        
           |  |  | 60 |                             );
 | 
        
           |  |  | 61 |   | 
        
           |  |  | 62 |     /**
 | 
        
           |  |  | 63 |      * This is an alternate constructor (factory method) used by the
 | 
        
           |  |  | 64 |      * OpenID consumer library to create associations.  OpenID store
 | 
        
           |  |  | 65 |      * implementations shouldn't use this constructor.
 | 
        
           |  |  | 66 |      *
 | 
        
           |  |  | 67 |      * @access private
 | 
        
           |  |  | 68 |      *
 | 
        
           |  |  | 69 |      * @param integer $expires_in This is the amount of time this
 | 
        
           |  |  | 70 |      * association is good for, measured in seconds since the
 | 
        
           |  |  | 71 |      * association was issued.
 | 
        
           |  |  | 72 |      *
 | 
        
           |  |  | 73 |      * @param string $handle This is the handle the server gave this
 | 
        
           |  |  | 74 |      * association.
 | 
        
           |  |  | 75 |      *
 | 
        
           |  |  | 76 |      * @param string secret This is the shared secret the server
 | 
        
           |  |  | 77 |      * generated for this association.
 | 
        
           |  |  | 78 |      *
 | 
        
           |  |  | 79 |      * @param assoc_type This is the type of association this
 | 
        
           |  |  | 80 |      * instance represents.  The only valid value of this field at
 | 
        
           |  |  | 81 |      * this time is 'HMAC-SHA1', but new types may be defined in the
 | 
        
           |  |  | 82 |      * future.
 | 
        
           |  |  | 83 |      *
 | 
        
           |  |  | 84 |      * @return association An {@link Auth_OpenID_Association}
 | 
        
           |  |  | 85 |      * instance.
 | 
        
           |  |  | 86 |      */
 | 
        
           |  |  | 87 |     function fromExpiresIn($expires_in, $handle, $secret, $assoc_type)
 | 
        
           |  |  | 88 |     {
 | 
        
           |  |  | 89 |         $issued = time();
 | 
        
           |  |  | 90 |         $lifetime = $expires_in;
 | 
        
           |  |  | 91 |         return new Auth_OpenID_Association($handle, $secret,
 | 
        
           |  |  | 92 |                                            $issued, $lifetime, $assoc_type);
 | 
        
           |  |  | 93 |     }
 | 
        
           |  |  | 94 |   | 
        
           |  |  | 95 |     /**
 | 
        
           |  |  | 96 |      * This is the standard constructor for creating an association.
 | 
        
           |  |  | 97 |      * The library should create all of the necessary associations, so
 | 
        
           |  |  | 98 |      * this constructor is not part of the external API.
 | 
        
           |  |  | 99 |      *
 | 
        
           |  |  | 100 |      * @access private
 | 
        
           |  |  | 101 |      *
 | 
        
           |  |  | 102 |      * @param string $handle This is the handle the server gave this
 | 
        
           |  |  | 103 |      * association.
 | 
        
           |  |  | 104 |      *
 | 
        
           |  |  | 105 |      * @param string $secret This is the shared secret the server
 | 
        
           |  |  | 106 |      * generated for this association.
 | 
        
           |  |  | 107 |      *
 | 
        
           |  |  | 108 |      * @param integer $issued This is the time this association was
 | 
        
           |  |  | 109 |      * issued, in seconds since 00:00 GMT, January 1, 1970.  (ie, a
 | 
        
           |  |  | 110 |      * unix timestamp)
 | 
        
           |  |  | 111 |      *
 | 
        
           |  |  | 112 |      * @param integer $lifetime This is the amount of time this
 | 
        
           |  |  | 113 |      * association is good for, measured in seconds since the
 | 
        
           |  |  | 114 |      * association was issued.
 | 
        
           |  |  | 115 |      *
 | 
        
           |  |  | 116 |      * @param string $assoc_type This is the type of association this
 | 
        
           |  |  | 117 |      * instance represents.  The only valid value of this field at
 | 
        
           |  |  | 118 |      * this time is 'HMAC-SHA1', but new types may be defined in the
 | 
        
           |  |  | 119 |      * future.
 | 
        
           |  |  | 120 |      */
 | 
        
           |  |  | 121 |     function Auth_OpenID_Association(
 | 
        
           |  |  | 122 |         $handle, $secret, $issued, $lifetime, $assoc_type)
 | 
        
           |  |  | 123 |     {
 | 
        
           |  |  | 124 |         if ($assoc_type != 'HMAC-SHA1') {
 | 
        
           |  |  | 125 |             $fmt = 'HMAC-SHA1 is the only supported association type (got %s)';
 | 
        
           |  |  | 126 |             trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR);
 | 
        
           |  |  | 127 |         }
 | 
        
           |  |  | 128 |   | 
        
           |  |  | 129 |         $this->handle = $handle;
 | 
        
           |  |  | 130 |         $this->secret = $secret;
 | 
        
           |  |  | 131 |         $this->issued = $issued;
 | 
        
           |  |  | 132 |         $this->lifetime = $lifetime;
 | 
        
           |  |  | 133 |         $this->assoc_type = $assoc_type;
 | 
        
           |  |  | 134 |     }
 | 
        
           |  |  | 135 |   | 
        
           |  |  | 136 |     /**
 | 
        
           |  |  | 137 |      * This returns the number of seconds this association is still
 | 
        
           |  |  | 138 |      * valid for, or 0 if the association is no longer valid.
 | 
        
           |  |  | 139 |      *
 | 
        
           |  |  | 140 |      * @return integer $seconds The number of seconds this association
 | 
        
           |  |  | 141 |      * is still valid for, or 0 if the association is no longer valid.
 | 
        
           |  |  | 142 |      */
 | 
        
           |  |  | 143 |     function getExpiresIn($now = null)
 | 
        
           |  |  | 144 |     {
 | 
        
           |  |  | 145 |         if ($now == null) {
 | 
        
           |  |  | 146 |             $now = time();
 | 
        
           |  |  | 147 |         }
 | 
        
           |  |  | 148 |   | 
        
           |  |  | 149 |         return max(0, $this->issued + $this->lifetime - $now);
 | 
        
           |  |  | 150 |     }
 | 
        
           |  |  | 151 |   | 
        
           |  |  | 152 |     /**
 | 
        
           |  |  | 153 |      * This checks to see if two {@link Auth_OpenID_Association}
 | 
        
           |  |  | 154 |      * instances represent the same association.
 | 
        
           |  |  | 155 |      *
 | 
        
           |  |  | 156 |      * @return bool $result true if the two instances represent the
 | 
        
           |  |  | 157 |      * same association, false otherwise.
 | 
        
           |  |  | 158 |      */
 | 
        
           |  |  | 159 |     function equal($other)
 | 
        
           |  |  | 160 |     {
 | 
        
           |  |  | 161 |         return ((gettype($this) == gettype($other))
 | 
        
           |  |  | 162 |                 && ($this->handle == $other->handle)
 | 
        
           |  |  | 163 |                 && ($this->secret == $other->secret)
 | 
        
           |  |  | 164 |                 && ($this->issued == $other->issued)
 | 
        
           |  |  | 165 |                 && ($this->lifetime == $other->lifetime)
 | 
        
           |  |  | 166 |                 && ($this->assoc_type == $other->assoc_type));
 | 
        
           |  |  | 167 |     }
 | 
        
           |  |  | 168 |   | 
        
           |  |  | 169 |     /**
 | 
        
           |  |  | 170 |      * Convert an association to KV form.
 | 
        
           |  |  | 171 |      *
 | 
        
           |  |  | 172 |      * @return string $result String in KV form suitable for
 | 
        
           |  |  | 173 |      * deserialization by deserialize.
 | 
        
           |  |  | 174 |      */
 | 
        
           |  |  | 175 |     function serialize()
 | 
        
           |  |  | 176 |     {
 | 
        
           |  |  | 177 |         $data = array(
 | 
        
           |  |  | 178 |                      'version' => '2',
 | 
        
           |  |  | 179 |                      'handle' => $this->handle,
 | 
        
           |  |  | 180 |                      'secret' => base64_encode($this->secret),
 | 
        
           |  |  | 181 |                      'issued' => strval(intval($this->issued)),
 | 
        
           |  |  | 182 |                      'lifetime' => strval(intval($this->lifetime)),
 | 
        
           |  |  | 183 |                      'assoc_type' => $this->assoc_type
 | 
        
           |  |  | 184 |                      );
 | 
        
           |  |  | 185 |   | 
        
           |  |  | 186 |         assert(array_keys($data) == $this->assoc_keys);
 | 
        
           |  |  | 187 |   | 
        
           |  |  | 188 |         return Auth_OpenID_KVForm::fromArray($data, $strict = true);
 | 
        
           |  |  | 189 |     }
 | 
        
           |  |  | 190 |   | 
        
           |  |  | 191 |     /**
 | 
        
           |  |  | 192 |      * Parse an association as stored by serialize().  This is the
 | 
        
           |  |  | 193 |      * inverse of serialize.
 | 
        
           |  |  | 194 |      *
 | 
        
           |  |  | 195 |      * @param string $assoc_s Association as serialized by serialize()
 | 
        
           |  |  | 196 |      * @return Auth_OpenID_Association $result instance of this class
 | 
        
           |  |  | 197 |      */
 | 
        
           |  |  | 198 |     function deserialize($class_name, $assoc_s)
 | 
        
           |  |  | 199 |     {
 | 
        
           |  |  | 200 |         $pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true);
 | 
        
           |  |  | 201 |         $keys = array();
 | 
        
           |  |  | 202 |         $values = array();
 | 
        
           |  |  | 203 |         foreach ($pairs as $key => $value) {
 | 
        
           |  |  | 204 |             if (is_array($value)) {
 | 
        
           |  |  | 205 |                 list($key, $value) = $value;
 | 
        
           |  |  | 206 |             }
 | 
        
           |  |  | 207 |             $keys[] = $key;
 | 
        
           |  |  | 208 |             $values[] = $value;
 | 
        
           |  |  | 209 |         }
 | 
        
           |  |  | 210 |   | 
        
           |  |  | 211 |         $class_vars = get_class_vars($class_name);
 | 
        
           |  |  | 212 |         $class_assoc_keys = $class_vars['assoc_keys'];
 | 
        
           |  |  | 213 |   | 
        
           |  |  | 214 |         sort($keys);
 | 
        
           |  |  | 215 |         sort($class_assoc_keys);
 | 
        
           |  |  | 216 |   | 
        
           |  |  | 217 |         if ($keys != $class_assoc_keys) {
 | 
        
           |  |  | 218 |             trigger_error('Unexpected key values: ' . strval($keys),
 | 
        
           |  |  | 219 |                           E_USER_WARNING);
 | 
        
           |  |  | 220 |             return null;
 | 
        
           |  |  | 221 |         }
 | 
        
           |  |  | 222 |   | 
        
           |  |  | 223 |         $version = $pairs['version'];
 | 
        
           |  |  | 224 |         $handle = $pairs['handle'];
 | 
        
           |  |  | 225 |         $secret = $pairs['secret'];
 | 
        
           |  |  | 226 |         $issued = $pairs['issued'];
 | 
        
           |  |  | 227 |         $lifetime = $pairs['lifetime'];
 | 
        
           |  |  | 228 |         $assoc_type = $pairs['assoc_type'];
 | 
        
           |  |  | 229 |   | 
        
           |  |  | 230 |         if ($version != '2') {
 | 
        
           |  |  | 231 |             trigger_error('Unknown version: ' . $version, E_USER_WARNING);
 | 
        
           |  |  | 232 |             return null;
 | 
        
           |  |  | 233 |         }
 | 
        
           |  |  | 234 |   | 
        
           |  |  | 235 |         $issued = intval($issued);
 | 
        
           |  |  | 236 |         $lifetime = intval($lifetime);
 | 
        
           |  |  | 237 |         $secret = base64_decode($secret);
 | 
        
           |  |  | 238 |   | 
        
           |  |  | 239 |         return new $class_name(
 | 
        
           |  |  | 240 |             $handle, $secret, $issued, $lifetime, $assoc_type);
 | 
        
           |  |  | 241 |     }
 | 
        
           |  |  | 242 |   | 
        
           |  |  | 243 |     /**
 | 
        
           |  |  | 244 |      * Generate a signature for a sequence of (key, value) pairs
 | 
        
           |  |  | 245 |      *
 | 
        
           |  |  | 246 |      * @access private
 | 
        
           |  |  | 247 |      * @param array $pairs The pairs to sign, in order.  This is an
 | 
        
           |  |  | 248 |      * array of two-tuples.
 | 
        
           |  |  | 249 |      * @return string $signature The binary signature of this sequence
 | 
        
           |  |  | 250 |      * of pairs
 | 
        
           |  |  | 251 |      */
 | 
        
           |  |  | 252 |     function sign($pairs)
 | 
        
           |  |  | 253 |     {
 | 
        
           |  |  | 254 |         $kv = Auth_OpenID_KVForm::fromArray($pairs);
 | 
        
           |  |  | 255 |         return Auth_OpenID_HMACSHA1($this->secret, $kv);
 | 
        
           |  |  | 256 |     }
 | 
        
           |  |  | 257 |   | 
        
           |  |  | 258 |     /**
 | 
        
           |  |  | 259 |      * Generate a signature for some fields in a dictionary
 | 
        
           |  |  | 260 |      *
 | 
        
           |  |  | 261 |      * @access private
 | 
        
           |  |  | 262 |      * @param array $fields The fields to sign, in order; this is an
 | 
        
           |  |  | 263 |      * array of strings.
 | 
        
           |  |  | 264 |      * @param array $data Dictionary of values to sign (an array of
 | 
        
           |  |  | 265 |      * string => string pairs).
 | 
        
           |  |  | 266 |      * @return string $signature The signature, base64 encoded
 | 
        
           |  |  | 267 |      */
 | 
        
           |  |  | 268 |     function signDict($fields, $data, $prefix = 'openid.')
 | 
        
           |  |  | 269 |     {
 | 
        
           |  |  | 270 |         $pairs = array();
 | 
        
           |  |  | 271 |         foreach ($fields as $field) {
 | 
        
           |  |  | 272 |             $pairs[] = array($field, $data[$prefix . $field]);
 | 
        
           |  |  | 273 |         }
 | 
        
           |  |  | 274 |   | 
        
           |  |  | 275 |         return base64_encode($this->sign($pairs));
 | 
        
           |  |  | 276 |     }
 | 
        
           |  |  | 277 |   | 
        
           |  |  | 278 |     /**
 | 
        
           |  |  | 279 |      * Add a signature to an array of fields
 | 
        
           |  |  | 280 |      *
 | 
        
           |  |  | 281 |      * @access private
 | 
        
           |  |  | 282 |      */
 | 
        
           |  |  | 283 |     function addSignature($fields, &$data, $prefix = 'openid.')
 | 
        
           |  |  | 284 |     {
 | 
        
           |  |  | 285 |         $sig = $this->signDict($fields, $data, $prefix);
 | 
        
           |  |  | 286 |         $signed = implode(",", $fields);
 | 
        
           |  |  | 287 |         $data[$prefix . 'sig'] = $sig;
 | 
        
           |  |  | 288 |         $data[$prefix . 'signed'] = $signed;
 | 
        
           |  |  | 289 |     }
 | 
        
           |  |  | 290 |   | 
        
           |  |  | 291 |     /**
 | 
        
           |  |  | 292 |      * Confirm that the signature of these fields matches the
 | 
        
           |  |  | 293 |      * signature contained in the data
 | 
        
           |  |  | 294 |      *
 | 
        
           |  |  | 295 |      * @access private
 | 
        
           |  |  | 296 |      */
 | 
        
           |  |  | 297 |     function checkSignature($data, $prefix = 'openid.')
 | 
        
           |  |  | 298 |     {
 | 
        
           |  |  | 299 |         $signed = $data[$prefix . 'signed'];
 | 
        
           |  |  | 300 |         $fields = explode(",", $signed);
 | 
        
           |  |  | 301 |         $expected_sig = $this->signDict($fields, $data, $prefix);
 | 
        
           |  |  | 302 |         $request_sig = $data[$prefix . 'sig'];
 | 
        
           |  |  | 303 |   | 
        
           |  |  | 304 |         return ($request_sig == $expected_sig);
 | 
        
           |  |  | 305 |     }
 | 
        
           |  |  | 306 | }
 | 
        
           |  |  | 307 |   | 
        
           |  |  | 308 | ?>
 |