ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

Ristretto255.php (21884B)


      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 }