*/ class struct_srp_toolset/**/ { /** * @var function,string> */ public $encode; /** * @var function,type_number> */ public $decode; /** * @var function,type_number> */ public $make; /** * @var function,type_number> */ public $zero; /** * @todo check if really needed * @var function,type_number> */ public $one; /** * @var function,string> */ public $to_string; /** * @var function,boolean> */ public $equal; /** * @var function,type_number> */ public $add; /** * @var function,type_number> */ public $subtract; /** * @var function,type_number> */ public $multiply; /** * @var function,type_number> */ public $xor; /** * @var function,type_number> */ public $mod; /** * @var function,type_number> */ public $modpow; /** * @var function,string> */ public $join; /** * @var function<> */ public $compute_hash; /** * @var function,string> */ public $compute_password_hash; /** * @var function,type_number> */ public $compute_random; /** * @param array $tools { * record< * encode:function,string>, * decode:function,type_number>, * make:function,type_number>, * zero:function,type_number>, * one:function,type_number>, * to_string:function,string>, * equal:function,boolean>, * add:function,type_number>, * subtract:function,type_number>, * multiply:function,type_number>, * xor:function,type_number>, * mod:function,type_number>, * modpow:function,type_number>, * join:function,string>, * compute_hash:function<>, * compute_password_hash:function,string>, * compute_random:function,type_number>, * > * } */ public function __construct( array $tools ) { $this->encode = $tools['encode']; $this->decode = $tools['decode']; $this->make = $tools['make']; $this->zero = $tools['zero']; $this->one = $tools['one']; $this->to_string = $tools['to_string']; $this->equal = $tools['equal']; $this->add = $tools['add']; $this->subtract = $tools['subtract']; $this->multiply = $tools['multiply']; $this->xor = $tools['xor']; $this->mod = $tools['mod']; $this->modpow = $tools['modpow']; $this->join = $tools['join']; $this->compute_hash = $tools['compute_hash']; $this->compute_password_hash = $tools['compute_password_hash']; $this->compute_random = $tools['compute_random']; } } /** * @author Christian Fraß */ function _srp_toolset_simple( ) : struct_srp_toolset/**/ { return (new struct_srp_toolset/**/( [ 'encode' => (fn($number) => \sprintf('%X', $number)), 'decode' => (fn($string) => \intval($string, 16)), 'make' => (fn($base, $digits) => \intval($digits, $base)), 'zero' => (fn() => 0), 'one' => (fn() => 1), 'to_string' => (fn($number) => \sprintf('%X', $number)), 'equal' => (fn($x, $y) => ($x === $y)), 'add' => (fn($x, $y) => ($x + $y)), 'subtract' => (fn($x, $y) => ($x - $y)), 'multiply' => (fn($x, $y) => ($x * $y)), 'xor' => (fn($x, $y) => ($x ^ $y)), // 'mod' => (fn($x, $y) => ($x % $y)), 'mod' => (fn($x, $y) => \alveolata\math\mod($x, $y)), // 'modpow' => (fn($base, $exponent, $modulus) => (($base ** $exponent) % $modulus)), 'modpow' => (fn($base, $exponent, $modulus) => \alveolata\math\modpow($base, $exponent, $modulus)), 'join' => (fn($parts) => \implode('', $parts)), 'compute_hash' => (fn($string) => $string), 'compute_password_hash' => (fn($salt, $password) => \sprintf( '%04X', \alveolata\list_\reduce( \alveolata\list_\map( \str_split($password . $salt), (fn($character) => \ord($character)) ), 0, (fn($x, $y) => (($x + $y) & 0xFFFF)) ) )), 'compute_random' => (fn() => \rand(1, 7)), ] )); } /** * requires composer package phpseclib/phpseclib * * @see https://api.phpseclib.com/master/phpseclib3/Math/BigInteger.html * @author Christian Fraß */ function _srp_toolset_biginteger( ) : struct_srp_toolset/*<\phpseclib3\Math\BigInteger>*/ { return (new struct_srp_toolset/*<\phpseclib3\Math\BigInteger>*/( [ 'encode' => (fn($number) => $number->toHex()), 'decode' => (fn($string) => (new \phpseclib3\Math\BigInteger($string, 16))), 'make' => (fn($base, $digits) => (new \phpseclib3\Math\BigInteger($digits, $base))), 'zero' => (fn() => (new \phpseclib3\Math\BigInteger('0', 16))), 'one' => (fn() => (new \phpseclib3\Math\BigInteger('1', 16))), 'to_string' => (fn($number) => $number->toHex()), 'equal' => (fn($x, $y) => $x->equals($y)), 'add' => (fn($x, $y) => $x->add($y)), 'subtract' => (fn($x, $y) => $x->subtract($y)), 'multiply' => (fn($x, $y) => $x->multiply($y)), 'xor' => (fn($x, $y) => $x->bitwise_xor($y)), 'mod' => (fn($x, $y) => $x->modPow(new \phpseclib3\Math\BigInteger('1', 16), $y)), 'modpow' => (fn($base, $exponent, $modulus) => $base->modPow($exponent, $modulus)), 'join' => (fn($parts) => \implode('', $parts)), 'compute_hash' => (fn($string) => $string), /* 'compute_password_hash' => (fn($salt, $password) => \Vinsaj9\Crypto\Scrypt\scrypt( $password, $salt, 16384, // TODO: wadd is dadd? 8, // TODO: wadd is dadd? 1, // TODO: wadd is dadd? 64 // TODO: wadd is dadd? )), */ /* 'compute_password_hash' => (fn($salt, $password) => \password_hash( $password, \PASSWORD_BCRYPT, [ 'salt' => $salt, ] )), */ 'compute_password_hash' => (fn($salt, $password) => \hash( 'sha256', ($password . $salt) )), 'compute_random' => (fn() => \phpseclib3\Math\BigInteger::random(24)), ] )); } /** * @author Christian Fraß */ class struct_srp_subject/**/ { /** * @var struct_srp_toolset */ public $toolset; /** * @var type_number */ public $base; /** * @var type_number */ public $modulus; /** */ public function __construct( struct_srp_toolset/**/ $toolset, /*type_number */$base, /*type_number */$modulus ) { $this->toolset = $toolset; $this->base = $base; $this->modulus = $modulus; } } /** */ function srp_subject_make/**/( struct_srp_toolset/**/ $toolset, /*type_number */$base, /*type_number */$modulus ) { return ( new struct_srp_subject( $toolset, $base, $modulus ) ); } /** * @return struct_srp_subject */ function srp_subject_test( ) : struct_srp_subject/**/ { $toolset = _srp_toolset_simple(); // <2>_19 = {2,4,8,16,13,7,14,9,18,17,15,11,3,6,12,5,10,1} return (new struct_srp_subject/**/( $toolset, ($toolset->decode)('2'), ($toolset->decode)('1323') // wollte eigentlich 19 nehmen, aber das ist zu klein :/ )); } /** * sind die Werte, die bytegod immer nimmt * requires composer package phpseclib/phpseclib * * @return struct_srp_subject<\phpseclib3\Math\BigInteger> */ function srp_subject_biginteger_1( ) : struct_srp_subject/*<\phpseclib3\Math\BigInteger>*/ { $toolset = _srp_toolset_biginteger(); return (new struct_srp_subject/*<\phpseclib3\Math\BigInteger>*/( $toolset, ($toolset->decode)('2'), ($toolset->decode)('AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73') )); } /** * @author Christian Fraß */ class _state { public static $subject_default = null; } /** * @author Christian Fraß */ function srp_set_default_subject( \Closure $factory ) : void { _state::$subject_default = $factory; } /** * @return struct_srp_subject * @author Christian Fraß */ function srp_subject_default( ) : struct_srp_subject { if (_state::$subject_default === null) { srp_set_default_subject(fn() => srp_subject_biginteger_1()); } return (_state::$subject_default)(); } /** * @param string $salt * @param string $password * @return array { * record< * x:type_number, * v:type_number * > * } */ function srp_compute_verifier/**/( struct_srp_subject/**/ $subject, string $salt, string $password ) : array { $x = ($subject->toolset->decode)( ($subject->toolset->compute_password_hash)($salt, $password) ); // g^x $verifier = ($subject->toolset->modpow)( $subject->base, $x, $subject->modulus ); return [ 'x' => $x, 'verifier' => ($verifier), ]; } /** * compute A * * @return array { * record< * k:type_number, * a_exponent:type_number, * a_value:type_number * > * } */ function srp_compute_a/**/( struct_srp_subject $subject ) : array { // set asymmetry factor $k = ($subject->toolset->decode)( ($subject->toolset->compute_hash)( ($subject->toolset->join)([ ($subject->toolset->to_string)($subject->modulus), ($subject->toolset->to_string)($subject->base), ]) ) ); // compute random until it fits requirements by specification while (true) { $a_exponent = ($subject->toolset->compute_random)(); // g^a % N $a_value = ($subject->toolset->modpow)($subject->base, $a_exponent, $subject->modulus); // (g^a % N) % N == 0 // if (! ($subject->toolset->equal)(($subject->toolset->mod)($a_value, $subject->modulus), ($subject->toolset->zero)())) { if (! ($subject->toolset->equal)($a_value, ($subject->toolset->zero)())) { break; } } return [ 'k' => $k, 'a_exponent' => $a_exponent, 'a_value' => $a_value, ]; } /** * compute B * * @param type_number $verifier * @return array { * record< * k:type_number, * b_exponent:type_number, * b_value:type_number * > * } */ function srp_compute_b/**/( struct_srp_subject/**/ $subject, /**/ $verifier ) : array { // asymmetry factor $k = ($subject->toolset->decode)( ($subject->toolset->compute_hash)( ($subject->toolset->join)([ ($subject->toolset->to_string)($subject->modulus), ($subject->toolset->to_string)($subject->base), ]) ) ); $b_exponent = ($subject->toolset->compute_random)(); // k*v $Bl = ($subject->toolset->multiply)($k, $verifier); // g^b $Br = ($subject->toolset->modpow)($subject->base, $b_exponent, $subject->modulus); // k*v + g^b $Bn = ($subject->toolset->add)($Bl, $Br); // (k*v + g^b) % N $b_value = ($subject->toolset->mod)($Bn, $subject->modulus); return [ 'k' => $k, 'b_exponent' => $b_exponent, 'b_value' => $b_value, ]; } /** * @return array { * record< * k_client:string * > * } */ function srp_compute_k_client/**/( struct_srp_subject/**/ $subject, /*type_number */$a_exponent, /*type_number */$a_value, /*type_number */$b_value, /*type_number */$x, /*type_number */$k ) : array { $u = ($subject->toolset->decode)( ($subject->toolset->compute_hash)( ($subject->toolset->join)([ ($subject->toolset->to_string)($a_value), ($subject->toolset->to_string)($b_value), ]) ) ); // B - k*g^x $Sl = ($subject->toolset->subtract)( $b_value, ($subject->toolset->multiply)( $k, ($subject->toolset->modpow)( $subject->base, $x, $subject->modulus ) ) ); // a + u*x $Sr = ($subject->toolset->add)( $a_exponent, ($subject->toolset->multiply)( $u, $x ) ); // (B - k*g^x) ^ (a + u*x) $S = ($subject->toolset->modpow)( $Sl, $Sr, $subject->modulus ); $k_client = ($subject->toolset->compute_hash)(($subject->toolset->to_string)($S)); return [ 'k_client' => $k_client, ]; } /** * @return array { * record< * k_server:string * > * } */ function srp_compute_k_server/**/( struct_srp_subject/**/ $subject, /*type_number */$a_value, /*type_number */$b_exponent, /*type_number */$b_value, /*type_number */$verifier ) : array { $u = ($subject->toolset->decode)( ($subject->toolset->compute_hash)( ($subject->toolset->join)([ ($subject->toolset->to_string)($a_value), ($subject->toolset->to_string)($b_value), ]) ) ); // A*v^u $Sl = ($subject->toolset->multiply)( $a_value, ($subject->toolset->modpow)( $verifier, $u, $subject->modulus ) ); // (A*v^u)^b $S = ($subject->toolset->modpow)( $Sl, $b_exponent, $subject->modulus ); $k_server = ($subject->toolset->compute_hash)(($subject->toolset->to_string)($S)); return [ 'k_server' => $k_server, ]; } /** * @return array { * record< * m1:string * > * } */ function srp_compute_m1_generic/**/( struct_srp_subject/**/ $subject, string $s, /*type_number */$a_value, /*type_number */$b_value, string $identifier, string $k ) : array { $HN = ($subject->toolset->decode)( ($subject->toolset->compute_hash)( ($subject->toolset->to_string)($subject->modulus) ) ); $Hg = ($subject->toolset->decode)( ($subject->toolset->compute_hash)( ($subject->toolset->to_string)($subject->base) ) ); $HI = ($subject->toolset->decode)( ($subject->toolset->compute_hash)( $identifier ) ); // H(H(N) XOR H(g), H(I), s, A, B, k) $m1 = ($subject->toolset->compute_hash)( ($subject->toolset->join)([ ($subject->toolset->to_string)(($subject->toolset->xor)($HN, $Hg)), ($subject->toolset->to_string)($HI), $s, ($subject->toolset->to_string)($a_value), ($subject->toolset->to_string)($b_value), $k, ]) ) ; return [ 'm1' => $m1, ]; } /** * @return array { * record< * m1_client:string, * > * } */ function srp_compute_m1_client/**/( struct_srp_subject/**/ $subject, string $s, /*type_number */$a_value, /*type_number */$b_value, string $identifier, string $k_client ) : array { return ['m1_client' => srp_compute_m1_generic( $subject, $s, $a_value, $b_value, $identifier, $k_client )['m1']]; } /** * @return array { * record< * m1_server:string * > * } */ function srp_compute_m1_server/**/( struct_srp_subject/**/ $subject, string $s, /*type_number */$a_value, /*type_number */$b_value, string $identifier, string $k_server ) : array { return ['m1_server' => srp_compute_m1_generic( $subject, $s, $a_value, $b_value, $identifier, $k_server )['m1']]; } /** * @return array { * record< * M2:string * > * } */ function srp_compute_m2_generic/**/( struct_srp_subject/**/ $subject, /*type_number */$a_value, string $m1, string $k ) : array { $M2 = ($subject->toolset->compute_hash)( ($subject->toolset->join)([ ($subject->toolset->to_string)($a_value), $m1, $k, ]) ); return [ 'M2' => $M2, ]; } /** * @return array { * record< * m2_client:string * > * } */ function srp_compute_m2_client/**/( struct_srp_subject/**/ $subject, /*type_number */$a_value, string $m1_client, string $k_client ) : array { return ['m2_client' => srp_compute_m2_generic( $subject, $a_value, $m1_client, $k_client )['M2']]; } /** * @return array { * record< * m2_server:string * > * } */ function srp_compute_m2_server/**/( struct_srp_subject/**/ $subject, /*type_number */$a_value, string $m1_server, string $k_server ) : array { return ['m2_server' => srp_compute_m2_generic( $subject, $a_value, $m1_server, $k_server )['M2']]; } /** * @return boolean */ function srp_verify_a/**/( struct_srp_subject/**/ $subject, /*type_number */$a_value ) : bool { if (! ($subject->toolset->equal)($a_value, ($subject->toolset->zero)())) { if (! ($subject->toolset->equal)(($subject->toolset->mod)($a_value, $subject->modulus), ($subject->toolset->zero)())) { return true; } else { return false; } } else { return false; } } /** * @return boolean */ function srp_verify_b/**/( struct_srp_subject/**/ $subject, /*type_number */$b_value ) : bool { if (! ($subject->toolset->equal)($b_value, ($subject->toolset->zero)())) { if (! ($subject->toolset->equal)(($subject->toolset->mod)($b_value, $subject->modulus), ($subject->toolset->zero)())) { return true; } else { return false; } } else { return false; } } /** * @return boolean */ function srp_verify_hab/**/( struct_srp_subject/**/ $subject, /*type_number */$a_value, /*type_number */$b_value ) : bool { $u = ($subject->toolset->decode)( ($subject->toolset->compute_hash)( ($subject->toolset->join)([ ($subject->toolset->to_string)($a_value), ($subject->toolset->to_string)($b_value), ]) ) ); if (! ($subject->toolset->equal)($u, ($subject->toolset->zero)())) { return true; } else { return false; } } /** * @return boolean */ function srp_verify_k( struct_srp_subject/**/ $subject, string $k_client, string $k_server ) : bool { if (($k_client != null) && ($k_server != null)) { if ($k_client === $k_server) { return true; } else { return false; } } else { return false; } } ?>