1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16:
17: 18: 19:
20: require_once 'Auth/OpenID/CryptUtil.php';
21:
22: 23: 24:
25: require_once 'Auth/OpenID/KVForm.php';
26:
27: 28: 29:
30: require_once 'Auth/OpenID/HMAC.php';
31:
32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43:
44: class Auth_OpenID_Association {
45:
46: 47: 48: 49: 50:
51: var $SIG_LENGTH = 20;
52:
53: 54: 55: 56: 57:
58: var $assoc_keys = array(
59: 'version',
60: 'handle',
61: 'secret',
62: 'issued',
63: 'lifetime',
64: 'assoc_type'
65: );
66:
67: var $_macs = array(
68: 'HMAC-SHA1' => 'Auth_OpenID_HMACSHA1',
69: 'HMAC-SHA256' => 'Auth_OpenID_HMACSHA256'
70: );
71:
72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96:
97: static function fromExpiresIn($expires_in, $handle, $secret, $assoc_type)
98: {
99: $issued = time();
100: $lifetime = $expires_in;
101: return new Auth_OpenID_Association($handle, $secret,
102: $issued, $lifetime, $assoc_type);
103: }
104:
105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130:
131: function Auth_OpenID_Association(
132: $handle, $secret, $issued, $lifetime, $assoc_type)
133: {
134: if (!in_array($assoc_type,
135: Auth_OpenID_getSupportedAssociationTypes(), true)) {
136: $fmt = 'Unsupported association type (%s)';
137: trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR);
138: }
139:
140: $this->handle = $handle;
141: $this->secret = $secret;
142: $this->issued = $issued;
143: $this->lifetime = $lifetime;
144: $this->assoc_type = $assoc_type;
145: }
146:
147: 148: 149: 150: 151: 152: 153:
154: function getExpiresIn($now = null)
155: {
156: if ($now == null) {
157: $now = time();
158: }
159:
160: return max(0, $this->issued + $this->lifetime - $now);
161: }
162:
163: 164: 165: 166: 167: 168: 169:
170: function equal($other)
171: {
172: return ((gettype($this) == gettype($other))
173: && ($this->handle == $other->handle)
174: && ($this->secret == $other->secret)
175: && ($this->issued == $other->issued)
176: && ($this->lifetime == $other->lifetime)
177: && ($this->assoc_type == $other->assoc_type));
178: }
179:
180: 181: 182: 183: 184: 185:
186: function serialize()
187: {
188: $data = array(
189: 'version' => '2',
190: 'handle' => $this->handle,
191: 'secret' => base64_encode($this->secret),
192: 'issued' => strval(intval($this->issued)),
193: 'lifetime' => strval(intval($this->lifetime)),
194: 'assoc_type' => $this->assoc_type
195: );
196:
197: assert(array_keys($data) == $this->assoc_keys);
198:
199: return Auth_OpenID_KVForm::fromArray($data, $strict = true);
200: }
201:
202: 203: 204: 205: 206: 207: 208:
209: static function deserialize($class_name, $assoc_s)
210: {
211: $pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true);
212: $keys = array();
213: $values = array();
214: foreach ($pairs as $key => $value) {
215: if (is_array($value)) {
216: list($key, $value) = $value;
217: }
218: $keys[] = $key;
219: $values[] = $value;
220: }
221:
222: $class_vars = get_class_vars($class_name);
223: $class_assoc_keys = $class_vars['assoc_keys'];
224:
225: sort($keys);
226: sort($class_assoc_keys);
227:
228: if ($keys != $class_assoc_keys) {
229: trigger_error('Unexpected key values: ' . var_export($keys, true),
230: E_USER_WARNING);
231: return null;
232: }
233:
234: $version = $pairs['version'];
235: $handle = $pairs['handle'];
236: $secret = $pairs['secret'];
237: $issued = $pairs['issued'];
238: $lifetime = $pairs['lifetime'];
239: $assoc_type = $pairs['assoc_type'];
240:
241: if ($version != '2') {
242: trigger_error('Unknown version: ' . $version, E_USER_WARNING);
243: return null;
244: }
245:
246: $issued = intval($issued);
247: $lifetime = intval($lifetime);
248: $secret = base64_decode($secret);
249:
250: return new $class_name(
251: $handle, $secret, $issued, $lifetime, $assoc_type);
252: }
253:
254: 255: 256: 257: 258: 259: 260: 261: 262:
263: function sign($pairs)
264: {
265: $kv = Auth_OpenID_KVForm::fromArray($pairs);
266:
267:
268: $callback = $this->_macs[$this->assoc_type];
269:
270: return call_user_func_array($callback, array($this->secret, $kv));
271: }
272:
273: 274: 275: 276: 277: 278: 279: 280: 281: 282:
283: function signMessage($message)
284: {
285: if ($message->hasKey(Auth_OpenID_OPENID_NS, 'sig') ||
286: $message->hasKey(Auth_OpenID_OPENID_NS, 'signed')) {
287:
288: return null;
289: }
290:
291: $extant_handle = $message->getArg(Auth_OpenID_OPENID_NS,
292: 'assoc_handle');
293:
294: if ($extant_handle && ($extant_handle != $this->handle)) {
295:
296: return null;
297: }
298:
299: $signed_message = $message;
300: $signed_message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle',
301: $this->handle);
302:
303: $message_keys = array_keys($signed_message->toPostArgs());
304: $signed_list = array();
305: $signed_prefix = 'openid.';
306:
307: foreach ($message_keys as $k) {
308: if (strpos($k, $signed_prefix) === 0) {
309: $signed_list[] = substr($k, strlen($signed_prefix));
310: }
311: }
312:
313: $signed_list[] = 'signed';
314: sort($signed_list);
315:
316: $signed_message->setArg(Auth_OpenID_OPENID_NS, 'signed',
317: implode(',', $signed_list));
318: $sig = $this->getMessageSignature($signed_message);
319: $signed_message->setArg(Auth_OpenID_OPENID_NS, 'sig', $sig);
320: return $signed_message;
321: }
322:
323: 324: 325: 326: 327: 328: 329:
330: function _makePairs($message)
331: {
332: $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
333: if (!$signed || Auth_OpenID::isFailure($signed)) {
334:
335: return null;
336: }
337:
338: $signed_list = explode(',', $signed);
339: $pairs = array();
340: $data = $message->toPostArgs();
341: foreach ($signed_list as $field) {
342: $pairs[] = array($field, Auth_OpenID::arrayGet($data,
343: 'openid.' .
344: $field, ''));
345: }
346: return $pairs;
347: }
348:
349: 350: 351: 352: 353: 354:
355: function getMessageSignature($message)
356: {
357: $pairs = $this->_makePairs($message);
358: return base64_encode($this->sign($pairs));
359: }
360:
361: 362: 363: 364: 365: 366:
367: function checkMessageSignature($message)
368: {
369: $sig = $message->getArg(Auth_OpenID_OPENID_NS,
370: 'sig');
371:
372: if (!$sig || Auth_OpenID::isFailure($sig)) {
373: return false;
374: }
375:
376: $calculated_sig = $this->getMessageSignature($message);
377: return Auth_OpenID_CryptUtil::constEq($calculated_sig, $sig);
378: }
379: }
380:
381: function Auth_OpenID_getSecretSize($assoc_type)
382: {
383: if ($assoc_type == 'HMAC-SHA1') {
384: return 20;
385: } else if ($assoc_type == 'HMAC-SHA256') {
386: return 32;
387: } else {
388: return null;
389: }
390: }
391:
392: function Auth_OpenID_getAllAssociationTypes()
393: {
394: return array('HMAC-SHA1', 'HMAC-SHA256');
395: }
396:
397: function Auth_OpenID_getSupportedAssociationTypes()
398: {
399: $a = array('HMAC-SHA1');
400:
401: if (Auth_OpenID_HMACSHA256_SUPPORTED) {
402: $a[] = 'HMAC-SHA256';
403: }
404:
405: return $a;
406: }
407:
408: function Auth_OpenID_getSessionTypes($assoc_type)
409: {
410: $assoc_to_session = array(
411: 'HMAC-SHA1' => array('DH-SHA1', 'no-encryption'));
412:
413: if (Auth_OpenID_HMACSHA256_SUPPORTED) {
414: $assoc_to_session['HMAC-SHA256'] =
415: array('DH-SHA256', 'no-encryption');
416: }
417:
418: return Auth_OpenID::arrayGet($assoc_to_session, $assoc_type, array());
419: }
420:
421: function Auth_OpenID_checkSessionType($assoc_type, $session_type)
422: {
423: if (!in_array($session_type,
424: Auth_OpenID_getSessionTypes($assoc_type))) {
425: return false;
426: }
427:
428: return true;
429: }
430:
431: function Auth_OpenID_getDefaultAssociationOrder()
432: {
433: $order = array();
434:
435: if (!Auth_OpenID_noMathSupport()) {
436: $order[] = array('HMAC-SHA1', 'DH-SHA1');
437:
438: if (Auth_OpenID_HMACSHA256_SUPPORTED) {
439: $order[] = array('HMAC-SHA256', 'DH-SHA256');
440: }
441: }
442:
443: $order[] = array('HMAC-SHA1', 'no-encryption');
444:
445: if (Auth_OpenID_HMACSHA256_SUPPORTED) {
446: $order[] = array('HMAC-SHA256', 'no-encryption');
447: }
448:
449: return $order;
450: }
451:
452: function Auth_OpenID_getOnlyEncryptedOrder()
453: {
454: $result = array();
455:
456: foreach (Auth_OpenID_getDefaultAssociationOrder() as $pair) {
457: list($assoc, $session) = $pair;
458:
459: if ($session != 'no-encryption') {
460: if (Auth_OpenID_HMACSHA256_SUPPORTED &&
461: ($assoc == 'HMAC-SHA256')) {
462: $result[] = $pair;
463: } else if ($assoc != 'HMAC-SHA256') {
464: $result[] = $pair;
465: }
466: }
467: }
468:
469: return $result;
470: }
471:
472: function Auth_OpenID_getDefaultNegotiator()
473: {
474: return new Auth_OpenID_SessionNegotiator(
475: Auth_OpenID_getDefaultAssociationOrder());
476: }
477:
478: function Auth_OpenID_getEncryptedNegotiator()
479: {
480: return new Auth_OpenID_SessionNegotiator(
481: Auth_OpenID_getOnlyEncryptedOrder());
482: }
483:
484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524:
525: class Auth_OpenID_SessionNegotiator {
526: function Auth_OpenID_SessionNegotiator($allowed_types)
527: {
528: $this->allowed_types = array();
529: $this->setAllowedTypes($allowed_types);
530: }
531:
532: 533: 534: 535: 536: 537:
538: function setAllowedTypes($allowed_types)
539: {
540: foreach ($allowed_types as $pair) {
541: list($assoc_type, $session_type) = $pair;
542: if (!Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
543: return false;
544: }
545: }
546:
547: $this->allowed_types = $allowed_types;
548: return true;
549: }
550:
551: 552: 553: 554: 555: 556: 557:
558: function addAllowedType($assoc_type, $session_type = null)
559: {
560: if ($this->allowed_types === null) {
561: $this->allowed_types = array();
562: }
563:
564: if ($session_type === null) {
565: $available = Auth_OpenID_getSessionTypes($assoc_type);
566:
567: if (!$available) {
568: return false;
569: }
570:
571: foreach ($available as $session_type) {
572: $this->addAllowedType($assoc_type, $session_type);
573: }
574: } else {
575: if (Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
576: $this->allowed_types[] = array($assoc_type, $session_type);
577: } else {
578: return false;
579: }
580: }
581:
582: return true;
583: }
584:
585:
586: function isAllowed($assoc_type, $session_type)
587: {
588: $assoc_good = in_array(array($assoc_type, $session_type),
589: $this->allowed_types);
590:
591: $matches = in_array($session_type,
592: Auth_OpenID_getSessionTypes($assoc_type));
593:
594: return ($assoc_good && $matches);
595: }
596:
597: 598: 599: 600:
601: function getAllowedType()
602: {
603: if (!$this->allowed_types) {
604: return array(null, null);
605: }
606:
607: return $this->allowed_types[0];
608: }
609: }
610:
611: