SMMP/build/token/core/TreeToken.php

302 lines
7.3 KiB
PHP
Raw Permalink Normal View History

2017-10-27 15:59:46 +00:00
<?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;
}
}