wp/wp-includes/sodium_compat/src/Core/Ristretto255.php
changeset 18 be944660c56a
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * Class ParagonIE_Sodium_Core_Ristretto255
       
     5  */
       
     6 class ParagonIE_Sodium_Core_Ristretto255 extends ParagonIE_Sodium_Core_Ed25519
       
     7 {
       
     8     const crypto_core_ristretto255_HASHBYTES = 64;
       
     9     const HASH_SC_L = 48;
       
    10     const CORE_H2C_SHA256 = 1;
       
    11     const CORE_H2C_SHA512 = 2;
       
    12 
       
    13     /**
       
    14      * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
       
    15      * @param int $b
       
    16      * @return ParagonIE_Sodium_Core_Curve25519_Fe
       
    17      */
       
    18     public static function fe_cneg(ParagonIE_Sodium_Core_Curve25519_Fe $f, $b)
       
    19     {
       
    20         $negf = self::fe_neg($f);
       
    21         return self::fe_cmov($f, $negf, $b);
       
    22     }
       
    23 
       
    24     /**
       
    25      * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
       
    26      * @return ParagonIE_Sodium_Core_Curve25519_Fe
       
    27      * @throws SodiumException
       
    28      */
       
    29     public static function fe_abs(ParagonIE_Sodium_Core_Curve25519_Fe $f)
       
    30     {
       
    31         return self::fe_cneg($f, self::fe_isnegative($f));
       
    32     }
       
    33 
       
    34     /**
       
    35      * Returns 0 if this field element results in all NUL bytes.
       
    36      *
       
    37      * @internal You should not use this directly from another application
       
    38      *
       
    39      * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
       
    40      * @return int
       
    41      * @throws SodiumException
       
    42      */
       
    43     public static function fe_iszero(ParagonIE_Sodium_Core_Curve25519_Fe $f)
       
    44     {
       
    45         static $zero;
       
    46         if ($zero === null) {
       
    47             $zero = str_repeat("\x00", 32);
       
    48         }
       
    49         /** @var string $zero */
       
    50         $str = self::fe_tobytes($f);
       
    51 
       
    52         $d = 0;
       
    53         for ($i = 0; $i < 32; ++$i) {
       
    54             $d |= self::chrToInt($str[$i]);
       
    55         }
       
    56         return (($d - 1) >> 31) & 1;
       
    57     }
       
    58 
       
    59 
       
    60     /**
       
    61      * @param ParagonIE_Sodium_Core_Curve25519_Fe $u
       
    62      * @param ParagonIE_Sodium_Core_Curve25519_Fe $v
       
    63      * @return array{x: ParagonIE_Sodium_Core_Curve25519_Fe, nonsquare: int}
       
    64      *
       
    65      * @throws SodiumException
       
    66      */
       
    67     public static function ristretto255_sqrt_ratio_m1(
       
    68         ParagonIE_Sodium_Core_Curve25519_Fe $u,
       
    69         ParagonIE_Sodium_Core_Curve25519_Fe $v
       
    70     ) {
       
    71         $sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1);
       
    72 
       
    73         $v3 = self::fe_mul(
       
    74             self::fe_sq($v),
       
    75             $v
       
    76         ); /* v3 = v^3 */
       
    77         $x = self::fe_mul(
       
    78             self::fe_mul(
       
    79                 self::fe_sq($v3),
       
    80                 $u
       
    81             ),
       
    82             $v
       
    83         ); /* x = uv^7 */
       
    84 
       
    85         $x = self::fe_mul(
       
    86             self::fe_mul(
       
    87                 self::fe_pow22523($x), /* x = (uv^7)^((q-5)/8) */
       
    88                 $v3
       
    89             ),
       
    90             $u
       
    91         ); /* x = uv^3(uv^7)^((q-5)/8) */
       
    92 
       
    93         $vxx = self::fe_mul(
       
    94             self::fe_sq($x),
       
    95             $v
       
    96         ); /* vx^2 */
       
    97 
       
    98         $m_root_check = self::fe_sub($vxx, $u); /* vx^2-u */
       
    99         $p_root_check = self::fe_add($vxx, $u); /* vx^2+u */
       
   100         $f_root_check = self::fe_mul($u, $sqrtm1); /* u*sqrt(-1) */
       
   101         $f_root_check = self::fe_add($vxx, $f_root_check); /* vx^2+u*sqrt(-1) */
       
   102 
       
   103         $has_m_root = self::fe_iszero($m_root_check);
       
   104         $has_p_root = self::fe_iszero($p_root_check);
       
   105         $has_f_root = self::fe_iszero($f_root_check);
       
   106 
       
   107         $x_sqrtm1 = self::fe_mul($x, $sqrtm1); /* x*sqrt(-1) */
       
   108 
       
   109         $x = self::fe_abs(
       
   110             self::fe_cmov($x, $x_sqrtm1, $has_p_root | $has_f_root)
       
   111         );
       
   112         return array(
       
   113             'x' => $x,
       
   114             'nonsquare' => $has_m_root | $has_p_root
       
   115         );
       
   116     }
       
   117 
       
   118     /**
       
   119      * @param string $s
       
   120      * @return int
       
   121      * @throws SodiumException
       
   122      */
       
   123     public static function ristretto255_point_is_canonical($s)
       
   124     {
       
   125         $c = (self::chrToInt($s[31]) & 0x7f) ^ 0x7f;
       
   126         for ($i = 30; $i > 0; --$i) {
       
   127             $c |= self::chrToInt($s[$i]) ^ 0xff;
       
   128         }
       
   129         $c = ($c - 1) >> 8;
       
   130         $d = (0xed - 1 - self::chrToInt($s[0])) >> 8;
       
   131         $e = self::chrToInt($s[31]) >> 7;
       
   132 
       
   133         return 1 - ((($c & $d) | $e | self::chrToInt($s[0])) & 1);
       
   134     }
       
   135 
       
   136     /**
       
   137      * @param string $s
       
   138      * @param bool $skipCanonicalCheck
       
   139      * @return array{h: ParagonIE_Sodium_Core_Curve25519_Ge_P3, res: int}
       
   140      * @throws SodiumException
       
   141      */
       
   142     public static function ristretto255_frombytes($s, $skipCanonicalCheck = false)
       
   143     {
       
   144         if (!$skipCanonicalCheck) {
       
   145             if (!self::ristretto255_point_is_canonical($s)) {
       
   146                 throw new SodiumException('S is not canonical');
       
   147             }
       
   148         }
       
   149 
       
   150         $s_ = self::fe_frombytes($s);
       
   151         $ss = self::fe_sq($s_); /* ss = s^2 */
       
   152 
       
   153         $u1 = self::fe_sub(self::fe_1(), $ss); /* u1 = 1-ss */
       
   154         $u1u1 = self::fe_sq($u1); /* u1u1 = u1^2 */
       
   155 
       
   156         $u2 = self::fe_add(self::fe_1(), $ss); /* u2 = 1+ss */
       
   157         $u2u2 = self::fe_sq($u2); /* u2u2 = u2^2 */
       
   158 
       
   159         $v = self::fe_mul(
       
   160             ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d),
       
   161             $u1u1
       
   162         ); /* v = d*u1^2 */
       
   163         $v = self::fe_neg($v); /* v = -d*u1^2 */
       
   164         $v = self::fe_sub($v, $u2u2); /* v = -(d*u1^2)-u2^2 */
       
   165         $v_u2u2 = self::fe_mul($v, $u2u2); /* v_u2u2 = v*u2^2 */
       
   166 
       
   167         // fe25519_1(one);
       
   168         // notsquare = ristretto255_sqrt_ratio_m1(inv_sqrt, one, v_u2u2);
       
   169         $one = self::fe_1();
       
   170         $result = self::ristretto255_sqrt_ratio_m1($one, $v_u2u2);
       
   171         $inv_sqrt = $result['x'];
       
   172         $notsquare = $result['nonsquare'];
       
   173 
       
   174         $h = new ParagonIE_Sodium_Core_Curve25519_Ge_P3();
       
   175 
       
   176         $h->X = self::fe_mul($inv_sqrt, $u2);
       
   177         $h->Y = self::fe_mul(self::fe_mul($inv_sqrt, $h->X), $v);
       
   178 
       
   179         $h->X = self::fe_mul($h->X, $s_);
       
   180         $h->X = self::fe_abs(
       
   181             self::fe_add($h->X, $h->X)
       
   182         );
       
   183         $h->Y = self::fe_mul($u1, $h->Y);
       
   184         $h->Z = self::fe_1();
       
   185         $h->T = self::fe_mul($h->X, $h->Y);
       
   186 
       
   187         $res = - ((1 - $notsquare) | self::fe_isnegative($h->T) | self::fe_iszero($h->Y));
       
   188         return array('h' => $h, 'res' => $res);
       
   189     }
       
   190 
       
   191     /**
       
   192      * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h
       
   193      * @return string
       
   194      * @throws SodiumException
       
   195      */
       
   196     public static function ristretto255_p3_tobytes(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h)
       
   197     {
       
   198         $sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1);
       
   199         $invsqrtamd = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$invsqrtamd);
       
   200 
       
   201         $u1 = self::fe_add($h->Z, $h->Y); /* u1 = Z+Y */
       
   202         $zmy = self::fe_sub($h->Z, $h->Y); /* zmy = Z-Y */
       
   203         $u1 = self::fe_mul($u1, $zmy); /* u1 = (Z+Y)*(Z-Y) */
       
   204         $u2 = self::fe_mul($h->X, $h->Y); /* u2 = X*Y */
       
   205 
       
   206         $u1_u2u2 = self::fe_mul(self::fe_sq($u2), $u1); /* u1_u2u2 = u1*u2^2 */
       
   207         $one = self::fe_1();
       
   208 
       
   209         // fe25519_1(one);
       
   210         // (void) ristretto255_sqrt_ratio_m1(inv_sqrt, one, u1_u2u2);
       
   211         $result = self::ristretto255_sqrt_ratio_m1($one, $u1_u2u2);
       
   212         $inv_sqrt = $result['x'];
       
   213 
       
   214         $den1 = self::fe_mul($inv_sqrt, $u1); /* den1 = inv_sqrt*u1 */
       
   215         $den2 = self::fe_mul($inv_sqrt, $u2); /* den2 = inv_sqrt*u2 */
       
   216         $z_inv = self::fe_mul($h->T, self::fe_mul($den1, $den2)); /* z_inv = den1*den2*T */
       
   217 
       
   218         $ix = self::fe_mul($h->X, $sqrtm1); /* ix = X*sqrt(-1) */
       
   219         $iy = self::fe_mul($h->Y, $sqrtm1); /* iy = Y*sqrt(-1) */
       
   220         $eden = self::fe_mul($den1, $invsqrtamd);
       
   221 
       
   222         $t_z_inv =  self::fe_mul($h->T, $z_inv); /* t_z_inv = T*z_inv */
       
   223         $rotate = self::fe_isnegative($t_z_inv);
       
   224 
       
   225         $x_ = self::fe_copy($h->X);
       
   226         $y_ = self::fe_copy($h->Y);
       
   227         $den_inv = self::fe_copy($den2);
       
   228 
       
   229         $x_ = self::fe_cmov($x_, $iy, $rotate);
       
   230         $y_ = self::fe_cmov($y_, $ix, $rotate);
       
   231         $den_inv = self::fe_cmov($den_inv, $eden, $rotate);
       
   232 
       
   233         $x_z_inv = self::fe_mul($x_, $z_inv);
       
   234         $y_ = self::fe_cneg($y_, self::fe_isnegative($x_z_inv));
       
   235 
       
   236 
       
   237         // fe25519_sub(s_, h->Z, y_);
       
   238         // fe25519_mul(s_, den_inv, s_);
       
   239         // fe25519_abs(s_, s_);
       
   240         // fe25519_tobytes(s, s_);
       
   241         return self::fe_tobytes(
       
   242             self::fe_abs(
       
   243                 self::fe_mul(
       
   244                     $den_inv,
       
   245                     self::fe_sub($h->Z, $y_)
       
   246                 )
       
   247             )
       
   248         );
       
   249     }
       
   250 
       
   251     /**
       
   252      * @param ParagonIE_Sodium_Core_Curve25519_Fe $t
       
   253      * @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
       
   254      *
       
   255      * @throws SodiumException
       
   256      */
       
   257     public static function ristretto255_elligator(ParagonIE_Sodium_Core_Curve25519_Fe $t)
       
   258     {
       
   259         $sqrtm1   = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1);
       
   260         $onemsqd  = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$onemsqd);
       
   261         $d        = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d);
       
   262         $sqdmone  = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqdmone);
       
   263         $sqrtadm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtadm1);
       
   264 
       
   265         $one = self::fe_1();
       
   266         $r   = self::fe_mul($sqrtm1, self::fe_sq($t));         /* r = sqrt(-1)*t^2 */
       
   267         $u   = self::fe_mul(self::fe_add($r, $one), $onemsqd); /* u = (r+1)*(1-d^2) */
       
   268         $c   = self::fe_neg(self::fe_1());                     /* c = -1 */
       
   269         $rpd = self::fe_add($r, $d);                           /* rpd = r+d */
       
   270 
       
   271         $v = self::fe_mul(
       
   272             self::fe_sub(
       
   273                 $c,
       
   274                 self::fe_mul($r, $d)
       
   275             ),
       
   276             $rpd
       
   277         ); /* v = (c-r*d)*(r+d) */
       
   278 
       
   279         $result = self::ristretto255_sqrt_ratio_m1($u, $v);
       
   280         $s = $result['x'];
       
   281         $wasnt_square = 1 - $result['nonsquare'];
       
   282 
       
   283         $s_prime = self::fe_neg(
       
   284             self::fe_abs(
       
   285                 self::fe_mul($s, $t)
       
   286             )
       
   287         ); /* s_prime = -|s*t| */
       
   288         $s = self::fe_cmov($s, $s_prime, $wasnt_square);
       
   289         $c = self::fe_cmov($c, $r, $wasnt_square);
       
   290 
       
   291         // fe25519_sub(n, r, one);            /* n = r-1 */
       
   292         // fe25519_mul(n, n, c);              /* n = c*(r-1) */
       
   293         // fe25519_mul(n, n, ed25519_sqdmone); /* n = c*(r-1)*(d-1)^2 */
       
   294         // fe25519_sub(n, n, v);              /* n =  c*(r-1)*(d-1)^2-v */
       
   295         $n = self::fe_sub(
       
   296             self::fe_mul(
       
   297                 self::fe_mul(
       
   298                     self::fe_sub($r, $one),
       
   299                     $c
       
   300                 ),
       
   301                 $sqdmone
       
   302             ),
       
   303             $v
       
   304         ); /* n =  c*(r-1)*(d-1)^2-v */
       
   305 
       
   306         $w0 = self::fe_mul(
       
   307             self::fe_add($s, $s),
       
   308             $v
       
   309         ); /* w0 = 2s*v */
       
   310 
       
   311         $w1 = self::fe_mul($n, $sqrtadm1); /* w1 = n*sqrt(ad-1) */
       
   312         $ss = self::fe_sq($s); /* ss = s^2 */
       
   313         $w2 = self::fe_sub($one, $ss); /* w2 = 1-s^2 */
       
   314         $w3 = self::fe_add($one, $ss); /* w3 = 1+s^2 */
       
   315 
       
   316         return new ParagonIE_Sodium_Core_Curve25519_Ge_P3(
       
   317             self::fe_mul($w0, $w3),
       
   318             self::fe_mul($w2, $w1),
       
   319             self::fe_mul($w1, $w3),
       
   320             self::fe_mul($w0, $w2)
       
   321         );
       
   322     }
       
   323 
       
   324     /**
       
   325      * @param string $h
       
   326      * @return string
       
   327      * @throws SodiumException
       
   328      */
       
   329     public static function ristretto255_from_hash($h)
       
   330     {
       
   331         if (self::strlen($h) !== 64) {
       
   332             throw new SodiumException('Hash must be 64 bytes');
       
   333         }
       
   334         //fe25519_frombytes(r0, h);
       
   335         //fe25519_frombytes(r1, h + 32);
       
   336         $r0 = self::fe_frombytes(self::substr($h, 0, 32));
       
   337         $r1 = self::fe_frombytes(self::substr($h, 32, 32));
       
   338 
       
   339         //ristretto255_elligator(&p0, r0);
       
   340         //ristretto255_elligator(&p1, r1);
       
   341         $p0 = self::ristretto255_elligator($r0);
       
   342         $p1 = self::ristretto255_elligator($r1);
       
   343 
       
   344         //ge25519_p3_to_cached(&p1_cached, &p1);
       
   345         //ge25519_add_cached(&p_p1p1, &p0, &p1_cached);
       
   346         $p_p1p1 = self::ge_add(
       
   347             $p0,
       
   348             self::ge_p3_to_cached($p1)
       
   349         );
       
   350 
       
   351         //ge25519_p1p1_to_p3(&p, &p_p1p1);
       
   352         //ristretto255_p3_tobytes(s, &p);
       
   353         return self::ristretto255_p3_tobytes(
       
   354             self::ge_p1p1_to_p3($p_p1p1)
       
   355         );
       
   356     }
       
   357 
       
   358     /**
       
   359      * @param string $p
       
   360      * @return int
       
   361      * @throws SodiumException
       
   362      */
       
   363     public static function is_valid_point($p)
       
   364     {
       
   365         $result = self::ristretto255_frombytes($p);
       
   366         if ($result['res'] !== 0) {
       
   367             return 0;
       
   368         }
       
   369         return 1;
       
   370     }
       
   371 
       
   372     /**
       
   373      * @param string $p
       
   374      * @param string $q
       
   375      * @return string
       
   376      * @throws SodiumException
       
   377      */
       
   378     public static function ristretto255_add($p, $q)
       
   379     {
       
   380         $p_res = self::ristretto255_frombytes($p);
       
   381         $q_res = self::ristretto255_frombytes($q);
       
   382         if ($p_res['res'] !== 0 || $q_res['res'] !== 0) {
       
   383             throw new SodiumException('Could not add points');
       
   384         }
       
   385         $p_p3 = $p_res['h'];
       
   386         $q_p3 = $q_res['h'];
       
   387         $q_cached = self::ge_p3_to_cached($q_p3);
       
   388         $r_p1p1 = self::ge_add($p_p3, $q_cached);
       
   389         $r_p3 = self::ge_p1p1_to_p3($r_p1p1);
       
   390         return self::ristretto255_p3_tobytes($r_p3);
       
   391     }
       
   392 
       
   393     /**
       
   394      * @param string $p
       
   395      * @param string $q
       
   396      * @return string
       
   397      * @throws SodiumException
       
   398      */
       
   399     public static function ristretto255_sub($p, $q)
       
   400     {
       
   401         $p_res = self::ristretto255_frombytes($p);
       
   402         $q_res = self::ristretto255_frombytes($q);
       
   403         if ($p_res['res'] !== 0 || $q_res['res'] !== 0) {
       
   404             throw new SodiumException('Could not add points');
       
   405         }
       
   406         $p_p3 = $p_res['h'];
       
   407         $q_p3 = $q_res['h'];
       
   408         $q_cached = self::ge_p3_to_cached($q_p3);
       
   409         $r_p1p1 = self::ge_sub($p_p3, $q_cached);
       
   410         $r_p3 = self::ge_p1p1_to_p3($r_p1p1);
       
   411         return self::ristretto255_p3_tobytes($r_p3);
       
   412     }
       
   413 
       
   414 
       
   415     /**
       
   416      * @param int $hLen
       
   417      * @param ?string $ctx
       
   418      * @param string $msg
       
   419      * @return string
       
   420      * @throws SodiumException
       
   421      * @psalm-suppress PossiblyInvalidArgument hash API
       
   422      */
       
   423     protected static function h2c_string_to_hash_sha256($hLen, $ctx, $msg)
       
   424     {
       
   425         $h = array_fill(0, $hLen, 0);
       
   426         $ctx_len = !is_null($ctx) ? self::strlen($ctx) : 0;
       
   427         if ($hLen > 0xff) {
       
   428             throw new SodiumException('Hash must be less than 256 bytes');
       
   429         }
       
   430 
       
   431         if ($ctx_len > 0xff) {
       
   432             $st = hash_init('sha256');
       
   433             self::hash_update($st, "H2C-OVERSIZE-DST-");
       
   434             self::hash_update($st, $ctx);
       
   435             $ctx = hash_final($st, true);
       
   436             $ctx_len = 32;
       
   437         }
       
   438         $t = array(0, $hLen, 0);
       
   439         $ux = str_repeat("\0", 64);
       
   440         $st = hash_init('sha256');
       
   441         self::hash_update($st, $ux);
       
   442         self::hash_update($st, $msg);
       
   443         self::hash_update($st, self::intArrayToString($t));
       
   444         self::hash_update($st, $ctx);
       
   445         self::hash_update($st, self::intToChr($ctx_len));
       
   446         $u0 = hash_final($st, true);
       
   447 
       
   448         for ($i = 0; $i < $hLen; $i += 64) {
       
   449             $ux = self::xorStrings($ux, $u0);
       
   450             ++$t[2];
       
   451             $st = hash_init('sha256');
       
   452             self::hash_update($st, $ux);
       
   453             self::hash_update($st, self::intToChr($t[2]));
       
   454             self::hash_update($st, $ctx);
       
   455             self::hash_update($st, self::intToChr($ctx_len));
       
   456             $ux = hash_final($st, true);
       
   457             $amount = min($hLen - $i, 64);
       
   458             for ($j = 0; $j < $amount; ++$j) {
       
   459                 $h[$i + $j] = self::chrToInt($ux[$i]);
       
   460             }
       
   461         }
       
   462         return self::intArrayToString(array_slice($h, 0, $hLen));
       
   463     }
       
   464 
       
   465     /**
       
   466      * @param int $hLen
       
   467      * @param ?string $ctx
       
   468      * @param string $msg
       
   469      * @return string
       
   470      * @throws SodiumException
       
   471      * @psalm-suppress PossiblyInvalidArgument hash API
       
   472      */
       
   473     protected static function h2c_string_to_hash_sha512($hLen, $ctx, $msg)
       
   474     {
       
   475         $h = array_fill(0, $hLen, 0);
       
   476         $ctx_len = !is_null($ctx) ? self::strlen($ctx) : 0;
       
   477         if ($hLen > 0xff) {
       
   478             throw new SodiumException('Hash must be less than 256 bytes');
       
   479         }
       
   480 
       
   481         if ($ctx_len > 0xff) {
       
   482             $st = hash_init('sha256');
       
   483             self::hash_update($st, "H2C-OVERSIZE-DST-");
       
   484             self::hash_update($st, $ctx);
       
   485             $ctx = hash_final($st, true);
       
   486             $ctx_len = 32;
       
   487         }
       
   488         $t = array(0, $hLen, 0);
       
   489         $ux = str_repeat("\0", 128);
       
   490         $st = hash_init('sha512');
       
   491         self::hash_update($st, $ux);
       
   492         self::hash_update($st, $msg);
       
   493         self::hash_update($st, self::intArrayToString($t));
       
   494         self::hash_update($st, $ctx);
       
   495         self::hash_update($st, self::intToChr($ctx_len));
       
   496         $u0 = hash_final($st, true);
       
   497 
       
   498         for ($i = 0; $i < $hLen; $i += 128) {
       
   499             $ux = self::xorStrings($ux, $u0);
       
   500             ++$t[2];
       
   501             $st = hash_init('sha512');
       
   502             self::hash_update($st, $ux);
       
   503             self::hash_update($st, self::intToChr($t[2]));
       
   504             self::hash_update($st, $ctx);
       
   505             self::hash_update($st, self::intToChr($ctx_len));
       
   506             $ux = hash_final($st, true);
       
   507             $amount = min($hLen - $i, 128);
       
   508             for ($j = 0; $j < $amount; ++$j) {
       
   509                 $h[$i + $j] = self::chrToInt($ux[$i]);
       
   510             }
       
   511         }
       
   512         return self::intArrayToString(array_slice($h, 0, $hLen));
       
   513     }
       
   514 
       
   515     /**
       
   516      * @param int $hLen
       
   517      * @param ?string $ctx
       
   518      * @param string $msg
       
   519      * @param int $hash_alg
       
   520      * @return string
       
   521      * @throws SodiumException
       
   522      */
       
   523     public static function h2c_string_to_hash($hLen, $ctx, $msg, $hash_alg)
       
   524     {
       
   525         switch ($hash_alg) {
       
   526             case self::CORE_H2C_SHA256:
       
   527                 return self::h2c_string_to_hash_sha256($hLen, $ctx, $msg);
       
   528             case self::CORE_H2C_SHA512:
       
   529                 return self::h2c_string_to_hash_sha512($hLen, $ctx, $msg);
       
   530             default:
       
   531                 throw new SodiumException('Invalid H2C hash algorithm');
       
   532         }
       
   533     }
       
   534 
       
   535     /**
       
   536      * @param ?string $ctx
       
   537      * @param string $msg
       
   538      * @param int $hash_alg
       
   539      * @return string
       
   540      * @throws SodiumException
       
   541      */
       
   542     protected static function _string_to_element($ctx, $msg, $hash_alg)
       
   543     {
       
   544         return self::ristretto255_from_hash(
       
   545             self::h2c_string_to_hash(self::crypto_core_ristretto255_HASHBYTES, $ctx, $msg, $hash_alg)
       
   546         );
       
   547     }
       
   548 
       
   549     /**
       
   550      * @return string
       
   551      * @throws SodiumException
       
   552      * @throws Exception
       
   553      */
       
   554     public static function ristretto255_random()
       
   555     {
       
   556         return self::ristretto255_from_hash(
       
   557             ParagonIE_Sodium_Compat::randombytes_buf(self::crypto_core_ristretto255_HASHBYTES)
       
   558         );
       
   559     }
       
   560 
       
   561     /**
       
   562      * @return string
       
   563      * @throws SodiumException
       
   564      */
       
   565     public static function ristretto255_scalar_random()
       
   566     {
       
   567         return self::scalar_random();
       
   568     }
       
   569 
       
   570     /**
       
   571      * @param string $s
       
   572      * @return string
       
   573      * @throws SodiumException
       
   574      */
       
   575     public static function ristretto255_scalar_complement($s)
       
   576     {
       
   577         return self::scalar_complement($s);
       
   578     }
       
   579 
       
   580 
       
   581     /**
       
   582      * @param string $s
       
   583      * @return string
       
   584      */
       
   585     public static function ristretto255_scalar_invert($s)
       
   586     {
       
   587         return self::sc25519_invert($s);
       
   588     }
       
   589 
       
   590     /**
       
   591      * @param string $s
       
   592      * @return string
       
   593      * @throws SodiumException
       
   594      */
       
   595     public static function ristretto255_scalar_negate($s)
       
   596     {
       
   597         return self::scalar_negate($s);
       
   598     }
       
   599 
       
   600     /**
       
   601      * @param string $x
       
   602      * @param string $y
       
   603      * @return string
       
   604      */
       
   605     public static function ristretto255_scalar_add($x, $y)
       
   606     {
       
   607         return self::scalar_add($x, $y);
       
   608     }
       
   609 
       
   610     /**
       
   611      * @param string $x
       
   612      * @param string $y
       
   613      * @return string
       
   614      */
       
   615     public static function ristretto255_scalar_sub($x, $y)
       
   616     {
       
   617         return self::scalar_sub($x, $y);
       
   618     }
       
   619 
       
   620     /**
       
   621      * @param string $x
       
   622      * @param string $y
       
   623      * @return string
       
   624      */
       
   625     public static function ristretto255_scalar_mul($x, $y)
       
   626     {
       
   627         return self::sc25519_mul($x, $y);
       
   628     }
       
   629 
       
   630     /**
       
   631      * @param string $ctx
       
   632      * @param string $msg
       
   633      * @param int $hash_alg
       
   634      * @return string
       
   635      * @throws SodiumException
       
   636      */
       
   637     public static function ristretto255_scalar_from_string($ctx, $msg, $hash_alg)
       
   638     {
       
   639         $h = array_fill(0, 64, 0);
       
   640         $h_be = self::stringToIntArray(
       
   641             self::h2c_string_to_hash(
       
   642                 self::HASH_SC_L, $ctx, $msg, $hash_alg
       
   643             )
       
   644         );
       
   645 
       
   646         for ($i = 0; $i < self::HASH_SC_L; ++$i) {
       
   647             $h[$i] = $h_be[self::HASH_SC_L - 1 - $i];
       
   648         }
       
   649         return self::ristretto255_scalar_reduce(self::intArrayToString($h));
       
   650     }
       
   651 
       
   652     /**
       
   653      * @param string $s
       
   654      * @return string
       
   655      */
       
   656     public static function ristretto255_scalar_reduce($s)
       
   657     {
       
   658         return self::sc_reduce($s);
       
   659     }
       
   660 
       
   661     /**
       
   662      * @param string $n
       
   663      * @param string $p
       
   664      * @return string
       
   665      * @throws SodiumException
       
   666      */
       
   667     public static function scalarmult_ristretto255($n, $p)
       
   668     {
       
   669         if (self::strlen($n) !== 32) {
       
   670             throw new SodiumException('Scalar must be 32 bytes, ' . self::strlen($p) . ' given.');
       
   671         }
       
   672         if (self::strlen($p) !== 32) {
       
   673             throw new SodiumException('Point must be 32 bytes, ' . self::strlen($p) . ' given.');
       
   674         }
       
   675         $result = self::ristretto255_frombytes($p);
       
   676         if ($result['res'] !== 0) {
       
   677             throw new SodiumException('Could not multiply points');
       
   678         }
       
   679         $P = $result['h'];
       
   680 
       
   681         $t = self::stringToIntArray($n);
       
   682         $t[31] &= 0x7f;
       
   683         $Q = self::ge_scalarmult(self::intArrayToString($t), $P);
       
   684         $q = self::ristretto255_p3_tobytes($Q);
       
   685         if (ParagonIE_Sodium_Compat::is_zero($q)) {
       
   686             throw new SodiumException('An unknown error has occurred');
       
   687         }
       
   688         return $q;
       
   689     }
       
   690 
       
   691     /**
       
   692      * @param string $n
       
   693      * @return string
       
   694      * @throws SodiumException
       
   695      */
       
   696     public static function scalarmult_ristretto255_base($n)
       
   697     {
       
   698         $t = self::stringToIntArray($n);
       
   699         $t[31] &= 0x7f;
       
   700         $Q = self::ge_scalarmult_base(self::intArrayToString($t));
       
   701         $q = self::ristretto255_p3_tobytes($Q);
       
   702         if (ParagonIE_Sodium_Compat::is_zero($q)) {
       
   703             throw new SodiumException('An unknown error has occurred');
       
   704         }
       
   705         return $q;
       
   706     }
       
   707 }