public function testFlow() { $consumerRequest = $this->generateConsumerRequest(); // association request $data = $this->doAssociation($consumerRequest['modulus'], $consumerRequest['gen'], $consumerRequest['consumer_public']); // we received the encrypted secret over the wire. The client can now // decrypt the secret $assocHandle = $data['assoc_handle']; $assocType = $data['assoc_type']; $serverPublicKey = $data['dh_server_public']; $encMacKey = $data['enc_mac_key']; $this->assertTrue(!empty($assocHandle)); $this->assertTrue(!empty($assocType)); $this->assertTrue(!empty($serverPublicKey)); $this->assertTrue(!empty($encMacKey)); $serverPub = base64_decode($serverPublicKey); $dhSec = OpenSsl::dhComputeKey($serverPub, $consumerRequest['pkey']); $sec = OpenSsl::digest(ProviderAbstract::btwoc($dhSec), 'SHA1', true); $serverSecret = base64_encode($sec ^ base64_decode($data['enc_mac_key'])); // the client has established the association we can make now an // checkid setup request. If the user is authenticated he gets // redirected back to the relying party $url = $this->doCheckidSetupRequest($assocHandle); // later the relying party calls check authentication to verify the // callback $this->doCheckAuthentication($url); }
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); }
/** * Tries to establish a association with the op if a store is available. The * method returns null or PSX\OpenId\Provider\Data\Association. Discovery * must be made before calling this method * * @return PSX\OpenId\Provider\Data\Association|null */ private function establishAssociaton($assocType = 'HMAC-SHA256', $sessionType = 'DH-SHA256') { // request association $g = pack('H*', ProviderAbstract::DH_G); $p = pack('H*', ProviderAbstract::DH_P); $pkey = new PKey(array('private_key_type' => OPENSSL_KEYTYPE_DH, 'dh' => array('p' => $p, 'g' => $g))); $details = $pkey->getDetails(); $params = array('openid.ns' => ProviderAbstract::NS, 'openid.mode' => 'associate', 'openid.assoc_type' => $assocType, 'openid.session_type' => $sessionType, 'openid.dh_modulus' => base64_encode(ProviderAbstract::btwoc($details['dh']['p'])), 'openid.dh_gen' => base64_encode(ProviderAbstract::btwoc($details['dh']['g'])), 'openid.dh_consumer_public' => base64_encode(ProviderAbstract::btwoc($details['dh']['pub_key']))); $request = new PostRequest($this->identity->getServer(), array('User-Agent' => __CLASS__ . ' ' . Base::VERSION), $params); $response = $this->http->request($request); if ($response->getStatusCode() == 200) { $data = self::keyValueDecode($response->getBody()); // check values $diff = array_diff(array('ns', 'assoc_handle', 'session_type', 'assoc_type', 'expires_in'), array_keys($data)); if (count($diff) > 0) { throw new Exception('Missing fields ' . implode(', ', $diff)); } if ($data['ns'] != ProviderAbstract::NS) { throw new Exception('Invalid namesspace'); } if (!in_array($data['session_type'], self::$supportedSessionTypes)) { throw new Exception('Invalid session type'); } if (!in_array($data['assoc_type'], self::$supportedAssocTypes)) { throw new Exception('Invalid assoc type'); } // decrypt shared secret if ($data['session_type'] != 'no-encryption') { if (!isset($data['dh_server_public'])) { throw new Exception('DH server public not set'); } if (!isset($data['enc_mac_key'])) { throw new Exception('Encoded mac key not set'); } $dhFunc = str_replace('DH-', '', $data['session_type']); $serverPub = base64_decode($data['dh_server_public']); $dhSec = OpenSsl::dhComputeKey($serverPub, $pkey); $sec = OpenSsl::digest(ProviderAbstract::btwoc($dhSec), $dhFunc, true); $serverSecret = base64_encode($sec ^ base64_decode($data['enc_mac_key'])); } else { if (!isset($data['mac_key'])) { throw new Exception('Mac key not set'); } $dhFunc = null; $serverSecret = $data['mac_key']; } // build association $assoc = new Association(); $assoc->setAssocHandle($data['assoc_handle']); $assoc->setAssocType($data['assoc_type']); $assoc->setSessionType($data['session_type']); $assoc->setSecret($serverSecret); $assoc->setExpire($data['expires_in']); return $assoc; } else { throw new Exception('Could not establish associaton received ' . $response->getStatusCode()); } }
public static function generateDh($dhGen, $dhModulus, $dhConsumerPublic, $dhFunc, $secret) { if (empty($dhConsumerPublic)) { throw new Exception('Empty "openid.dh_consumer_public"'); } $g = empty($dhGen) ? pack('H*', self::DH_G) : base64_decode($dhGen); $p = empty($dhModulus) ? pack('H*', self::DH_P) : base64_decode($dhModulus); $dhKey = self::createDhKey($p, $g); $details = $dhKey->getDetails(); $dh = isset($details['dh']) ? $details['dh'] : null; if (empty($dh)) { throw new Exception('Could not get dh details'); } $sec = OpenSsl::dhComputeKey(base64_decode($dhConsumerPublic), $dhKey); $digest = OpenSsl::digest(self::btwoc($sec), $dhFunc, true); $res = array('pubKey' => base64_encode(self::btwoc($dh['pub_key'])), 'macKey' => base64_encode($digest ^ $secret)); return $res; }
public function testDigest() { $methods = OpenSsl::getMdMethods(); $this->assertTrue(is_array($methods)); $this->assertTrue(count($methods) > 0); $data = OpenSsl::digest('foobar', 'SHA256'); $this->assertEquals('c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2', $data); }