rosavox/lib/alveolata/auth/implementation-srp/core.php
2025-05-23 07:33:29 +00:00

880 lines
18 KiB
PHP

<?php
declare(strict_types = 1);
namespace alveolata\auth;
// require_once('vendor/autoload.php');
require_once(DIR_ALVEOLATA . '/math/functions.php');
/**
* @template type_number
* @author Christian Fraß <frass@greenscale.de>
*/
class struct_srp_toolset/*<type_number>*/
{
/**
* @var function<tuple<type_number>,string>
*/
public $encode;
/**
* @var function<tuple<string>,type_number>
*/
public $decode;
/**
* @var function<tuple<int,string>,type_number>
*/
public $make;
/**
* @var function<tuple<>,type_number>
*/
public $zero;
/**
* @todo check if really needed
* @var function<tuple<>,type_number>
*/
public $one;
/**
* @var function<tuple<type_number>,string>
*/
public $to_string;
/**
* @var function<tuple<type_number,type_number>,boolean>
*/
public $equal;
/**
* @var function<tuple<type_number,type_number>,type_number>
*/
public $add;
/**
* @var function<tuple<type_number,type_number>,type_number>
*/
public $subtract;
/**
* @var function<tuple<type_number,type_number>,type_number>
*/
public $multiply;
/**
* @var function<tuple<type_number,type_number>,type_number>
*/
public $xor;
/**
* @var function<tuple<type_number,type_number>,type_number>
*/
public $mod;
/**
* @var function<tuple<type_number,type_number,type_number>,type_number>
*/
public $modpow;
/**
* @var function<list<string>,string>
*/
public $join;
/**
* @var function<>
*/
public $compute_hash;
/**
* @var function<tuple<string,string>,string>
*/
public $compute_password_hash;
/**
* @var function<tuple<>,type_number>
*/
public $compute_random;
/**
* @param array $tools {
* record<
* encode:function<tuple<type_number>,string>,
* decode:function<tuple<string>,type_number>,
* make:function<tuple<int,string>,type_number>,
* zero:function<tuple<>,type_number>,
* one:function<tuple<>,type_number>,
* to_string:function<tuple<type_number>,string>,
* equal:function<tuple<type_number,type_number>,boolean>,
* add:function<tuple<type_number,type_number>,type_number>,
* subtract:function<tuple<type_number,type_number>,type_number>,
* multiply:function<tuple<type_number,type_number>,type_number>,
* xor:function<tuple<type_number,type_number>,type_number>,
* mod:function<tuple<type_number,type_number>,type_number>,
* modpow:function<tuple<type_number,type_number,type_number>,type_number>,
* join:function<list<string>,string>,
* compute_hash:function<>,
* compute_password_hash:function<tuple<string,string>,string>,
* compute_random:function<tuple<>,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ß <frass@greenscale.de>
*/
function _srp_toolset_simple(
) : struct_srp_toolset/*<int>*/
{
return (new struct_srp_toolset/*<int>*/(
[
'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ß <frass@greenscale.de>
*/
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ß <frass@greenscale.de>
*/
class struct_srp_subject/*<type_number>*/
{
/**
* @var struct_srp_toolset<type_number>
*/
public $toolset;
/**
* @var type_number
*/
public $base;
/**
* @var type_number
*/
public $modulus;
/**
*/
public function __construct(
struct_srp_toolset/*<type_number>*/ $toolset,
/*type_number */$base,
/*type_number */$modulus
)
{
$this->toolset = $toolset;
$this->base = $base;
$this->modulus = $modulus;
}
}
/**
*/
function srp_subject_make/*<type_number>*/(
struct_srp_toolset/*<type_number>*/ $toolset,
/*type_number */$base,
/*type_number */$modulus
)
{
return (
new struct_srp_subject(
$toolset,
$base,
$modulus
)
);
}
/**
* @return struct_srp_subject<int>
*/
function srp_subject_test(
) : struct_srp_subject/*<int>*/
{
$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/*<int>*/(
$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ß <frass@greenscale.de>
*/
class _state
{
public static $subject_default = null;
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
function srp_set_default_subject(
\Closure $factory
) : void
{
_state::$subject_default = $factory;
}
/**
* @return struct_srp_subject<any>
* @author Christian Fraß <frass@greenscale.de>
*/
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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $subject,
/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/(
struct_srp_subject/*<type_number>*/ $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/*<type_number>*/ $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;
}
}
?>