Added build/core/TreeToken
This commit is contained in:
parent
0cbb21ece8
commit
4f2144f64b
|
@ -0,0 +1,302 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace token\core;
|
||||||
|
|
||||||
|
class TreeToken{
|
||||||
|
|
||||||
|
private static $DEBUG = false;
|
||||||
|
|
||||||
|
private $secret = null;
|
||||||
|
private $step = 0;
|
||||||
|
private $max_step = 0;
|
||||||
|
private static $salt = '_9284 we;\'sa';
|
||||||
|
private static $pepper = 'dasklj3948\'3=2';
|
||||||
|
private $sync = null;
|
||||||
|
|
||||||
|
|
||||||
|
/* (1) Constructs a new TreeToken
|
||||||
|
*
|
||||||
|
* @max_step<int> Max. number of children tokens
|
||||||
|
*
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
public function __construct($max_step=100){
|
||||||
|
/* (1) Check argument */
|
||||||
|
if( abs(intval($max_step)) !== $max_step || $max_step < 0 )
|
||||||
|
throw new \Exception('Invalid argument type.');
|
||||||
|
|
||||||
|
/* (2) Manage session */
|
||||||
|
if( session_status() != PHP_SESSION_ACTIVE )
|
||||||
|
\session_start();
|
||||||
|
|
||||||
|
/* (3) Set attributes */
|
||||||
|
$this->max_step = $max_step+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* (2) Gets the existing parent
|
||||||
|
*
|
||||||
|
* @return status<bool> TRUE: right parent + token
|
||||||
|
* FALSE: Invalid token
|
||||||
|
* NULL: No parent found
|
||||||
|
*
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
private function check_parent(){
|
||||||
|
|
||||||
|
/* (1) Check system state
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
$has_token = isset($_COOKIE['_PUBLIC_']) && preg_match( '/^[a-z0-9]{128}$/i', $_COOKIE['_PUBLIC_'] );
|
||||||
|
$has_parent = isset($_SESSION['_PRIVATE_']) && preg_match( '/^([a-z0-9]{128})\.(\d+)$/i', $_SESSION['_PRIVATE_'], $p_match );
|
||||||
|
|
||||||
|
/* (1) If no parent -> NULL */
|
||||||
|
if( !$has_parent )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
/* (2) But if no token -> FALSE */
|
||||||
|
if( !$has_token )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( self::$DEBUG ){
|
||||||
|
echo "* PARENT RECEIVED *\n";
|
||||||
|
echo 'sess_id: '.session_id()."\n";
|
||||||
|
echo 'pub: '.$_COOKIE['_PUBLIC_']."\n";
|
||||||
|
echo 'priv: '.$_SESSION['_PRIVATE_']."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* (2) Check parent token
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Check public token */
|
||||||
|
if( self::tgen($p_match[1], $this->max_step) !== $_COOKIE['_PUBLIC_'] ){
|
||||||
|
|
||||||
|
if( self::$DEBUG )
|
||||||
|
echo "<b>/!\ invalid parent pub (token)</b>\n";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (2) Inherit parent properties */
|
||||||
|
$this->secret = $p_match[1];
|
||||||
|
$this->step = $p_match[2];
|
||||||
|
|
||||||
|
/* (3) If valid token */
|
||||||
|
if( self::$DEBUG )
|
||||||
|
echo "[ <b>Valid parent pub (token)</b> ]\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* (3) Create or regenerate a parent
|
||||||
|
*
|
||||||
|
* @inName<inType> inDesc
|
||||||
|
*
|
||||||
|
* @return outName<outType> outDesc
|
||||||
|
*
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
public function init_parent(){
|
||||||
|
/* (1) Check parent
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Process check */
|
||||||
|
$valid_parent = $this->check_parent();
|
||||||
|
|
||||||
|
/* (2) If invalid parent -> destroy session */
|
||||||
|
if( $valid_parent === false ){
|
||||||
|
|
||||||
|
if( self::$DEBUG )
|
||||||
|
echo "-> new session <-\n";
|
||||||
|
|
||||||
|
\session_regenerate_id(true); // true: delete old session
|
||||||
|
\session_unset();
|
||||||
|
\session_destroy();
|
||||||
|
\session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* (2) Init new parent
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Choose new secret */
|
||||||
|
$this->secret = self::tgen(uniqid());
|
||||||
|
|
||||||
|
/* (2) Set step = max */
|
||||||
|
$this->step = $this->max_step;
|
||||||
|
|
||||||
|
/* (3) Generate PRIVATE */
|
||||||
|
$_SESSION['_PRIVATE_'] = $this->secret.'.'.$this->max_step;
|
||||||
|
|
||||||
|
/* (4) Generate PUBLIC */
|
||||||
|
$_COOKIE['_PUBLIC_'] = self::tgen($this->secret, $this->max_step);
|
||||||
|
setcookie('_PUBLIC_', $_COOKIE['_PUBLIC_'], time()+30*60, '/');
|
||||||
|
|
||||||
|
if( self::$DEBUG ){
|
||||||
|
echo "\n* PARENT UPDATED *\n";
|
||||||
|
echo 'sess_id: '.session_id()."\n";
|
||||||
|
echo 'pub: '.$_COOKIE['_PUBLIC_']."\n";
|
||||||
|
echo 'priv: '.$_SESSION['_PRIVATE_']."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (5) Granted */
|
||||||
|
return $valid_parent !== false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* (4) Checks a child
|
||||||
|
*
|
||||||
|
* @return status<bool> TRUE: Valid child
|
||||||
|
* FALSE: Invalid token
|
||||||
|
*
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
private function check_child(){
|
||||||
|
|
||||||
|
/* (1) Check the parent
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Process parent check */
|
||||||
|
$valid_parent = $this->check_parent();
|
||||||
|
|
||||||
|
/* (2) Manage missing OR invalid parent */
|
||||||
|
if( $valid_parent !== true )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* (2) Check system state
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
$has_token = !is_null(self::getTreeToken()) && preg_match( '/^[a-z0-9]{128}$/i', self::getTreeToken() );
|
||||||
|
|
||||||
|
/* (1) If no token -> false */
|
||||||
|
if( !$has_token )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* (3) Check child token
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) If no more steps -> false */
|
||||||
|
if( $this->step <= 1 ){
|
||||||
|
|
||||||
|
if( self::$DEBUG )
|
||||||
|
echo "<b>/!\ no more token available</b>\n";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( self::$DEBUG ){
|
||||||
|
echo "* CHILD RECEIVED *\n";
|
||||||
|
echo 'sess_id: '.session_id()."\n";
|
||||||
|
echo 'token: '.self::getTreeToken()."\n";
|
||||||
|
echo 'priv: '.$this->secret.'.'.$this->step."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (2) Check child token */
|
||||||
|
if( self::tgen($this->secret, $this->step) !== self::getTreeToken() ){
|
||||||
|
|
||||||
|
if( self::$DEBUG )
|
||||||
|
echo "<b>/!\ invalid child token</b>\n";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (3) If valid token */
|
||||||
|
if( self::$DEBUG )
|
||||||
|
echo "[ <b>Valid child token</b> ]\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* (5) Updates a child
|
||||||
|
*
|
||||||
|
* @inName<inType> inDesc
|
||||||
|
*
|
||||||
|
* @return outName<outType> outDesc
|
||||||
|
*
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
public function init_child(){
|
||||||
|
/* (1) Check child
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Process check */
|
||||||
|
$valid_child = $this->check_child();
|
||||||
|
|
||||||
|
/* (2) If invalid child -> destroy session */
|
||||||
|
if( $valid_child !== true )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
/* (2) Update parent for other children
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
/* (1) Decrement step */
|
||||||
|
$this->step--;
|
||||||
|
|
||||||
|
if( $this->step > 1 ){ // only if it is not the last step
|
||||||
|
|
||||||
|
/* (2) Update step in session */
|
||||||
|
$_SESSION['_PRIVATE_'] = $this->secret.'.'.$this->step;
|
||||||
|
|
||||||
|
/* (3) Generate child-specific PUBLIC */
|
||||||
|
header('X-Tree-Token: '.self::tgen($this->secret, $this->step));
|
||||||
|
|
||||||
|
if( self::$DEBUG ){
|
||||||
|
echo "\n* CHILD UPDATED *\n";
|
||||||
|
echo 'sess_id: '.session_id()."\n";
|
||||||
|
echo 'token: '.self::getTreeToken()."\n";
|
||||||
|
echo 'priv: '.$_SESSION['_PRIVATE_']."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (2) Granted */
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* (6) Get custom TreeToken header
|
||||||
|
*
|
||||||
|
* @return token<String> TreeToken fetched from header list
|
||||||
|
* NULL on error
|
||||||
|
*
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
private static function getTreeToken(){
|
||||||
|
|
||||||
|
/* (1) Check if exsits */
|
||||||
|
if( !isset($_SERVER['HTTP_X_TREE_TOKEN']))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
/* (2) Return result */
|
||||||
|
return $_SERVER['HTTP_X_TREE_TOKEN'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* (x) Generates a pseudo-rdm token
|
||||||
|
*
|
||||||
|
* @data<String> Seed to use
|
||||||
|
* @depth<int> Token depth
|
||||||
|
*
|
||||||
|
* @return token<String> @data hashed @depth times
|
||||||
|
*
|
||||||
|
---------------------------------------------------------*/
|
||||||
|
private static function tgen($data, $depth=1){
|
||||||
|
/* (0) If depth < 1 -> depth=1 */
|
||||||
|
$depth = ( $depth < 1 ) ? 1 : $depth;
|
||||||
|
|
||||||
|
/* (1) Apply salt */
|
||||||
|
$hash = self::$salt.$data;
|
||||||
|
|
||||||
|
/* (2) Hash @depth times */
|
||||||
|
for( $d = 0 ; $d < $depth ; $d++ )
|
||||||
|
$hash = ( $d == $depth-1 ) ? hash('sha512', $hash.self::$pepper) : hash('sha512', $hash);
|
||||||
|
|
||||||
|
/* (3) Return hash */
|
||||||
|
return $hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue