Subversion Repositories Applications.framework

Compare Revisions

No changes between revisions

Ignore whitespace Rev 462 → Rev 461

/trunk/framework/brouillons/OpenIdClient.php
New file
0,0 → 1,1193
<?php
/**
* La classe OpenIdClient est une implémentation d'un client OpenId, depuis une classe Zend.
* Elle permet d'établir une connexion avec un serveur, en fonction d'un identifiant OpenId.
* Elle permet de communiquer de manière sécurisée avec ce serveur, et doit aboutir a une
* identification centralisée.
* */
 
class OpenIdClient {
//OpenID 2.0 namespace. Tous les messages OpenID 2.0 DOIVENT contenir la variable openid.ns et sa valeur
const NS_2_0 = 'http://specs.openid.net/auth/2.0';
// TODO : remplacer _storage par une gestion par cache ?
/**
* Variable permettant le stockage d'informations, notammenent à propos des clés DiffieHellmann
* @var Storage $_storage
*/
protected $_storage = null;
/**
* Tableau "cache" interne permettant d'éviter des accès inutiles au fichier storage
* @var array $_cache
*/
protected $_cache = array();
 
// Client pour les requetes.
private $client;
/**
* Constructeur de l'application
* */
function __construct() {
$this->client = new Client();
$this->_storage = new StorageFile();
}
/**
* Fonction login
*
* Return true ou false
* > Ne retourne rien si true car redirige vers l'adresse du serveur OID
* */
//FIXME : le paramètre immediate ?
// A vérifier mais ça doit permettre de passer directement le mot de passe. Il reste plus qu'à trouver le nom de la variable mot de passe.
function login($id, $immediate = false) {
// L'original retourne la fonction checkId, avec le parametre immediate = true
// Je ne comprends pas l'utilité, je fusionne les deux pour l'instant
// FIXME : si pas de comportement étrange, valider.
//Tests et arrêt si non validé :
//Normaliser (traite si XRI ou URL, normalize URL)
//FIXME : voir avec JP pour équivalent dans framework
if (!$this->normalize($id)) {
return false;
}
//Discovery
// Récupérer les informations sur le serveur OPEN ID
/*
FIXME : ca change la valeur de l'id !!!
if (!$this->_discovery($id, $server, $version)) {
trigger_error('Discovery failed');
return false;
}*/
$retour_url = $this->client->consulter($id);
//Le retour contient les balises suivantes :
/*
*
* <link rel="openid.server" href="http://www.myopenid.com/server" />
* <link rel="openid2.provider" href="http://www.myopenid.com/server" />
*/
$metaServeur = $this->verifierVersion($retour_url);
//TODO : Voir avec JP : la classe client ne permet pas de vérifer le statut ??
if ($retour_url === false) {
trigger_error('L\'adresse $id est inacessible', E_USER_ERROR);
return false;
}
if ($metaServeur === false) {
return false;
}
if (!$this->_associate($metaServeur['serveur'], $metaServeur['version'])) {
trigger_error('Impossible de s\'associer avec le serveur');
}
/*TODO : fonctionnement différent
if (!$this->_getAssociation(
$server,
$handle,
$macFunc,
$secret,
$expires)) {
/* Use dumb mode *
unset($handle);
unset($macFunc);
unset($secret);
unset($expires);*
}*/
 
//on a la version, l'adresse du serveur et le realId si c'est une 2.0 dans metaServeur
if (isset($metaServeur['realId'])) {
$id = $metaServeur['realId'];
}
//Associate
//getAssociation
//Organisation des paramètres :
$params = array();
if ($metaServeur['version'] >= 2.0) {
$params['openid.ns'] = self::NS_2_0;
}
 
$params['openid.mode'] = $immediate ?
'checkid_immediate' : 'checkid_setup';
 
$params['openid.identity'] = $id;
 
//FIXME : Ex : $params['openid.claimed_id'] = $claimedId; > jvois pas l'intéret
$params['openid.claimed_id'] = $id;
/*
* TODO : gérer les sessions et namespace
* if ($metaServeur['version'] <= 2.0) {
if ($this->_session !== null) {
$this->_session->identity = $id;
$this->_session->claimed_id = $claimedId;
} else if (defined('SID')) {
$_SESSION["zend_openid"] = array(
"identity" => $id,
"claimed_id" => $claimedId);
} else {
require_once "Zend/Session/Namespace.php";
$this->_session = new Zend_Session_Namespace("zend_openid");
$this->_session->identity = $id;
$this->_session->claimed_id = $claimedId;
}
}*/
 
if (isset($handle)) {
$params['openid.assoc_handle'] = $handle;
}
 
//FIXME : $params['openid.return_to'] = $this->absoluteUrl($returnTo);
$params['openid.return_to'] = $this->absoluteUrl(null);
 
if (empty($root)) {
$root = $this->selfUrl();
if ($root[strlen($root)-1] != '/') {
$root = dirname($root);
}
}
if ($metaServeur['version'] >= 2.0) {
$params['openid.realm'] = $root;
} else {
$params['openid.trust_root'] = $root;
}
 
/*FIXME ::
if (!Zend_OpenId_Extension::forAll($extensions, 'prepareRequest', $params)) {
$this->_setError("Extension::prepareRequest failure");
return false;
}*/
 
$this->redirect($metaServeur['serveur'], $params);
return true;
//Renvoyer vers l'url
}
/**
* Verifies authentication response from OpenID server.
*
* This is the second step of OpenID authentication process.
* The function returns true on successful authentication and false on
* failure.
*
* @param array $params HTTP query data from OpenID server
* @param string &$identity this argument is set to end-user's claimed
* identifier or OpenID provider local identifier.
* @param mixed $extensions extension object or array of extensions objects
* @return bool
*/
public function verify($params, &$identity = "", $extensions = null)
{
if (isset($params['openid_ns']) &&
$params['openid_ns'] == $this->NS_2_0) {
$version = 2.0;
}
 
if (isset($params["openid_claimed_id"])) {
$identity = $params["openid_claimed_id"];
} else if (isset($params["openid_identity"])){
$identity = $params["openid_identity"];
} else {
$identity = "";
}
 
if ($version < 2.0 && !isset($params["openid_claimed_id"])) {
if ($this->_session !== null) {
if ($this->_session->identity === $identity) {
$identity = $this->_session->claimed_id;
}
} else if (defined('SID')) {
if (isset($_SESSION["zend_openid"]["identity"]) &&
isset($_SESSION["zend_openid"]["claimed_id"]) &&
$_SESSION["zend_openid"]["identity"] === $identity) {
$identity = $_SESSION["zend_openid"]["claimed_id"];
}
} else {
require_once "Zend/Session/Namespace.php";
$this->_session = new Zend_Session_Namespace("zend_openid");
if ($this->_session->identity === $identity) {
$identity = $this->_session->claimed_id;
}
}
}
 
if (empty($params['openid_mode'])) {
$this->_setError("Missing openid.mode");
return false;
}
if (empty($params['openid_return_to'])) {
$this->_setError("Missing openid.return_to");
return false;
}
if (empty($params['openid_signed'])) {
$this->_setError("Missing openid.signed");
return false;
}
if (empty($params['openid_sig'])) {
$this->_setError("Missing openid.sig");
return false;
}
if ($params['openid_mode'] != 'id_res') {
$this->_setError("Wrong openid.mode '".$params['openid_mode']."' != 'id_res'");
return false;
}
if (empty($params['openid_assoc_handle'])) {
$this->_setError("Missing openid.assoc_handle");
return false;
}
if ($params['openid_return_to'] != $this->selfUrl()) {
/* Ignore query part in openid.return_to */
$pos = strpos($params['openid_return_to'], '?');
if ($pos === false ||
SUBSTR($params['openid_return_to'], 0 , $pos) != $this->selfUrl()) {
 
/*$this->_setError("Wrong openid.return_to '".
$params['openid_return_to']."' != '" . $this->selfUrl() ."'");*/
trigger_error('Wrong openid.return_to', E_USER_ERROR);
return false;
}
}
 
if ($version >= 2.0) {
if (empty($params['openid_response_nonce'])) {
trigger_error('Missing openid.response_nonce', E_USER_ERROR);
return false;
}
if (empty($params['openid_op_endpoint'])) {
trigger_error('Missing openid.op_endpoint', E_USER_ERROR);
return false;
/* OpenID 2.0 (11.3) Checking the Nonce */
} else if (!$this->_storage->isUniqueNonce($params['openid_op_endpoint'], $params['openid_response_nonce'])) {
trigger_error('Duplicate openid.response_nonce', E_USER_ERROR);
return false;
}
}
 
if (!empty($params['openid_invalidate_handle'])) {
if ($this->_storage->getAssociationByHandle(
$params['openid_invalidate_handle'],
$url,
$macFunc,
$secret,
$expires)) {
$this->_storage->delAssociation($url);
}
}
 
if ($this->_storage->getAssociationByHandle(
$params['openid_assoc_handle'],
$url,
$macFunc,
$secret,
$expires)) {
$signed = explode(',', $params['openid_signed']);
$data = '';
foreach ($signed as $key) {
$data .= $key . ':' . $params['openid_' . strtr($key,'.','_')] . "\n";
}
if (base64_decode($params['openid_sig']) ==
Zend_OpenId::hashHmac($macFunc, $data, $secret)) {
/*
* FIXME dépendance je sais pas pour quoi : a voir :
* if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) {
$this->_setError("Extension::parseResponse failure");
return false;
}*/
/* OpenID 2.0 (11.2) Verifying Discovered Information */
if (isset($params['openid_claimed_id'])) {
$id = $params['openid_claimed_id'];
if (!$this->normalize($id)) {
$this->_setError("Normalization failed");
return false;
} else if (!$this->_discovery($id, $discovered_server, $discovered_version)) {
$this->_setError("Discovery failed: " . $this->getError());
return false;
} else if ((!empty($params['openid_identity']) &&
$params["openid_identity"] != $id) ||
(!empty($params['openid_op_endpoint']) &&
$params['openid_op_endpoint'] != $discovered_server) ||
$discovered_version != $version) {
$this->_setError("Discovery information verification failed");
return false;
}
}
return true;
}
$this->_storage->delAssociation($url);
$this->_setError("Signature check failed");
return false;
}
else
{
/* Use dumb mode */
if (isset($params['openid_claimed_id'])) {
$id = $params['openid_claimed_id'];
} else if (isset($params['openid_identity'])) {
$id = $params['openid_identity'];
} else {
$this->_setError("Missing openid.claimed_id and openid.identity");
return false;
}
 
if (!$this->normalize($id)) {
trigger_error('Normalization failed', E_USER_ERROR);
return false;
} else if (!$this->_discovery($id, $server, $discovered_version)) {
trigger_error('Discovery failed', E_USER_ERROR);
return false;
}
 
/* OpenID 2.0 (11.2) Verifying Discovered Information */
if ((isset($params['openid_identity']) &&
$params["openid_identity"] != $id) ||
(isset($params['openid_op_endpoint']) &&
$params['openid_op_endpoint'] != $server) ||
$discovered_version != $version) {
trigger_error('Discovery information verification failed', E_USER_ERROR);
return false;
}
 
$params2 = array();
foreach ($params as $key => $val) {
if (strpos($key, 'openid_ns_') === 0) {
$key = 'openid.ns.' . substr($key, strlen('openid_ns_'));
} else if (strpos($key, 'openid_sreg_') === 0) {
$key = 'openid.sreg.' . substr($key, strlen('openid_sreg_'));
} else if (strpos($key, 'openid_') === 0) {
$key = 'openid.' . substr($key, strlen('openid_'));
}
$params2[$key] = $val;
}
$params2['openid.mode'] = 'check_authentication';
$ret = $this->client->modifier($serveur, $params2);
//_httpRequest($server, 'POST', $params2, $status);
if ($ret === false) {
trigger_error("'Dumb' signature verification HTTP request failed", E_USER_ERROR);
return false;
}
$r = array();
if (is_string($ret)) {
foreach(explode("\n", $ret) as $line) {
$line = trim($line);
if (!empty($line)) {
$x = explode(':', $line, 2);
if (is_array($x) && count($x) == 2) {
list($key, $value) = $x;
$r[trim($key)] = trim($value);
}
}
}
}
$ret = $r;
if (!empty($ret['invalidate_handle'])) {
if ($this->_storage->getAssociationByHandle(
$ret['invalidate_handle'],
$url,
$macFunc,
$secret,
$expires)) {
$this->_storage->delAssociation($url);
}
}
if (isset($ret['is_valid']) && $ret['is_valid'] == 'true') {
if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) {
$this->_setError("Extension::parseResponse failure");
return false;
}
return true;
}
$this->_setError("'Dumb' signature verification failed");
return false;
}
}
/**
* Performs discovery of identity and finds OpenID URL, OpenID server URL
* and OpenID protocol version. Returns true on succees and false on
* failure.
*
* @param string &$id OpenID identity URL
* @param string &$server OpenID server URL
* @param float &$version OpenID protocol version
* @return bool
* @todo OpenID 2.0 (7.3) XRI and Yadis discovery
*/
protected function _discovery(&$id, &$server, &$version)
{
$realId = $id;
if ($this->_storage->getDiscoveryInfo(
$id,
$realId,
$server,
$version,
$expire)) {
$id = $realId;
return true;
}
/* TODO: OpenID 2.0 (7.3) XRI and Yadis discovery */
 
/* HTML-based discovery */
$clientDiscovery = new Client();
//TODO : rajouter un test sur le statut de la réponse
// Nécessite la prise en compte des entetes dans le framework
/*if ($status != 200 || !is_string($response)) {
return false;
}*/
$reponse = $clientDiscovery->consulter($id);
$metaServeur = $this->verifierVersion($reponse);
if (!isset($metaServeur) || empty($reponse)) {
trigger_error('Aucune donnée OpenId', E_USER_ERROR);
return false;
}
$expire = time() + 60 * 60;
$this->_storage->addDiscoveryInfo($id, $metaServeur['realId'], $metaServeur['serveur'], $metaServeur['version'], $expire);
if (!empty($metaServeur['realId'])) {
$id = $metaServeur['realId'];
}
return true;
}
//Parser l'HTML de réponse pour trouver la version du serveur OPEN ID
function verifierVersion($reponseHtml) {
// TODO : remplacer l'arlgorythme suivant par cette solution :
//1. Chercher l'existence d'une balise openidN.provider
//2. Déterminer la version en fonction de la chaine : openid2.provider => 2.0; openid.provider => 1.1
//3. Récupérer l'url du serveur href="serveur"
//4. SI 2.0, récupérer la valeur réelle de l'ID
//TODO : penser à tester les deux versions du serveur
$metaServeur = Array();
if (preg_match(
'/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
$reponseHtml,
$r)) {
$metaServeur['version'] = 2.0;
$metaServeur['serveur'] = $r[3];
} else if (preg_match(
'/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\3[^>]*\/?>/i',
$reponseHtml,
$r)) {
$metaServeur['version'] = 2.0;
$metaServeur['serveur'] = $r[1];
} else if (preg_match(
'/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
$reponseHtml,
$r)) {
$metaServeur['version'] = 1.1;
$metaServeur['serveur'] = $r[3];
} else if (preg_match(
'/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\3[^>]*\/?>/i',
$reponseHtml,
$r)) {
$metaServeur['version'] = 1.1;
$metaServeur['serveur'] = $r[2];
} else {
return false;
}
if ($metaServeur['version'] >= 2.0) {
if (preg_match(
'/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
$reponseHtml,
$r)) {
$metaServeur['realId'] = $r[3];
} else if (preg_match(
'/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\3[^>]*\/?>/i',
$reponseHtml,
$r)) {
$metaServeur['realId'] = $r[2];
}
} else {
if (preg_match(
'/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
$reponseHtml,
$r)) {
$metaServeur['realId'] = $r[3];
} else if (preg_match(
'/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\3[^>]*\/?>/i',
$reponseHtml,
$r)) {
$metaServeur['realId'] = $r[2];
}
}
return $metaServeur;
}
/**
* Create (or reuse existing) association between OpenID consumer and
* OpenID server based on Diffie-Hellman key agreement. Returns true
* on success and false on failure.
*
* @param string $url OpenID server url
* @param float $version OpenID protocol version
* @param string $priv_key for testing only
* @return bool
*/
protected function _associate($url, $version, $priv_key=null)
{
/* Check if we already have association in chace or storage */
/*
* TODO : Utiliser le stockage plutot
* */
if ($this->_getAssociation(
$url,
$handle,
$macFunc,
$secret,
$expires)) {
return true;
}
 
/*
* TODO : utiliser le fichier de config
* if ($this->_dumbMode) {
return true;
}*/
 
$params = array();
 
if ($version >= 2.0) {
$params = array(
'openid.ns' => $this->NS_2_0,
'openid.mode' => 'associate',
'openid.assoc_type' => 'HMAC-SHA256',
'openid.session_type' => 'DH-SHA256',
);
} else {
$params = array(
'openid.mode' => 'associate',
'openid.assoc_type' => 'HMAC-SHA1',
'openid.session_type' => 'DH-SHA1',
);
}
 
$dh = DiffieHellmanUtil::createDhKey(pack('H*', DiffieHellmanUtil::DH_P),
pack('H*', DiffieHellmanUtil::DH_G),
$priv_key);
$dh_details = DiffieHellmanUtil::getDhKeyDetails($dh);
 
$params['openid.dh_modulus'] = base64_encode(
DiffieHellmanUtil::btwoc($dh_details['p']));
$params['openid.dh_gen'] = base64_encode(
DiffieHellmanUtil::btwoc($dh_details['g']));
$params['openid.dh_consumer_public'] = base64_encode(
DiffieHellmanUtil::btwoc($dh_details['pub_key']));
 
while(1) {
//FIXME : c'est pas une modification ...
$ret = $this->client->modifier($url, $params); // FIXME : a quoi sert status ?, $status);
if ($ret === false) {
//$this->_setError("HTTP request failed");
trigger_error('La requête a échoué', E_USER_ERROR);
return false;
}
 
$r = array();
$bad_response = false;
foreach(explode("\n", $ret) as $line) {
$line = trim($line);
if (!empty($line)) {
$x = explode(':', $line, 2);
if (is_array($x) && count($x) == 2) {
list($key, $value) = $x;
$r[trim($key)] = trim($value);
} else {
$bad_response = true;
}
}
}
if ($bad_response && strpos($ret, 'Unknown session type') !== false) {
$r['error_code'] = 'unsupported-type';
}
$ret = $r;
 
if (isset($ret['error_code']) &&
$ret['error_code'] == 'unsupported-type') {
if ($params['openid.session_type'] == 'DH-SHA256') {
$params['openid.session_type'] = 'DH-SHA1';
$params['openid.assoc_type'] = 'HMAC-SHA1';
} else if ($params['openid.session_type'] == 'DH-SHA1') {
$params['openid.session_type'] = 'no-encryption';
} else {
trigger_error("The OpenID service responded with: " . $ret['error_code'], E_USER_ERROR);
return false;
}
} else {
break;
}
}
 
/*
FIXME : gestion du statut avec la classe client ??
if ($status != 200) {
$this->_setError("The server responded with status code: " . $status);
return false;
}*/
 
if ($version >= 2.0 &&
isset($ret['ns']) &&
$ret['ns'] != $this->NS_2_0) {
$this->_setError("Wrong namespace definition in the server response");
return false;
}
 
if (!isset($ret['assoc_handle']) ||
!isset($ret['expires_in']) ||
!isset($ret['assoc_type']) ||
$params['openid.assoc_type'] != $ret['assoc_type']) {
if ($params['openid.assoc_type'] != $ret['assoc_type']) {
$this->_setError("The returned assoc_type differed from the supplied openid.assoc_type");
} else {
$this->_setError("Missing required data from provider (assoc_handle, expires_in, assoc_type are required)");
}
return false;
}
 
$handle = $ret['assoc_handle'];
$expiresIn = $ret['expires_in'];
 
if ($ret['assoc_type'] == 'HMAC-SHA1') {
$macFunc = 'sha1';
} else if ($ret['assoc_type'] == 'HMAC-SHA256' &&
$version >= 2.0) {
$macFunc = 'sha256';
} else {
$this->_setError("Unsupported assoc_type");
return false;
}
 
if ((empty($ret['session_type']) ||
($version >= 2.0 && $ret['session_type'] == 'no-encryption')) &&
isset($ret['mac_key'])) {
$secret = base64_decode($ret['mac_key']);
} else if (isset($ret['session_type']) &&
$ret['session_type'] == 'DH-SHA1' &&
!empty($ret['dh_server_public']) &&
!empty($ret['enc_mac_key'])) {
$dhFunc = 'sha1';
} else if (isset($ret['session_type']) &&
$ret['session_type'] == 'DH-SHA256' &&
$version >= 2.0 &&
!empty($ret['dh_server_public']) &&
!empty($ret['enc_mac_key'])) {
$dhFunc = 'sha256';
} else {
$this->_setError("Unsupported session_type");
return false;
}
if (isset($dhFunc)) {
$serverPub = base64_decode($ret['dh_server_public']);
$dhSec = DiffieHellmanUtil::computeDhSecret($serverPub, $dh);
if ($dhSec === false) {
$this->_setError("DH secret comutation failed");
return false;
}
$sec = $this->digest($dhFunc, $dhSec);
if ($sec === false) {
$this->_setError("Could not create digest");
return false;
}
$secret = $sec ^ base64_decode($ret['enc_mac_key']);
}
if ($macFunc == 'sha1') {
if (DiffieHellmanUtil::strlen($secret) != 20) {
$this->_setError("The length of the sha1 secret must be 20");
return false;
}
} else if ($macFunc == 'sha256') {
if (DiffieHellmanUtil::strlen($secret) != 32) {
$this->_setError("The length of the sha256 secret must be 32");
return false;
}
}
$this->_addAssociation(
$url,
$handle,
$macFunc,
$secret,
time() + $expiresIn);
/* $this->association['url'] = $url;
$this->association['handle'] = $handle;
$this->association['macFunc'] = $macFunc;
$this->association['secret'] = $secret;
$this->association['expiresIn'] = time() + $expiresIn;*/
return true;
}
/**
* Store assiciation in internal chace and external storage
*
* @param string $url OpenID server url
* @param string $handle association handle
* @param string $macFunc HMAC function (sha1 or sha256)
* @param string $secret shared secret
* @param integer $expires expiration UNIX time
* @return void
*/
protected function _addAssociation($url, $handle, $macFunc, $secret, $expires)
{
$this->_cache[$url] = array($handle, $macFunc, $secret, $expires);
return $this->_storage->addAssociation(
$url,
$handle,
$macFunc,
$secret,
$expires);
}
/**
* Retrive assiciation information for given $url from internal cahce or
* external storage
*
* @param string $url OpenID server url
* @param string &$handle association handle
* @param string &$macFunc HMAC function (sha1 or sha256)
* @param string &$secret shared secret
* @param integer &$expires expiration UNIX time
* @return void
*/
protected function _getAssociation($url, &$handle, &$macFunc, &$secret, &$expires)
{
if (isset($this->_cache[$url])) {
$handle = $this->_cache[$url][0];
$macFunc = $this->_cache[$url][1];
$secret = $this->_cache[$url][2];
$expires = $this->_cache[$url][3];
return true;
}
if ($this->_storage->getAssociation(
$url,
$handle,
$macFunc,
$secret,
$expires)) {
$this->_cache[$url] = array($handle, $macFunc, $secret, $expires);
return true;
}
return false;
}
/**
* Normalizes URL according to RFC 3986 to use it in comparison operations.
* The function gets URL argument by reference and modifies it.
* It returns true on success and false of failure.
*
* @param string &$id url to be normalized
* @return bool
*/
static public function normalizeUrl(&$id)
{
// RFC 3986, 6.2.2. Syntax-Based Normalization
 
// RFC 3986, 6.2.2.2 Percent-Encoding Normalization
$i = 0;
$n = strlen($id);
$res = '';
while ($i < $n) {
if ($id[$i] == '%') {
if ($i + 2 >= $n) {
return false;
}
++$i;
if ($id[$i] >= '0' && $id[$i] <= '9') {
$c = ord($id[$i]) - ord('0');
} else if ($id[$i] >= 'A' && $id[$i] <= 'F') {
$c = ord($id[$i]) - ord('A') + 10;
} else if ($id[$i] >= 'a' && $id[$i] <= 'f') {
$c = ord($id[$i]) - ord('a') + 10;
} else {
return false;
}
++$i;
if ($id[$i] >= '0' && $id[$i] <= '9') {
$c = ($c << 4) | (ord($id[$i]) - ord('0'));
} else if ($id[$i] >= 'A' && $id[$i] <= 'F') {
$c = ($c << 4) | (ord($id[$i]) - ord('A') + 10);
} else if ($id[$i] >= 'a' && $id[$i] <= 'f') {
$c = ($c << 4) | (ord($id[$i]) - ord('a') + 10);
} else {
return false;
}
++$i;
$ch = chr($c);
if (($ch >= 'A' && $ch <= 'Z') ||
($ch >= 'a' && $ch <= 'z') ||
$ch == '-' ||
$ch == '.' ||
$ch == '_' ||
$ch == '~') {
$res .= $ch;
} else {
$res .= '%';
if (($c >> 4) < 10) {
$res .= chr(($c >> 4) + ord('0'));
} else {
$res .= chr(($c >> 4) - 10 + ord('A'));
}
$c = $c & 0xf;
if ($c < 10) {
$res .= chr($c + ord('0'));
} else {
$res .= chr($c - 10 + ord('A'));
}
}
} else {
$res .= $id[$i++];
}
}
 
if (!preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?#]*)?((?:[?](?:[^#]*))?)((?:#.*)?)$|', $res, $reg)) {
return false;
}
$scheme = $reg[1];
$auth = $reg[2];
$host = $reg[3];
$port = $reg[4];
$path = $reg[5];
$query = $reg[6];
$fragment = $reg[7]; /* strip it */
 
if (empty($scheme) || empty($host)) {
return false;
}
 
// RFC 3986, 6.2.2.1. Case Normalization
$scheme = strtolower($scheme);
$host = strtolower($host);
 
// RFC 3986, 6.2.2.3. Path Segment Normalization
if (!empty($path)) {
$i = 0;
$n = strlen($path);
$res = "";
while ($i < $n) {
if ($path[$i] == '/') {
++$i;
while ($i < $n && $path[$i] == '/') {
++$i;
}
if ($i < $n && $path[$i] == '.') {
++$i;
if ($i < $n && $path[$i] == '.') {
++$i;
if ($i == $n || $path[$i] == '/') {
if (($pos = strrpos($res, '/')) !== false) {
$res = substr($res, 0, $pos);
}
} else {
$res .= '/..';
}
} else if ($i != $n && $path[$i] != '/') {
$res .= '/.';
}
} else {
$res .= '/';
}
} else {
$res .= $path[$i++];
}
}
$path = $res;
}
 
// RFC 3986,6.2.3. Scheme-Based Normalization
if ($scheme == 'http') {
if ($port == 80) {
$port = '';
}
} else if ($scheme == 'https') {
if ($port == 443) {
$port = '';
}
}
if (empty($path)) {
$path = '/';
}
 
$id = $scheme
. '://'
. $auth
. $host
. (empty($port) ? '' : (':' . $port))
. $path
. $query;
return true;
}
 
/**
* Normaliser l'identifiant OpenId qui peut être une URL ou nom XRI
* Retourne true ou false en cas d'erreur.
*
* Règles de normalisation :
* 1. If the user's input starts with one of the "xri://", "xri://$ip*",
* or "xri://$dns*" prefixes, they MUST be stripped off, so that XRIs
* are used in the canonical form, and URI-authority XRIs are further
* considered URL identifiers.
* 2. If the first character of the resulting string is an XRI Global
* Context Symbol ("=", "@", "+", "$", "!"), then the input SHOULD be
* treated as an XRI.
* 3. Otherwise, the input SHOULD be treated as an http URL; if it does
* not include a "http" or "https" scheme, the Identifier MUST be
* prefixed with the string "http://".
* 4. URL identifiers MUST then be further normalized by both following
* redirects when retrieving their content and finally applying the
* rules in Section 6 of [RFC3986] to the final destination URL.
* @param string &$id identifier to be normalized
* @return bool
*/
static public function normalize(&$id)
{
$id = trim($id);
if (strlen($id) === 0) {
return true;
}
 
// 7.2.1
if (strpos($id, 'xri://$ip*') === 0) {
$id = substr($id, strlen('xri://$ip*'));
} else if (strpos($id, 'xri://$dns*') === 0) {
$id = substr($id, strlen('xri://$dns*'));
} else if (strpos($id, 'xri://') === 0) {
$id = substr($id, strlen('xri://'));
}
 
// 7.2.2
if ($id[0] == '=' ||
$id[0] == '@' ||
$id[0] == '+' ||
$id[0] == '$' ||
$id[0] == '!') {
return true;
}
 
// 7.2.3
if (strpos($id, "://") === false) {
$id = 'http://' . $id;
}
 
// 7.2.4
return self::normalizeURL($id);
}
/**
* Generates a hash value (message digest) according to given algorithm.
* It returns RAW binary string.
*
* This is a wrapper function that uses one of available internal function
* dependent on given PHP configuration. It may use various functions from
* ext/openssl, ext/hash, ext/mhash or ext/standard.
*
* @param string $func digest algorithm
* @param string $data data to sign
* @return string RAW digital signature
* @throws Zend_OpenId_Exception
*/
public function digest($func, $data)
{
if (function_exists('openssl_digest')) {
return openssl_digest($data, $func, true);
} else if (function_exists('hash')) {
return hash($func, $data, true);
} else if ($func === 'sha1') {
return sha1($data, true);
} else if ($func === 'sha256') {
if (function_exists('mhash')) {
return mhash(MHASH_SHA256 , $data);
}
}
/*require_once "Zend/OpenId/Exception.php";
throw new Zend_OpenId_Exception(
'Unsupported digest algorithm "' . $func . '".',
Zend_OpenId_Exception::UNSUPPORTED_DIGEST);*/
trigger_error('Unsupported digest algorithm '.$func , E_USER_ERROR);
}
/**
* Returns a full URL that was requested on current HTTP request.
*
* @return string
*/
public function selfUrl()
{
/*FIXME :
* if ($this->$selfUrl !== null) {
return $this->$selfUrl;
} */
if (isset($_SERVER['SCRIPT_URI'])) {
return $_SERVER['SCRIPT_URI'];
}
$url = '';
$port = '';
if (isset($_SERVER['HTTP_HOST'])) {
if (($pos = strpos($_SERVER['HTTP_HOST'], ':')) === false) {
if (isset($_SERVER['SERVER_PORT'])) {
$port = ':' . $_SERVER['SERVER_PORT'];
}
$url = $_SERVER['HTTP_HOST'];
} else {
$url = substr($_SERVER['HTTP_HOST'], 0, $pos);
$port = substr($_SERVER['HTTP_HOST'], $pos);
}
} else if (isset($_SERVER['SERVER_NAME'])) {
$url = $_SERVER['SERVER_NAME'];
if (isset($_SERVER['SERVER_PORT'])) {
$port = ':' . $_SERVER['SERVER_PORT'];
}
}
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
$url = 'https://' . $url;
if ($port == ':443') {
$port = '';
}
} else {
$url = 'http://' . $url;
if ($port == ':80') {
$port = '';
}
}
 
$url .= $port;
if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
$url .= $_SERVER['HTTP_X_REWRITE_URL'];
} elseif (isset($_SERVER['REQUEST_URI'])) {
$query = strpos($_SERVER['REQUEST_URI'], '?');
if ($query === false) {
$url .= $_SERVER['REQUEST_URI'];
} else {
$url .= substr($_SERVER['REQUEST_URI'], 0, $query);
}
} else if (isset($_SERVER['SCRIPT_URL'])) {
$url .= $_SERVER['SCRIPT_URL'];
} else if (isset($_SERVER['REDIRECT_URL'])) {
$url .= $_SERVER['REDIRECT_URL'];
} else if (isset($_SERVER['PHP_SELF'])) {
$url .= $_SERVER['PHP_SELF'];
} else if (isset($_SERVER['SCRIPT_NAME'])) {
$url .= $_SERVER['SCRIPT_NAME'];
if (isset($_SERVER['PATH_INFO'])) {
$url .= $_SERVER['PATH_INFO'];
}
}
return $url;
}
//TODO : vérifier si les fonctions FWK & ZEND sont bien équivalente
/**
* Retourne l'url absolue d'une url donnée
*
* @param string $url absilute or relative URL
* @return string
*/
public function absoluteUrl($url)
{
if (!empty($ur)) {
$urlAbsolue = new Url($url);
$urlAbsolue->normaliser();
$url = $urlAbsolue->getUrl();
} else {
$url = $this->selfUrl();
}
return $url;
/*
if (empty($url)) {
return $this->selfUrl();
} else if (!preg_match('|^([^:]+)://|', $url)) {
if (preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?]*)?((?:[?](?:[^#]*))?(?:#.*)?)$|', $this->selfUrl(), $reg)) {
$scheme = $reg[1];
$auth = $reg[2];
$host = $reg[3];
$port = $reg[4];
$path = $reg[5];
$query = $reg[6];
if ($url[0] == '/') {
return $scheme
. '://'
. $auth
. $host
. (empty($port) ? '' : (':' . $port))
. $url;
} else {
$dir = dirname($path);
return $scheme
. '://'
. $auth
. $host
. (empty($port) ? '' : (':' . $port))
. (strlen($dir) > 1 ? $dir : '')
. '/'
. $url;
}
}
}
return $url;*/
}
//TODO : voir si on ne peut pas glisser ça dans client ?
//FIXME : je met une fonction SIMPLISSIME a améliorer et reécrire
// La fonction de Zend est plus poussée est prend en compte le cas ou l'header ne peut pas etre envoyé
/**
* Rediriger vers la page du serveur avec les paramètres de confiration
*
* @param string $url URL de retour
* @param array $params paramètres additionnels
*/
public function redirect($url, $params) {
//1. fabriquer l'url Get
$urlRedirection = new Url($url);
$urlRedirection->setRequete($params);
//echo $urlRedirection->getUrl();
try {
header('Location:'.$urlRedirection->getUrl());
} catch (Exception $e) {
//TODO : voir autres méthodes de redirection
// > balise META
// > formulaire HTML
// > JS
}
}
}
?>
Property changes:
Added: svn:keywords
+Id Author Date Revision HeadURL
\ No newline at end of property
/trunk/framework/brouillons/Storage.php
New file
0,0 → 1,132
<?php
 
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_OpenId
* @subpackage Zend_OpenId_Consumer
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id$
*/
 
/**
* Abstract class to implement external storage for OpenID consumer
*
* @category Zend
* @package Zend_OpenId
* @subpackage Zend_OpenId_Consumer
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
abstract class Storage
{
 
/**
* Stores information about association identified by $url/$handle
*
* @param string $url OpenID server URL
* @param string $handle assiciation handle
* @param string $macFunc HMAC function (sha1 or sha256)
* @param string $secret shared secret
* @param long $expires expiration UNIX time
* @return void
*/
abstract public function addAssociation($url, $handle, $macFunc, $secret, $expires);
 
/**
* Gets information about association identified by $url
* Returns true if given association found and not expired and false
* otherwise
*
* @param string $url OpenID server URL
* @param string &$handle assiciation handle
* @param string &$macFunc HMAC function (sha1 or sha256)
* @param string &$secret shared secret
* @param long &$expires expiration UNIX time
* @return bool
*/
abstract public function getAssociation($url, &$handle, &$macFunc, &$secret, &$expires);
 
/**
* Gets information about association identified by $handle
* Returns true if given association found and not expired and false
* othverwise
*
* @param string $handle assiciation handle
* @param string &$url OpenID server URL
* @param string &$macFunc HMAC function (sha1 or sha256)
* @param string &$secret shared secret
* @param long &$expires expiration UNIX time
* @return bool
*/
abstract public function getAssociationByHandle($handle, &$url, &$macFunc, &$secret, &$expires);
 
/**
* Deletes association identified by $url
*
* @param string $url OpenID server URL
* @return void
*/
abstract public function delAssociation($url);
 
/**
* Stores information discovered from identity $id
*
* @param string $id identity
* @param string $realId discovered real identity URL
* @param string $server discovered OpenID server URL
* @param float $version discovered OpenID protocol version
* @param long $expires expiration UNIX time
* @return void
*/
abstract public function addDiscoveryInfo($id, $realId, $server, $version, $expires);
 
/**
* Gets information discovered from identity $id
* Returns true if such information exists and false otherwise
*
* @param string $id identity
* @param string &$realId discovered real identity URL
* @param string &$server discovered OpenID server URL
* @param float &$version discovered OpenID protocol version
* @param long &$expires expiration UNIX time
* @return bool
*/
abstract public function getDiscoveryInfo($id, &$realId, &$server, &$version, &$expires);
 
/**
* Removes cached information discovered from identity $id
*
* @param string $id identity
* @return bool
*/
abstract public function delDiscoveryInfo($id);
 
/**
* The function checks the uniqueness of openid.response_nonce
*
* @param string $provider openid.openid_op_endpoint field from authentication response
* @param string $nonce openid.response_nonce field from authentication response
* @return bool
*/
abstract public function isUniqueNonce($provider, $nonce);
 
/**
* Removes data from the uniqueness database that is older then given date
*
* @param string $date Date of expired data
*/
abstract public function purgeNonces($date=null);
}
Property changes:
Added: svn:keywords
+Id Author Date Revision HeadURL
\ No newline at end of property
/trunk/framework/brouillons/StorageFile.php
New file
0,0 → 1,512
<?php
 
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_OpenId
* @subpackage Zend_OpenId_Consumer
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id$
*/
 
/**
* External storage implemmentation using serialized files
*
* @category Zend
* @package Zend_OpenId
* @subpackage Zend_OpenId_Consumer
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class StorageFile extends Storage
{
 
/**
* Directory name to store data files in
*
* @var string $_dir
*/
private $_dir;
 
/**
* Constructs storage object and creates storage directory
*
* @param string $dir directory name to store data files in
* @throws Zend_OpenId_Exception
*/
public function __construct($dir = null)
{
if ($dir === null) {
$tmp = getenv('TMP');
if (empty($tmp)) {
$tmp = getenv('TEMP');
if (empty($tmp)) {
$tmp = "/tmp";
}
}
$user = get_current_user();
if (is_string($user) && !empty($user)) {
$tmp .= '/' . $user;
}
$dir = $tmp . '/openid/consumer';
}
$this->_dir = $dir;
if (!is_dir($this->_dir)) {
if (!@mkdir($this->_dir, 0700, 1)) {
/**
* @see Zend_OpenId_Exception
*/
require_once 'Zend/OpenId/Exception.php';
throw new Zend_OpenId_Exception(
'Cannot access storage directory ' . $dir,
Zend_OpenId_Exception::ERROR_STORAGE);
}
}
if (($f = fopen($this->_dir.'/assoc.lock', 'w+')) === null) {
/**
* @see Zend_OpenId_Exception
*/
/*require_once 'Zend/OpenId/Exception.php';
throw new Zend_OpenId_Exception(
'Cannot create a lock file in the directory ' . $dir,
Zend_OpenId_Exception::ERROR_STORAGE);*/
trigger_error('Cannot create a lock file in the directory ', E_STRICT);
}
fclose($f);
if (($f = fopen($this->_dir.'/discovery.lock', 'w+')) === null) {
/**
* @see Zend_OpenId_Exception
*/
/*require_once 'Zend/OpenId/Exception.php';
throw new Zend_OpenId_Exception(
'Cannot create a lock file in the directory ' . $dir,
Zend_OpenId_Exception::ERROR_STORAGE);*/
trigger_error('Cannot create a lock file in the directory', E_STRICT);
}
fclose($f);
if (($f = fopen($this->_dir.'/nonce.lock', 'w+')) === null) {
/**
* @see Zend_OpenId_Exception
*/
/*require_once 'Zend/OpenId/Exception.php';
throw new Zend_OpenId_Exception(
'Cannot create a lock file in the directory ' . $dir,
Zend_OpenId_Exception::ERROR_STORAGE);*/
trigger_error('Cannot create a lock file in the directory', E_STRICT);
}
fclose($f);
}
 
/**
* Stores information about association identified by $url/$handle
*
* @param string $url OpenID server URL
* @param string $handle assiciation handle
* @param string $macFunc HMAC function (sha1 or sha256)
* @param string $secret shared secret
* @param long $expires expiration UNIX time
* @return bool
*/
public function addAssociation($url, $handle, $macFunc, $secret, $expires)
{
$name1 = $this->_dir . '/assoc_url_' . md5($url);
$name2 = $this->_dir . '/assoc_handle_' . md5($handle);
$lock = @fopen($this->_dir . '/assoc.lock', 'w+');
if ($lock === false) {
return false;
}
if (!flock($lock, LOCK_EX)) {
fclose($lock);
return false;
}
try {
$f = @fopen($name1, 'w+');
if ($f === false) {
fclose($lock);
return false;
}
$data = serialize(array($url, $handle, $macFunc, $secret, $expires));
fwrite($f, $data);
if (function_exists('symlink')) {
@unlink($name2);
if (symlink($name1, $name2)) {
fclose($f);
fclose($lock);
return true;
}
}
$f2 = @fopen($name2, 'w+');
if ($f2) {
fwrite($f2, $data);
fclose($f2);
@unlink($name1);
$ret = true;
} else {
$ret = false;
}
fclose($f);
fclose($lock);
return $ret;
} catch (Exception $e) {
fclose($lock);
throw $e;
}
}
 
/**
* Gets information about association identified by $url
* Returns true if given association found and not expired and false
* otherwise
*
* @param string $url OpenID server URL
* @param string &$handle assiciation handle
* @param string &$macFunc HMAC function (sha1 or sha256)
* @param string &$secret shared secret
* @param long &$expires expiration UNIX time
* @return bool
*/
public function getAssociation($url, &$handle, &$macFunc, &$secret, &$expires)
{
$name1 = $this->_dir . '/assoc_url_' . md5($url);
$lock = @fopen($this->_dir . '/assoc.lock', 'w+');
if ($lock === false) {
return false;
}
if (!flock($lock, LOCK_EX)) {
fclose($lock);
return false;
}
try {
$f = @fopen($name1, 'r');
if ($f === false) {
fclose($lock);
return false;
}
$ret = false;
$data = stream_get_contents($f);
if (!empty($data)) {
list($storedUrl, $handle, $macFunc, $secret, $expires) = unserialize($data);
if ($url === $storedUrl && $expires > time()) {
$ret = true;
} else {
$name2 = $this->_dir . '/assoc_handle_' . md5($handle);
fclose($f);
@unlink($name2);
@unlink($name1);
fclose($lock);
return false;
}
}
fclose($f);
fclose($lock);
return $ret;
} catch (Exception $e) {
fclose($lock);
throw $e;
}
}
 
/**
* Gets information about association identified by $handle
* Returns true if given association found and not expired and false
* otherwise
*
* @param string $handle assiciation handle
* @param string &$url OpenID server URL
* @param string &$macFunc HMAC function (sha1 or sha256)
* @param string &$secret shared secret
* @param long &$expires expiration UNIX time
* @return bool
*/
public function getAssociationByHandle($handle, &$url, &$macFunc, &$secret, &$expires)
{
$name2 = $this->_dir . '/assoc_handle_' . md5($handle);
$lock = @fopen($this->_dir . '/assoc.lock', 'w+');
if ($lock === false) {
return false;
}
if (!flock($lock, LOCK_EX)) {
fclose($lock);
return false;
}
try {
$f = @fopen($name2, 'r');
if ($f === false) {
fclose($lock);
return false;
}
$ret = false;
$data = stream_get_contents($f);
if (!empty($data)) {
list($url, $storedHandle, $macFunc, $secret, $expires) = unserialize($data);
if ($handle === $storedHandle && $expires > time()) {
$ret = true;
} else {
fclose($f);
@unlink($name2);
$name1 = $this->_dir . '/assoc_url_' . md5($url);
@unlink($name1);
fclose($lock);
return false;
}
}
fclose($f);
fclose($lock);
return $ret;
} catch (Exception $e) {
fclose($lock);
throw $e;
}
}
 
/**
* Deletes association identified by $url
*
* @param string $url OpenID server URL
* @return bool
*/
public function delAssociation($url)
{
$name1 = $this->_dir . '/assoc_url_' . md5($url);
$lock = @fopen($this->_dir . '/assoc.lock', 'w+');
if ($lock === false) {
return false;
}
if (!flock($lock, LOCK_EX)) {
fclose($lock);
return false;
}
try {
$f = @fopen($name1, 'r');
if ($f === false) {
fclose($lock);
return false;
}
$data = stream_get_contents($f);
if (!empty($data)) {
list($storedUrl, $handle, $macFunc, $secret, $expires) = unserialize($data);
if ($url === $storedUrl) {
$name2 = $this->_dir . '/assoc_handle_' . md5($handle);
fclose($f);
@unlink($name2);
@unlink($name1);
fclose($lock);
return true;
}
}
fclose($f);
fclose($lock);
return true;
} catch (Exception $e) {
fclose($lock);
throw $e;
}
}
 
/**
* Stores information discovered from identity $id
*
* @param string $id identity
* @param string $realId discovered real identity URL
* @param string $server discovered OpenID server URL
* @param float $version discovered OpenID protocol version
* @param long $expires expiration UNIX time
* @return bool
*/
public function addDiscoveryInfo($id, $realId, $server, $version, $expires)
{
$name = $this->_dir . '/discovery_' . md5($id);
$lock = @fopen($this->_dir . '/discovery.lock', 'w+');
if ($lock === false) {
return false;
}
if (!flock($lock, LOCK_EX)) {
fclose($lock);
return false;
}
try {
$f = @fopen($name, 'w+');
if ($f === false) {
fclose($lock);
return false;
}
$data = serialize(array($id, $realId, $server, $version, $expires));
fwrite($f, $data);
fclose($f);
fclose($lock);
return true;
} catch (Exception $e) {
fclose($lock);
throw $e;
}
}
 
/**
* Gets information discovered from identity $id
* Returns true if such information exists and false otherwise
*
* @param string $id identity
* @param string &$realId discovered real identity URL
* @param string &$server discovered OpenID server URL
* @param float &$version discovered OpenID protocol version
* @param long &$expires expiration UNIX time
* @return bool
*/
public function getDiscoveryInfo($id, &$realId, &$server, &$version, &$expires)
{
$name = $this->_dir . '/discovery_' . md5($id);
$lock = @fopen($this->_dir . '/discovery.lock', 'w+');
if ($lock === false) {
return false;
}
if (!flock($lock, LOCK_EX)) {
fclose($lock);
return false;
}
try {
$f = @fopen($name, 'r');
if ($f === false) {
fclose($lock);
return false;
}
$ret = false;
$data = stream_get_contents($f);
if (!empty($data)) {
list($storedId, $realId, $server, $version, $expires) = unserialize($data);
if ($id === $storedId && $expires > time()) {
$ret = true;
} else {
fclose($f);
@unlink($name);
fclose($lock);
return false;
}
}
fclose($f);
fclose($lock);
return $ret;
} catch (Exception $e) {
fclose($lock);
throw $e;
}
}
 
/**
* Removes cached information discovered from identity $id
*
* @param string $id identity
* @return bool
*/
public function delDiscoveryInfo($id)
{
$name = $this->_dir . '/discovery_' . md5($id);
$lock = @fopen($this->_dir . '/discovery.lock', 'w+');
if ($lock === false) {
return false;
}
if (!flock($lock, LOCK_EX)) {
fclose($lock);
return false;
}
try {
@unlink($name);
fclose($lock);
return true;
} catch (Exception $e) {
fclose($lock);
throw $e;
}
}
 
/**
* The function checks the uniqueness of openid.response_nonce
*
* @param string $provider openid.openid_op_endpoint field from authentication response
* @param string $nonce openid.response_nonce field from authentication response
* @return bool
*/
public function isUniqueNonce($provider, $nonce)
{
$name = $this->_dir . '/nonce_' . md5($provider.';'.$nonce);
echo $name;
$lock = @fopen($this->_dir . '/nonce.lock', 'w+');
if ($lock === false) {
return false;
}
if (!flock($lock, LOCK_EX)) {
fclose($lock);
return false;
}
try {
$f = @fopen($name, 'x');
if ($f === false) {
fclose($lock);
return false;
}
fwrite($f, $provider.';'.$nonce);
fclose($f);
fclose($lock);
return true;
} catch (Exception $e) {
fclose($lock);
throw $e;
}
}
 
/**
* Removes data from the uniqueness database that is older then given date
*
* @param mixed $date date of expired data
*/
public function purgeNonces($date=null)
{
$lock = @fopen($this->_dir . '/nonce.lock', 'w+');
if ($lock !== false) {
flock($lock, LOCK_EX);
}
try {
if (!is_int($date) && !is_string($date)) {
$nonceFiles = glob($this->_dir . '/nonce_*');
foreach ((array) $nonceFiles as $name) {
@unlink($name);
}
unset($nonceFiles);
} else {
if (is_string($date)) {
$time = time($date);
} else {
$time = $date;
}
$nonceFiles = glob($this->_dir . '/nonce_*');
foreach ((array) $nonceFiles as $name) {
if (filemtime($name) < $time) {
@unlink($name);
}
}
unset($nonceFiles);
}
if ($lock !== false) {
fclose($lock);
}
} catch (Exception $e) {
if ($lock !== false) {
fclose($lock);
}
throw $e;
}
}
}
Property changes:
Added: svn:keywords
+Id Author Date Revision HeadURL
\ No newline at end of property
/trunk/framework/brouillons/DiffieHellmanUtil.php
New file
0,0 → 1,224
<?php
 
/**
* Classe utilitaire proposant des fonctions permettant la réalisation d'un
* échange de clé Diffie Hellman
*
* "En cryptographie, l'échange de clés Diffie-Hellman, du nom de ses auteurs
* Whitfield Diffie et Martin Hellman, est une méthode par laquelle deux
* personnes peuvent se mettre d'accord sur un nombre (qu'ils peuvent utiliser
* comme clé pour chiffrer la conversation suivante) sans qu'une troisième
* personne appelée Ève puisse découvrir le nombre, même en ayant écouté tous
* leurs échanges."
*
* Voir http://fr.wikipedia.org/wiki/%C3%89change_de_cl%C3%A9s_Diffie-Hellman
*
* */
class DiffieHellmanUtil {
// Default Diffie-Hellman key generator (1024 bit)
const DH_P = 'dcf93a0b883972ec0e19989ac5a2ce310e1d37717e8d9571bb7623731866e61ef75a2e27898b057f9891c2e27a639c3f29b60814581cd3b2ca3986d2683705577d45c2e7e52dc81c7a171876e5cea74b1448bfdfaf18828efd2519f14e45e3826634af1949e5b535cc829a483b8a76223e5d490a257f05bdff16f2fb22c583ab';
//Default Diffie-Hellman prime number (should be 2 or 5)
const DH_G = '02';
 
/**
* Performs the first step of a Diffie-Hellman key exchange by generating
* private and public DH values based on given prime number $p and
* generator $g. Both sides of key exchange MUST have the same prime number
* and generator. In this case they will able to create a random shared
* secret that is never send from one to the other.
*
* @param string $p prime number in binary representation
* @param string $g generator in binary representation
* @param string $priv_key private key in binary representation
* @return mixed
*/
public static function createDhKey($p, $g, $priv_key = null)
{
if (function_exists('openssl_dh_compute_key')) {
$dh_details = array(
'p' => $p,
'g' => $g
);
if ($priv_key !== null) {
$dh_details['priv_key'] = $priv_key;
}
return openssl_pkey_new(array('dh'=>$dh_details));
} else {
$bn_p = self::binToBigNum($p);
$bn_g = self::binToBigNum($g);
if ($priv_key === null) {
$priv_key = self::randomBytes(self::strlen($p));
}
$bn_priv_key = self::binToBigNum($priv_key);
if (extension_loaded('gmp')) {
$bn_pub_key = gmp_powm($bn_g, $bn_priv_key, $bn_p);
} else if (extension_loaded('bcmath')) {
$bn_pub_key = bcpowmod($bn_g, $bn_priv_key, $bn_p);
}
$pub_key = self::bigNumToBin($bn_pub_key);
 
return array(
'p' => $bn_p,
'g' => $bn_g,
'priv_key' => $bn_priv_key,
'pub_key' => $bn_pub_key,
'details' => array(
'p' => $p,
'g' => $g,
'priv_key' => $priv_key,
'pub_key' => $pub_key));
}
}
/**
* Returns an associative array with Diffie-Hellman key components in
* binary representation. The array includes original prime number 'p' and
* generator 'g', random private key 'priv_key' and corresponding public
* key 'pub_key'.
*
* @param mixed $dh Diffie-Hellman key
* @return array
*/
public static function getDhKeyDetails($dh)
{
if (function_exists('openssl_dh_compute_key')) {
$details = openssl_pkey_get_details($dh);
if (isset($details['dh'])) {
return $details['dh'];
}
} else {
return $dh['details'];
}
}
// Depuis OpenId.php, les fonctions de Diffie-Hellman
// TODO : voir si ça peut être externaliser pour être réutilisé ? => dans utilitaires
/**
* Computes the shared secret from the private DH value $dh and the other
* party's public value in $pub_key
*
* @param string $pub_key other party's public value
* @param mixed $dh Diffie-Hellman key
* @return string
* @throws Zend_OpenId_Exception
*/
public function computeDhSecret($pub_key, $dh)
{
if (function_exists('openssl_dh_compute_key')) {
$ret = openssl_dh_compute_key($pub_key, $dh);
if (ord($ret[0]) > 127) {
$ret = "\0" . $ret;
}
return $ret;
} else if (extension_loaded('gmp')) {
$bn_pub_key = self::binToBigNum($pub_key);
$bn_secret = gmp_powm($bn_pub_key, $dh['priv_key'], $dh['p']);
return self::bigNumToBin($bn_secret);
} else if (extension_loaded('bcmath')) {
$bn_pub_key = self::binToBigNum($pub_key);
$bn_secret = bcpowmod($bn_pub_key, $dh['priv_key'], $dh['p']);
return self::bigNumToBin($bn_secret);
}
/*require_once "Zend/OpenId/Exception.php";
throw new Zend_OpenId_Exception(
'The system doesn\'t have proper big integer extension',
Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH);*/
trigger_error('Le système ne gère pas les nombre de taille arbitraire', E_STRICT);
}
/**
* Takes an arbitrary precision integer and returns its shortest big-endian
* two's complement representation.
*
* Arbitrary precision integers MUST be encoded as big-endian signed two's
* complement binary strings. Henceforth, "btwoc" is a function that takes
* an arbitrary precision integer and returns its shortest big-endian two's
* complement representation. All integers that are used with
* Diffie-Hellman Key Exchange are positive. This means that the left-most
* bit of the two's complement representation MUST be zero. If it is not,
* implementations MUST add a zero byte at the front of the string.
*
* @param string $str binary representation of arbitrary precision integer
* @return string big-endian signed representation
*/
public function btwoc($str)
{
if (ord($str[0]) > 127) {
return "\0" . $str;
}
return $str;
}
/**
* Produces string of random byte of given length.
*
* @param integer $len length of requested string
* @return string RAW random binary string
*/
public function randomBytes($len)
{
$key = '';
for($i=0; $i < $len; $i++) {
$key .= chr(mt_rand(0, 255));
}
return $key;
}
/**
* Returns lenght of binary string in bytes
*
* @param string $str
* @return int the string lenght
*/
static public function strlen($str)
{
if (extension_loaded('mbstring') &&
(((int)ini_get('mbstring.func_overload')) & 2)) {
return mb_strlen($str, 'latin1');
} else {
return strlen($str);
}
}
/**
* Converts binary representation into ext/gmp or ext/bcmath big integer
* representation.
*
* @param string $bin binary representation of big number
* @return mixed
* @throws Zend_OpenId_Exception
*/
protected function binToBigNum($bin)
{
if (extension_loaded('gmp')) {
return gmp_init(bin2hex($bin), 16);
} else if (extension_loaded('bcmath')) {
$bn = 0;
$len = self::strlen($bin);
for ($i = 0; $i < $len; $i++) {
$bn = bcmul($bn, 256);
$bn = bcadd($bn, ord($bin[$i]));
}
return $bn;
}
/*require_once "Zend/OpenId/Exception.php";
throw new Zend_OpenId_Exception(
'The system doesn\'t have proper big integer extension',
Zend_OpenId_Exception::UNSUPPORTED_LONG_MATH);*/
trigger_error('Le système ne gère pas les nombre de taille arbitraire', E_STRICT);
}
}
?>
Property changes:
Added: svn:keywords
+Id Author Date Revision HeadURL
\ No newline at end of property