/** * Generates an association from an request * * @param PSX\OpenId\Provider\Data\AssociationRequest $request * @return PSX\OpenId\Provider\Association */ public function generate(AssociationRequest $request) { // generate secret switch ($request->getAssocType()) { case 'HMAC-SHA1': $secret = ProviderAbstract::randomBytes(20); $macFunc = 'SHA1'; break; case 'HMAC-SHA256': $secret = ProviderAbstract::randomBytes(32); $macFunc = 'SHA256'; break; default: throw new InvalidDataException('Invalid association type'); break; } // generate dh switch ($request->getSessionType()) { case 'no-encryption': // $secret = base64_encode($secret); // $this->macKey = $secret; throw new InvalidDataException('no-encryption not supported'); break; case 'DH-SHA1': $dh = ProviderAbstract::generateDh($request->getDhGen(), $request->getDhModulus(), $request->getDhConsumerPublic(), $macFunc, $secret); $this->dhServerPublic = $dh['pubKey']; $this->encMacKey = $dh['macKey']; break; case 'DH-SHA256': $dh = ProviderAbstract::generateDh($request->getDhGen(), $request->getDhModulus(), $request->getDhConsumerPublic(), $macFunc, $secret); $this->dhServerPublic = $dh['pubKey']; $this->encMacKey = $dh['macKey']; break; default: throw new InvalidDataException('Invalid association type'); break; } $this->assocHandle = ProviderAbstract::generateHandle(); $this->secret = base64_encode($secret); $this->macFunc = $macFunc; $assoc = new Association(); $assoc->setAssocHandle($this->assocHandle); $assoc->setAssocType($request->getAssocType()); $assoc->setSessionType($request->getSessionType()); $assoc->setSecret($this->secret); return $assoc; }
public function testDhSha1() { // generate consumer $request = $this->generateConsumerRequest(); // generate server $dhGen = $request['gen']; $dhModulus = $request['modulus']; $dhConsumerPub = $request['consumer_public']; $dhFunc = 'SHA1'; $secret = ProviderAbstract::randomBytes(20); $res = ProviderAbstract::generateDh($dhGen, $dhModulus, $dhConsumerPub, $dhFunc, $secret); $this->assertEquals(true, isset($res['pubKey'])); $this->assertEquals(true, isset($res['macKey'])); // calculate consumer $serverPub = base64_decode($res['pubKey']); $dhSec = OpenSsl::dhComputeKey($serverPub, $request['pkey']); $sec = OpenSsl::digest(ProviderAbstract::btwoc($dhSec), $dhFunc, true); $serverSecret = $sec ^ base64_decode($res['macKey']); // compare with server $this->assertEquals(true, $secret === $serverSecret); }
public function testInitialize() { $testCase = $this; $http = new Http(new Callback(function ($request) use($testCase) { // association endpoint if ($request->getUrl()->getPath() == '/server') { $data = array(); parse_str($request->getBody(), $data); $testCase->assertEquals('http://specs.openid.net/auth/2.0', $data['openid_ns']); $testCase->assertEquals('associate', $data['openid_mode']); $testCase->assertEquals('HMAC-SHA256', $data['openid_assoc_type']); $testCase->assertEquals('DH-SHA256', $data['openid_session_type']); $dhGen = $data['openid_dh_gen']; $dhModulus = $data['openid_dh_modulus']; $dhConsumerPub = $data['openid_dh_consumer_public']; $dhFunc = 'SHA1'; $secret = ProviderAbstract::randomBytes(20); $res = ProviderAbstract::generateDh($dhGen, $dhModulus, $dhConsumerPub, $dhFunc, $secret); $testCase->assertEquals(true, isset($res['pubKey'])); $testCase->assertEquals(true, isset($res['macKey'])); $body = OpenId::keyValueEncode(array('ns' => 'http://specs.openid.net/auth/2.0', 'assoc_handle' => 'foobar', 'session_type' => 'DH-SHA256', 'assoc_type' => 'HMAC-SHA256', 'expires_in' => 60 * 60, 'dh_server_public' => $res['pubKey'], 'enc_mac_key' => $res['macKey'])); $response = <<<TEXT HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Date: Sat, 04 Jan 2014 18:19:45 GMT {$body} TEXT; } else { if ($request->getUrl()->getPath() == '/identity') { $response = <<<TEXT HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Date: Sat, 04 Jan 2014 18:19:45 GMT <html> \t<head> \t\t<link rel="openid.server" href="http://openid.com/server" /> \t\t<link rel="openid.delegate" href="http://foo.com" /> \t</head> </html> TEXT; } } return Response::convert($response, ResponseParser::MODE_LOOSE)->toString(); })); $store = new Store\Memory(); $openid = new OpenId($http, 'http://localhost.com', $store); $openid->initialize('http://foo.com/identity', 'http://localhost.com/callback'); // check whether the store has the association $assoc = $store->loadByHandle('http://openid.com/server', 'foobar'); $this->assertEquals('foobar', $assoc->getAssocHandle()); $this->assertEquals('HMAC-SHA256', $assoc->getAssocType()); $this->assertEquals('DH-SHA256', $assoc->getSessionType()); $this->assertEquals(3600, $assoc->getExpire()); // check redirect url $url = $openid->getRedirectUrl(); $this->assertEquals('http://specs.openid.net/auth/2.0', $url->getParam('openid.ns')); $this->assertEquals('checkid_setup', $url->getParam('openid.mode')); $this->assertEquals('http://localhost.com/callback', $url->getParam('openid.return_to')); $this->assertEquals('http://localhost.com', $url->getParam('openid.realm')); $this->assertEquals('http://foo.com/identity', $url->getParam('openid.claimed_id')); $this->assertEquals('http://foo.com', $url->getParam('openid.identity')); $this->assertEquals('foobar', $url->getParam('openid.assoc_handle')); // the user gets redirected from the openid provider to our callback now // we verfiy the data $signed = array('ns', 'mode', 'op_endpoint', 'return_to', 'response_nonce', 'assoc_handle'); $data = array('openid_ns' => 'http://specs.openid.net/auth/2.0', 'openid_mode' => 'id_res', 'openid_op_endpoint' => 'http://openid.com/server', 'openid_return_to' => 'http://localhost.com/callback', 'openid_response_nonce' => uniqid(), 'openid_assoc_handle' => $assoc->getAssocHandle(), 'openid_signed' => implode(',', $signed)); // generate signature $sig = OpenId::buildSignature(OpenId::extractParams($data), $signed, $assoc->getSecret(), $assoc->getAssocType()); $data['openid_sig'] = $sig; // verify $result = $openid->verify($data); $this->assertTrue($result); }
public function onCheckidSetup(SetupRequest $request) { // check whether authenticated if (!$this->isAuthenticated()) { $loginUrl = $this->config['psx_url'] . '/' . $this->config['psx_dispatch'] . 'login'; $selfUrl = new Url($this->base->getSelf()); $values = array_merge($_GET, $_POST); foreach ($values as $key => $value) { $selfUrl->addParam($key, $value); } //$selfUrl->addParam('openid.mode', 'checkid_setup'); //$selfUrl->addParam('openid.ns', self::NS); header('Location: ' . $loginUrl . '?redirect=' . urlencode(strval($selfUrl))); exit; } // check association $sql = <<<SQL SELECT \t`assoc`.`id`, \t`assoc`.`expires`, \t`assoc`.`date` FROM \t{$this->registry['table.openid_assoc']} `assoc` WHERE \t`assoc`.`assocHandle` = ? SQL; $row = $this->sql->getRow($sql, array($request->getAssocHandle())); if (!empty($row)) { // check expire $now = new DateTime('NOW', $this->registry['core.default_timezone']); $expire = (int) $row['expires']; if (time() > $now->getTimestamp() + $expire) { throw new Exception('Association is expired'); } } else { if (!$request->isImmediate()) { // create association $date = new DateTime('NOW', $this->registry['core.default_timezone']); $assocHandle = ProviderAbstract::generateHandle(); $secret = base64_encode(ProviderAbstract::randomBytes(20)); $this->sql->insert($this->registry['table.openid_assoc'], array('assocHandle' => $assocHandle, 'assocType' => 'HMAC-SHA1', 'sessionType' => 'DH-SHA1', 'secret' => $secret, 'expires' => self::EXPIRE, 'date' => $date->format(DateTime::SQL))); // set assoc handle $request->setAssocHandle($assocHandle); } else { throw new Exception('Invalid association'); } } // count connect requests /* $maxCount = 5; $con = new PSX_Sql_Condition(array('userId', '=', $this->user->getId()), array('status', '=', AmunService_Oauth_Record::TEMPORARY)); $count = $this->sql->count($this->registry['table.oauth_request'], $con); if($count > $maxCount) { $conDelete = new PSX_Sql_Condition(); $result = $this->sql->select($this->registry['table.oauth_request'], array('id', 'expire', 'date'), $con, PSX_Sql::SELECT_ALL); foreach($result as $row) { $now = new DateTime('NOW', $this->registry['core.default_timezone']); $date = new DateTime($row['date'], $this->registry['core.default_timezone']); $date->add(new DateInterval($row['expire'])); if($now > $date) { $conDelete->add('id', '=', $row['id'], 'OR'); } } if($conDelete->hasCondition()) { $this->sql->delete($this->registry['table.oauth_request'], $conDelete); } throw new Exception('You can have max ' . $maxCount . ' temporary account connect requests. Each request expires after 30 hour'); } */ // save request params $_SESSION['amun_openid_request'] = $request; // redirect header('Location: ' . $this->config['psx_url'] . '/' . $this->config['psx_dispatch'] . 'login/connect'); exit; }