[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/sodium_compat/src/Core/ -> Curve25519.php (source)

   1  <?php
   2  
   3  if (class_exists('ParagonIE_Sodium_Core_Curve25519', false)) {
   4      return;
   5  }
   6  
   7  /**
   8   * Class ParagonIE_Sodium_Core_Curve25519
   9   *
  10   * Implements Curve25519 core functions
  11   *
  12   * Based on the ref10 curve25519 code provided by libsodium
  13   *
  14   * @ref https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_core/curve25519/ref10/curve25519_ref10.c
  15   */
  16  abstract class ParagonIE_Sodium_Core_Curve25519 extends ParagonIE_Sodium_Core_Curve25519_H
  17  {
  18      /**
  19       * Get a field element of size 10 with a value of 0
  20       *
  21       * @internal You should not use this directly from another application
  22       *
  23       * @return ParagonIE_Sodium_Core_Curve25519_Fe
  24       */
  25      public static function fe_0()
  26      {
  27          return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
  28              array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
  29          );
  30      }
  31  
  32      /**
  33       * Get a field element of size 10 with a value of 1
  34       *
  35       * @internal You should not use this directly from another application
  36       *
  37       * @return ParagonIE_Sodium_Core_Curve25519_Fe
  38       */
  39      public static function fe_1()
  40      {
  41          return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
  42              array(1, 0, 0, 0, 0, 0, 0, 0, 0, 0)
  43          );
  44      }
  45  
  46      /**
  47       * Add two field elements.
  48       *
  49       * @internal You should not use this directly from another application
  50       *
  51       * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
  52       * @param ParagonIE_Sodium_Core_Curve25519_Fe $g
  53       * @return ParagonIE_Sodium_Core_Curve25519_Fe
  54       * @psalm-suppress MixedAssignment
  55       * @psalm-suppress MixedOperand
  56       */
  57      public static function fe_add(
  58          ParagonIE_Sodium_Core_Curve25519_Fe $f,
  59          ParagonIE_Sodium_Core_Curve25519_Fe $g
  60      ) {
  61          /** @var array<int, int> $arr */
  62          $arr = array();
  63          for ($i = 0; $i < 10; ++$i) {
  64              $arr[$i] = (int) ($f[$i] + $g[$i]);
  65          }
  66          return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($arr);
  67      }
  68  
  69      /**
  70       * Constant-time conditional move.
  71       *
  72       * @internal You should not use this directly from another application
  73       *
  74       * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
  75       * @param ParagonIE_Sodium_Core_Curve25519_Fe $g
  76       * @param int $b
  77       * @return ParagonIE_Sodium_Core_Curve25519_Fe
  78       * @psalm-suppress MixedAssignment
  79       */
  80      public static function fe_cmov(
  81          ParagonIE_Sodium_Core_Curve25519_Fe $f,
  82          ParagonIE_Sodium_Core_Curve25519_Fe $g,
  83          $b = 0
  84      ) {
  85          /** @var array<int, int> $h */
  86          $h = array();
  87          $b *= -1;
  88          for ($i = 0; $i < 10; ++$i) {
  89              $x = (($f[$i] ^ $g[$i]) & $b);
  90              $h[$i] = ($f[$i]) ^ $x;
  91          }
  92          return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($h);
  93      }
  94  
  95      /**
  96       * Create a copy of a field element.
  97       *
  98       * @internal You should not use this directly from another application
  99       *
 100       * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
 101       * @return ParagonIE_Sodium_Core_Curve25519_Fe
 102       */
 103      public static function fe_copy(ParagonIE_Sodium_Core_Curve25519_Fe $f)
 104      {
 105          $h = clone $f;
 106          return $h;
 107      }
 108  
 109      /**
 110       * Give: 32-byte string.
 111       * Receive: A field element object to use for internal calculations.
 112       *
 113       * @internal You should not use this directly from another application
 114       *
 115       * @param string $s
 116       * @return ParagonIE_Sodium_Core_Curve25519_Fe
 117       * @throws RangeException
 118       * @throws TypeError
 119       */
 120      public static function fe_frombytes($s)
 121      {
 122          if (self::strlen($s) !== 32) {
 123              throw new RangeException('Expected a 32-byte string.');
 124          }
 125          $h0 = self::load_4($s);
 126          $h1 = self::load_3(self::substr($s, 4, 3)) << 6;
 127          $h2 = self::load_3(self::substr($s, 7, 3)) << 5;
 128          $h3 = self::load_3(self::substr($s, 10, 3)) << 3;
 129          $h4 = self::load_3(self::substr($s, 13, 3)) << 2;
 130          $h5 = self::load_4(self::substr($s, 16, 4));
 131          $h6 = self::load_3(self::substr($s, 20, 3)) << 7;
 132          $h7 = self::load_3(self::substr($s, 23, 3)) << 5;
 133          $h8 = self::load_3(self::substr($s, 26, 3)) << 4;
 134          $h9 = (self::load_3(self::substr($s, 29, 3)) & 8388607) << 2;
 135  
 136          $carry9 = ($h9 + (1 << 24)) >> 25;
 137          $h0 += self::mul($carry9, 19, 5);
 138          $h9 -= $carry9 << 25;
 139          $carry1 = ($h1 + (1 << 24)) >> 25;
 140          $h2 += $carry1;
 141          $h1 -= $carry1 << 25;
 142          $carry3 = ($h3 + (1 << 24)) >> 25;
 143          $h4 += $carry3;
 144          $h3 -= $carry3 << 25;
 145          $carry5 = ($h5 + (1 << 24)) >> 25;
 146          $h6 += $carry5;
 147          $h5 -= $carry5 << 25;
 148          $carry7 = ($h7 + (1 << 24)) >> 25;
 149          $h8 += $carry7;
 150          $h7 -= $carry7 << 25;
 151  
 152          $carry0 = ($h0 + (1 << 25)) >> 26;
 153          $h1 += $carry0;
 154          $h0 -= $carry0 << 26;
 155          $carry2 = ($h2 + (1 << 25)) >> 26;
 156          $h3 += $carry2;
 157          $h2 -= $carry2 << 26;
 158          $carry4 = ($h4 + (1 << 25)) >> 26;
 159          $h5 += $carry4;
 160          $h4 -= $carry4 << 26;
 161          $carry6 = ($h6 + (1 << 25)) >> 26;
 162          $h7 += $carry6;
 163          $h6 -= $carry6 << 26;
 164          $carry8 = ($h8 + (1 << 25)) >> 26;
 165          $h9 += $carry8;
 166          $h8 -= $carry8 << 26;
 167  
 168          return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
 169              array(
 170                  (int) $h0,
 171                  (int) $h1,
 172                  (int) $h2,
 173                  (int) $h3,
 174                  (int) $h4,
 175                  (int) $h5,
 176                  (int) $h6,
 177                  (int) $h7,
 178                  (int) $h8,
 179                  (int) $h9
 180              )
 181          );
 182      }
 183  
 184      /**
 185       * Convert a field element to a byte string.
 186       *
 187       * @internal You should not use this directly from another application
 188       *
 189       * @param ParagonIE_Sodium_Core_Curve25519_Fe $h
 190       * @return string
 191       */
 192      public static function fe_tobytes(ParagonIE_Sodium_Core_Curve25519_Fe $h)
 193      {
 194          $h0 = (int) $h[0];
 195          $h1 = (int) $h[1];
 196          $h2 = (int) $h[2];
 197          $h3 = (int) $h[3];
 198          $h4 = (int) $h[4];
 199          $h5 = (int) $h[5];
 200          $h6 = (int) $h[6];
 201          $h7 = (int) $h[7];
 202          $h8 = (int) $h[8];
 203          $h9 = (int) $h[9];
 204  
 205          $q = (self::mul($h9, 19, 5) + (1 << 24)) >> 25;
 206          $q = ($h0 + $q) >> 26;
 207          $q = ($h1 + $q) >> 25;
 208          $q = ($h2 + $q) >> 26;
 209          $q = ($h3 + $q) >> 25;
 210          $q = ($h4 + $q) >> 26;
 211          $q = ($h5 + $q) >> 25;
 212          $q = ($h6 + $q) >> 26;
 213          $q = ($h7 + $q) >> 25;
 214          $q = ($h8 + $q) >> 26;
 215          $q = ($h9 + $q) >> 25;
 216  
 217          $h0 += self::mul($q, 19, 5);
 218  
 219          $carry0 = $h0 >> 26;
 220          $h1 += $carry0;
 221          $h0 -= $carry0 << 26;
 222          $carry1 = $h1 >> 25;
 223          $h2 += $carry1;
 224          $h1 -= $carry1 << 25;
 225          $carry2 = $h2 >> 26;
 226          $h3 += $carry2;
 227          $h2 -= $carry2 << 26;
 228          $carry3 = $h3 >> 25;
 229          $h4 += $carry3;
 230          $h3 -= $carry3 << 25;
 231          $carry4 = $h4 >> 26;
 232          $h5 += $carry4;
 233          $h4 -= $carry4 << 26;
 234          $carry5 = $h5 >> 25;
 235          $h6 += $carry5;
 236          $h5 -= $carry5 << 25;
 237          $carry6 = $h6 >> 26;
 238          $h7 += $carry6;
 239          $h6 -= $carry6 << 26;
 240          $carry7 = $h7 >> 25;
 241          $h8 += $carry7;
 242          $h7 -= $carry7 << 25;
 243          $carry8 = $h8 >> 26;
 244          $h9 += $carry8;
 245          $h8 -= $carry8 << 26;
 246          $carry9 = $h9 >> 25;
 247          $h9 -= $carry9 << 25;
 248  
 249          /**
 250           * @var array<int, int>
 251           */
 252          $s = array(
 253              (int) (($h0 >> 0) & 0xff),
 254              (int) (($h0 >> 8) & 0xff),
 255              (int) (($h0 >> 16) & 0xff),
 256              (int) ((($h0 >> 24) | ($h1 << 2)) & 0xff),
 257              (int) (($h1 >> 6) & 0xff),
 258              (int) (($h1 >> 14) & 0xff),
 259              (int) ((($h1 >> 22) | ($h2 << 3)) & 0xff),
 260              (int) (($h2 >> 5) & 0xff),
 261              (int) (($h2 >> 13) & 0xff),
 262              (int) ((($h2 >> 21) | ($h3 << 5)) & 0xff),
 263              (int) (($h3 >> 3) & 0xff),
 264              (int) (($h3 >> 11) & 0xff),
 265              (int) ((($h3 >> 19) | ($h4 << 6)) & 0xff),
 266              (int) (($h4 >> 2) & 0xff),
 267              (int) (($h4 >> 10) & 0xff),
 268              (int) (($h4 >> 18) & 0xff),
 269              (int) (($h5 >> 0) & 0xff),
 270              (int) (($h5 >> 8) & 0xff),
 271              (int) (($h5 >> 16) & 0xff),
 272              (int) ((($h5 >> 24) | ($h6 << 1)) & 0xff),
 273              (int) (($h6 >> 7) & 0xff),
 274              (int) (($h6 >> 15) & 0xff),
 275              (int) ((($h6 >> 23) | ($h7 << 3)) & 0xff),
 276              (int) (($h7 >> 5) & 0xff),
 277              (int) (($h7 >> 13) & 0xff),
 278              (int) ((($h7 >> 21) | ($h8 << 4)) & 0xff),
 279              (int) (($h8 >> 4) & 0xff),
 280              (int) (($h8 >> 12) & 0xff),
 281              (int) ((($h8 >> 20) | ($h9 << 6)) & 0xff),
 282              (int) (($h9 >> 2) & 0xff),
 283              (int) (($h9 >> 10) & 0xff),
 284              (int) (($h9 >> 18) & 0xff)
 285          );
 286          return self::intArrayToString($s);
 287      }
 288  
 289      /**
 290       * Is a field element negative? (1 = yes, 0 = no. Used in calculations.)
 291       *
 292       * @internal You should not use this directly from another application
 293       *
 294       * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
 295       * @return int
 296       * @throws SodiumException
 297       * @throws TypeError
 298       */
 299      public static function fe_isnegative(ParagonIE_Sodium_Core_Curve25519_Fe $f)
 300      {
 301          $str = self::fe_tobytes($f);
 302          return (int) (self::chrToInt($str[0]) & 1);
 303      }
 304  
 305      /**
 306       * Returns 0 if this field element results in all NUL bytes.
 307       *
 308       * @internal You should not use this directly from another application
 309       *
 310       * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
 311       * @return bool
 312       * @throws SodiumException
 313       * @throws TypeError
 314       */
 315      public static function fe_isnonzero(ParagonIE_Sodium_Core_Curve25519_Fe $f)
 316      {
 317          static $zero;
 318          if ($zero === null) {
 319              $zero = str_repeat("\x00", 32);
 320          }
 321          /** @var string $zero */
 322          /** @var string $str */
 323          $str = self::fe_tobytes($f);
 324          return !self::verify_32($str, (string) $zero);
 325      }
 326  
 327      /**
 328       * Multiply two field elements
 329       *
 330       * h = f * g
 331       *
 332       * @internal You should not use this directly from another application
 333       *
 334       * @security Is multiplication a source of timing leaks? If so, can we do
 335       *           anything to prevent that from happening?
 336       *
 337       * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
 338       * @param ParagonIE_Sodium_Core_Curve25519_Fe $g
 339       * @return ParagonIE_Sodium_Core_Curve25519_Fe
 340       */
 341      public static function fe_mul(
 342          ParagonIE_Sodium_Core_Curve25519_Fe $f,
 343          ParagonIE_Sodium_Core_Curve25519_Fe $g
 344      ) {
 345          // Ensure limbs aren't oversized.
 346          $f = self::fe_normalize($f);
 347          $g = self::fe_normalize($g);
 348          $f0 = $f[0];
 349          $f1 = $f[1];
 350          $f2 = $f[2];
 351          $f3 = $f[3];
 352          $f4 = $f[4];
 353          $f5 = $f[5];
 354          $f6 = $f[6];
 355          $f7 = $f[7];
 356          $f8 = $f[8];
 357          $f9 = $f[9];
 358          $g0 = $g[0];
 359          $g1 = $g[1];
 360          $g2 = $g[2];
 361          $g3 = $g[3];
 362          $g4 = $g[4];
 363          $g5 = $g[5];
 364          $g6 = $g[6];
 365          $g7 = $g[7];
 366          $g8 = $g[8];
 367          $g9 = $g[9];
 368          $g1_19 = self::mul($g1, 19, 5);
 369          $g2_19 = self::mul($g2, 19, 5);
 370          $g3_19 = self::mul($g3, 19, 5);
 371          $g4_19 = self::mul($g4, 19, 5);
 372          $g5_19 = self::mul($g5, 19, 5);
 373          $g6_19 = self::mul($g6, 19, 5);
 374          $g7_19 = self::mul($g7, 19, 5);
 375          $g8_19 = self::mul($g8, 19, 5);
 376          $g9_19 = self::mul($g9, 19, 5);
 377          $f1_2 = $f1 << 1;
 378          $f3_2 = $f3 << 1;
 379          $f5_2 = $f5 << 1;
 380          $f7_2 = $f7 << 1;
 381          $f9_2 = $f9 << 1;
 382          $f0g0    = self::mul($f0,    $g0, 26);
 383          $f0g1    = self::mul($f0,    $g1, 25);
 384          $f0g2    = self::mul($f0,    $g2, 26);
 385          $f0g3    = self::mul($f0,    $g3, 25);
 386          $f0g4    = self::mul($f0,    $g4, 26);
 387          $f0g5    = self::mul($f0,    $g5, 25);
 388          $f0g6    = self::mul($f0,    $g6, 26);
 389          $f0g7    = self::mul($f0,    $g7, 25);
 390          $f0g8    = self::mul($f0,    $g8, 26);
 391          $f0g9    = self::mul($f0,    $g9, 26);
 392          $f1g0    = self::mul($f1,    $g0, 26);
 393          $f1g1_2  = self::mul($f1_2,  $g1, 25);
 394          $f1g2    = self::mul($f1,    $g2, 26);
 395          $f1g3_2  = self::mul($f1_2,  $g3, 25);
 396          $f1g4    = self::mul($f1,    $g4, 26);
 397          $f1g5_2  = self::mul($f1_2,  $g5, 25);
 398          $f1g6    = self::mul($f1,    $g6, 26);
 399          $f1g7_2  = self::mul($f1_2,  $g7, 25);
 400          $f1g8    = self::mul($f1,    $g8, 26);
 401          $f1g9_38 = self::mul($g9_19, $f1_2, 26);
 402          $f2g0    = self::mul($f2,    $g0, 26);
 403          $f2g1    = self::mul($f2,    $g1, 25);
 404          $f2g2    = self::mul($f2,    $g2, 26);
 405          $f2g3    = self::mul($f2,    $g3, 25);
 406          $f2g4    = self::mul($f2,    $g4, 26);
 407          $f2g5    = self::mul($f2,    $g5, 25);
 408          $f2g6    = self::mul($f2,    $g6, 26);
 409          $f2g7    = self::mul($f2,    $g7, 25);
 410          $f2g8_19 = self::mul($g8_19, $f2, 26);
 411          $f2g9_19 = self::mul($g9_19, $f2, 26);
 412          $f3g0    = self::mul($f3,    $g0, 26);
 413          $f3g1_2  = self::mul($f3_2,  $g1, 25);
 414          $f3g2    = self::mul($f3,    $g2, 26);
 415          $f3g3_2  = self::mul($f3_2,  $g3, 25);
 416          $f3g4    = self::mul($f3,    $g4, 26);
 417          $f3g5_2  = self::mul($f3_2,  $g5, 25);
 418          $f3g6    = self::mul($f3,    $g6, 26);
 419          $f3g7_38 = self::mul($g7_19, $f3_2, 26);
 420          $f3g8_19 = self::mul($g8_19, $f3, 25);
 421          $f3g9_38 = self::mul($g9_19, $f3_2, 26);
 422          $f4g0    = self::mul($f4,    $g0, 26);
 423          $f4g1    = self::mul($f4,    $g1, 25);
 424          $f4g2    = self::mul($f4,    $g2, 26);
 425          $f4g3    = self::mul($f4,    $g3, 25);
 426          $f4g4    = self::mul($f4,    $g4, 26);
 427          $f4g5    = self::mul($f4,    $g5, 25);
 428          $f4g6_19 = self::mul($g6_19, $f4, 26);
 429          $f4g7_19 = self::mul($g7_19, $f4, 26);
 430          $f4g8_19 = self::mul($g8_19, $f4, 26);
 431          $f4g9_19 = self::mul($g9_19, $f4, 26);
 432          $f5g0    = self::mul($f5,    $g0, 26);
 433          $f5g1_2  = self::mul($f5_2,  $g1, 25);
 434          $f5g2    = self::mul($f5,    $g2, 26);
 435          $f5g3_2  = self::mul($f5_2,  $g3, 25);
 436          $f5g4    = self::mul($f5,    $g4, 26);
 437          $f5g5_38 = self::mul($g5_19, $f5_2, 26);
 438          $f5g6_19 = self::mul($g6_19, $f5, 25);
 439          $f5g7_38 = self::mul($g7_19, $f5_2, 26);
 440          $f5g8_19 = self::mul($g8_19, $f5, 25);
 441          $f5g9_38 = self::mul($g9_19, $f5_2, 26);
 442          $f6g0    = self::mul($f6,    $g0, 26);
 443          $f6g1    = self::mul($f6,    $g1, 25);
 444          $f6g2    = self::mul($f6,    $g2, 26);
 445          $f6g3    = self::mul($f6,    $g3, 25);
 446          $f6g4_19 = self::mul($g4_19, $f6, 26);
 447          $f6g5_19 = self::mul($g5_19, $f6, 26);
 448          $f6g6_19 = self::mul($g6_19, $f6, 26);
 449          $f6g7_19 = self::mul($g7_19, $f6, 26);
 450          $f6g8_19 = self::mul($g8_19, $f6, 26);
 451          $f6g9_19 = self::mul($g9_19, $f6, 26);
 452          $f7g0    = self::mul($f7,    $g0, 26);
 453          $f7g1_2  = self::mul($f7_2,  $g1, 25);
 454          $f7g2    = self::mul($f7,    $g2, 26);
 455          $f7g3_38 = self::mul($g3_19, $f7_2, 26);
 456          $f7g4_19 = self::mul($g4_19, $f7, 26);
 457          $f7g5_38 = self::mul($g5_19, $f7_2, 26);
 458          $f7g6_19 = self::mul($g6_19, $f7, 25);
 459          $f7g7_38 = self::mul($g7_19, $f7_2, 26);
 460          $f7g8_19 = self::mul($g8_19, $f7, 25);
 461          $f7g9_38 = self::mul($g9_19,$f7_2, 26);
 462          $f8g0    = self::mul($f8,    $g0, 26);
 463          $f8g1    = self::mul($f8,    $g1, 25);
 464          $f8g2_19 = self::mul($g2_19, $f8, 26);
 465          $f8g3_19 = self::mul($g3_19, $f8, 26);
 466          $f8g4_19 = self::mul($g4_19, $f8, 26);
 467          $f8g5_19 = self::mul($g5_19, $f8, 26);
 468          $f8g6_19 = self::mul($g6_19, $f8, 26);
 469          $f8g7_19 = self::mul($g7_19, $f8, 26);
 470          $f8g8_19 = self::mul($g8_19, $f8, 26);
 471          $f8g9_19 = self::mul($g9_19, $f8, 26);
 472          $f9g0    = self::mul($f9,    $g0, 26);
 473          $f9g1_38 = self::mul($g1_19, $f9_2, 26);
 474          $f9g2_19 = self::mul($g2_19, $f9, 25);
 475          $f9g3_38 = self::mul($g3_19, $f9_2, 26);
 476          $f9g4_19 = self::mul($g4_19, $f9, 25);
 477          $f9g5_38 = self::mul($g5_19, $f9_2, 26);
 478          $f9g6_19 = self::mul($g6_19, $f9, 25);
 479          $f9g7_38 = self::mul($g7_19, $f9_2, 26);
 480          $f9g8_19 = self::mul($g8_19, $f9, 25);
 481          $f9g9_38 = self::mul($g9_19, $f9_2, 26);
 482  
 483          $h0 = $f0g0 + $f1g9_38 + $f2g8_19 + $f3g7_38 + $f4g6_19 + $f5g5_38 + $f6g4_19 + $f7g3_38 + $f8g2_19 + $f9g1_38;
 484          $h1 = $f0g1 + $f1g0    + $f2g9_19 + $f3g8_19 + $f4g7_19 + $f5g6_19 + $f6g5_19 + $f7g4_19 + $f8g3_19 + $f9g2_19;
 485          $h2 = $f0g2 + $f1g1_2  + $f2g0    + $f3g9_38 + $f4g8_19 + $f5g7_38 + $f6g6_19 + $f7g5_38 + $f8g4_19 + $f9g3_38;
 486          $h3 = $f0g3 + $f1g2    + $f2g1    + $f3g0    + $f4g9_19 + $f5g8_19 + $f6g7_19 + $f7g6_19 + $f8g5_19 + $f9g4_19;
 487          $h4 = $f0g4 + $f1g3_2  + $f2g2    + $f3g1_2  + $f4g0    + $f5g9_38 + $f6g8_19 + $f7g7_38 + $f8g6_19 + $f9g5_38;
 488          $h5 = $f0g5 + $f1g4    + $f2g3    + $f3g2    + $f4g1    + $f5g0    + $f6g9_19 + $f7g8_19 + $f8g7_19 + $f9g6_19;
 489          $h6 = $f0g6 + $f1g5_2  + $f2g4    + $f3g3_2  + $f4g2    + $f5g1_2  + $f6g0    + $f7g9_38 + $f8g8_19 + $f9g7_38;
 490          $h7 = $f0g7 + $f1g6    + $f2g5    + $f3g4    + $f4g3    + $f5g2    + $f6g1    + $f7g0    + $f8g9_19 + $f9g8_19;
 491          $h8 = $f0g8 + $f1g7_2  + $f2g6    + $f3g5_2  + $f4g4    + $f5g3_2  + $f6g2    + $f7g1_2  + $f8g0    + $f9g9_38;
 492          $h9 = $f0g9 + $f1g8    + $f2g7    + $f3g6    + $f4g5    + $f5g4    + $f6g3    + $f7g2    + $f8g1    + $f9g0   ;
 493  
 494          $carry0 = ($h0 + (1 << 25)) >> 26;
 495          $h1 += $carry0;
 496          $h0 -= $carry0 << 26;
 497          $carry4 = ($h4 + (1 << 25)) >> 26;
 498          $h5 += $carry4;
 499          $h4 -= $carry4 << 26;
 500  
 501          $carry1 = ($h1 + (1 << 24)) >> 25;
 502          $h2 += $carry1;
 503          $h1 -= $carry1 << 25;
 504          $carry5 = ($h5 + (1 << 24)) >> 25;
 505          $h6 += $carry5;
 506          $h5 -= $carry5 << 25;
 507  
 508          $carry2 = ($h2 + (1 << 25)) >> 26;
 509          $h3 += $carry2;
 510          $h2 -= $carry2 << 26;
 511          $carry6 = ($h6 + (1 << 25)) >> 26;
 512          $h7 += $carry6;
 513          $h6 -= $carry6 << 26;
 514  
 515          $carry3 = ($h3 + (1 << 24)) >> 25;
 516          $h4 += $carry3;
 517          $h3 -= $carry3 << 25;
 518          $carry7 = ($h7 + (1 << 24)) >> 25;
 519          $h8 += $carry7;
 520          $h7 -= $carry7 << 25;
 521  
 522          $carry4 = ($h4 + (1 << 25)) >> 26;
 523          $h5 += $carry4;
 524          $h4 -= $carry4 << 26;
 525          $carry8 = ($h8 + (1 << 25)) >> 26;
 526          $h9 += $carry8;
 527          $h8 -= $carry8 << 26;
 528  
 529          $carry9 = ($h9 + (1 << 24)) >> 25;
 530          $h0 += self::mul($carry9, 19, 5);
 531          $h9 -= $carry9 << 25;
 532  
 533          $carry0 = ($h0 + (1 << 25)) >> 26;
 534          $h1 += $carry0;
 535          $h0 -= $carry0 << 26;
 536  
 537          return self::fe_normalize(
 538              ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
 539                  array(
 540                      (int) $h0,
 541                      (int) $h1,
 542                      (int) $h2,
 543                      (int) $h3,
 544                      (int) $h4,
 545                      (int) $h5,
 546                      (int) $h6,
 547                      (int) $h7,
 548                      (int) $h8,
 549                      (int) $h9
 550                  )
 551              )
 552          );
 553      }
 554  
 555      /**
 556       * Get the negative values for each piece of the field element.
 557       *
 558       * h = -f
 559       *
 560       * @internal You should not use this directly from another application
 561       *
 562       * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
 563       * @return ParagonIE_Sodium_Core_Curve25519_Fe
 564       * @psalm-suppress MixedAssignment
 565       */
 566      public static function fe_neg(ParagonIE_Sodium_Core_Curve25519_Fe $f)
 567      {
 568          $h = new ParagonIE_Sodium_Core_Curve25519_Fe();
 569          for ($i = 0; $i < 10; ++$i) {
 570              $h[$i] = -$f[$i];
 571          }
 572          return self::fe_normalize($h);
 573      }
 574  
 575      /**
 576       * Square a field element
 577       *
 578       * h = f * f
 579       *
 580       * @internal You should not use this directly from another application
 581       *
 582       * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
 583       * @return ParagonIE_Sodium_Core_Curve25519_Fe
 584       */
 585      public static function fe_sq(ParagonIE_Sodium_Core_Curve25519_Fe $f)
 586      {
 587          $f = self::fe_normalize($f);
 588          $f0 = (int) $f[0];
 589          $f1 = (int) $f[1];
 590          $f2 = (int) $f[2];
 591          $f3 = (int) $f[3];
 592          $f4 = (int) $f[4];
 593          $f5 = (int) $f[5];
 594          $f6 = (int) $f[6];
 595          $f7 = (int) $f[7];
 596          $f8 = (int) $f[8];
 597          $f9 = (int) $f[9];
 598  
 599          $f0_2 = $f0 << 1;
 600          $f1_2 = $f1 << 1;
 601          $f2_2 = $f2 << 1;
 602          $f3_2 = $f3 << 1;
 603          $f4_2 = $f4 << 1;
 604          $f5_2 = $f5 << 1;
 605          $f6_2 = $f6 << 1;
 606          $f7_2 = $f7 << 1;
 607          $f5_38 = self::mul($f5, 38, 6);
 608          $f6_19 = self::mul($f6, 19, 5);
 609          $f7_38 = self::mul($f7, 38, 6);
 610          $f8_19 = self::mul($f8, 19, 5);
 611          $f9_38 = self::mul($f9, 38, 6);
 612          $f0f0    = self::mul($f0,    $f0,    26);
 613          $f0f1_2  = self::mul($f0_2,  $f1,    26);
 614          $f0f2_2  = self::mul($f0_2,  $f2,    26);
 615          $f0f3_2  = self::mul($f0_2,  $f3,    26);
 616          $f0f4_2  = self::mul($f0_2,  $f4,    26);
 617          $f0f5_2  = self::mul($f0_2,  $f5,    26);
 618          $f0f6_2  = self::mul($f0_2,  $f6,    26);
 619          $f0f7_2  = self::mul($f0_2,  $f7,    26);
 620          $f0f8_2  = self::mul($f0_2,  $f8,    26);
 621          $f0f9_2  = self::mul($f0_2,  $f9,    26);
 622          $f1f1_2  = self::mul($f1_2,  $f1,    26);
 623          $f1f2_2  = self::mul($f1_2,  $f2,    26);
 624          $f1f3_4  = self::mul($f1_2,  $f3_2,  26);
 625          $f1f4_2  = self::mul($f1_2,  $f4,    26);
 626          $f1f5_4  = self::mul($f1_2,  $f5_2,  26);
 627          $f1f6_2  = self::mul($f1_2,  $f6,    26);
 628          $f1f7_4  = self::mul($f1_2,  $f7_2,  26);
 629          $f1f8_2  = self::mul($f1_2,  $f8,    26);
 630          $f1f9_76 = self::mul($f9_38, $f1_2,  27);
 631          $f2f2    = self::mul($f2,    $f2,    27);
 632          $f2f3_2  = self::mul($f2_2,  $f3,    27);
 633          $f2f4_2  = self::mul($f2_2,  $f4,    27);
 634          $f2f5_2  = self::mul($f2_2,  $f5,    27);
 635          $f2f6_2  = self::mul($f2_2,  $f6,    27);
 636          $f2f7_2  = self::mul($f2_2,  $f7,    27);
 637          $f2f8_38 = self::mul($f8_19, $f2_2,  27);
 638          $f2f9_38 = self::mul($f9_38, $f2,    26);
 639          $f3f3_2  = self::mul($f3_2,  $f3,    26);
 640          $f3f4_2  = self::mul($f3_2,  $f4,    26);
 641          $f3f5_4  = self::mul($f3_2,  $f5_2,  26);
 642          $f3f6_2  = self::mul($f3_2,  $f6,    26);
 643          $f3f7_76 = self::mul($f7_38, $f3_2,  26);
 644          $f3f8_38 = self::mul($f8_19, $f3_2,  26);
 645          $f3f9_76 = self::mul($f9_38, $f3_2,  26);
 646          $f4f4    = self::mul($f4,    $f4,    26);
 647          $f4f5_2  = self::mul($f4_2,  $f5,    26);
 648          $f4f6_38 = self::mul($f6_19, $f4_2,  27);
 649          $f4f7_38 = self::mul($f7_38, $f4,    26);
 650          $f4f8_38 = self::mul($f8_19, $f4_2,  27);
 651          $f4f9_38 = self::mul($f9_38, $f4,    26);
 652          $f5f5_38 = self::mul($f5_38, $f5,    26);
 653          $f5f6_38 = self::mul($f6_19, $f5_2,  26);
 654          $f5f7_76 = self::mul($f7_38, $f5_2,  26);
 655          $f5f8_38 = self::mul($f8_19, $f5_2,  26);
 656          $f5f9_76 = self::mul($f9_38, $f5_2,  26);
 657          $f6f6_19 = self::mul($f6_19, $f6,    26);
 658          $f6f7_38 = self::mul($f7_38, $f6,    26);
 659          $f6f8_38 = self::mul($f8_19, $f6_2,  27);
 660          $f6f9_38 = self::mul($f9_38, $f6,    26);
 661          $f7f7_38 = self::mul($f7_38, $f7,    26);
 662          $f7f8_38 = self::mul($f8_19, $f7_2,  26);
 663          $f7f9_76 = self::mul($f9_38, $f7_2,  26);
 664          $f8f8_19 = self::mul($f8_19, $f8,    26);
 665          $f8f9_38 = self::mul($f9_38, $f8,    26);
 666          $f9f9_38 = self::mul($f9_38, $f9,    26);
 667          $h0 = $f0f0   + $f1f9_76 + $f2f8_38 + $f3f7_76 + $f4f6_38 + $f5f5_38;
 668          $h1 = $f0f1_2 + $f2f9_38 + $f3f8_38 + $f4f7_38 + $f5f6_38;
 669          $h2 = $f0f2_2 + $f1f1_2  + $f3f9_76 + $f4f8_38 + $f5f7_76 + $f6f6_19;
 670          $h3 = $f0f3_2 + $f1f2_2  + $f4f9_38 + $f5f8_38 + $f6f7_38;
 671          $h4 = $f0f4_2 + $f1f3_4  + $f2f2    + $f5f9_76 + $f6f8_38 + $f7f7_38;
 672          $h5 = $f0f5_2 + $f1f4_2  + $f2f3_2  + $f6f9_38 + $f7f8_38;
 673          $h6 = $f0f6_2 + $f1f5_4  + $f2f4_2  + $f3f3_2  + $f7f9_76 + $f8f8_19;
 674          $h7 = $f0f7_2 + $f1f6_2  + $f2f5_2  + $f3f4_2  + $f8f9_38;
 675          $h8 = $f0f8_2 + $f1f7_4  + $f2f6_2  + $f3f5_4  + $f4f4    + $f9f9_38;
 676          $h9 = $f0f9_2 + $f1f8_2  + $f2f7_2  + $f3f6_2  + $f4f5_2;
 677  
 678          $carry0 = ($h0 + (1 << 25)) >> 26;
 679          $h1 += $carry0;
 680          $h0 -= $carry0 << 26;
 681          $carry4 = ($h4 + (1 << 25)) >> 26;
 682          $h5 += $carry4;
 683          $h4 -= $carry4 << 26;
 684  
 685          $carry1 = ($h1 + (1 << 24)) >> 25;
 686          $h2 += $carry1;
 687          $h1 -= $carry1 << 25;
 688          $carry5 = ($h5 + (1 << 24)) >> 25;
 689          $h6 += $carry5;
 690          $h5 -= $carry5 << 25;
 691  
 692          $carry2 = ($h2 + (1 << 25)) >> 26;
 693          $h3 += $carry2;
 694          $h2 -= $carry2 << 26;
 695          $carry6 = ($h6 + (1 << 25)) >> 26;
 696          $h7 += $carry6;
 697          $h6 -= $carry6 << 26;
 698  
 699          $carry3 = ($h3 + (1 << 24)) >> 25;
 700          $h4 += $carry3;
 701          $h3 -= $carry3 << 25;
 702          $carry7 = ($h7 + (1 << 24)) >> 25;
 703          $h8 += $carry7;
 704          $h7 -= $carry7 << 25;
 705  
 706          $carry4 = ($h4 + (1 << 25)) >> 26;
 707          $h5 += $carry4;
 708          $h4 -= $carry4 << 26;
 709          $carry8 = ($h8 + (1 << 25)) >> 26;
 710          $h9 += $carry8;
 711          $h8 -= $carry8 << 26;
 712  
 713          $carry9 = ($h9 + (1 << 24)) >> 25;
 714          $h0 += self::mul($carry9, 19, 5);
 715          $h9 -= $carry9 << 25;
 716  
 717          $carry0 = ($h0 + (1 << 25)) >> 26;
 718          $h1 += $carry0;
 719          $h0 -= $carry0 << 26;
 720  
 721          return self::fe_normalize(
 722              ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
 723                  array(
 724                      (int) $h0,
 725                      (int) $h1,
 726                      (int) $h2,
 727                      (int) $h3,
 728                      (int) $h4,
 729                      (int) $h5,
 730                      (int) $h6,
 731                      (int) $h7,
 732                      (int) $h8,
 733                      (int) $h9
 734                  )
 735              )
 736          );
 737      }
 738  
 739  
 740      /**
 741       * Square and double a field element
 742       *
 743       * h = 2 * f * f
 744       *
 745       * @internal You should not use this directly from another application
 746       *
 747       * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
 748       * @return ParagonIE_Sodium_Core_Curve25519_Fe
 749       */
 750      public static function fe_sq2(ParagonIE_Sodium_Core_Curve25519_Fe $f)
 751      {
 752          $f = self::fe_normalize($f);
 753          $f0 = (int) $f[0];
 754          $f1 = (int) $f[1];
 755          $f2 = (int) $f[2];
 756          $f3 = (int) $f[3];
 757          $f4 = (int) $f[4];
 758          $f5 = (int) $f[5];
 759          $f6 = (int) $f[6];
 760          $f7 = (int) $f[7];
 761          $f8 = (int) $f[8];
 762          $f9 = (int) $f[9];
 763  
 764          $f0_2 = $f0 << 1;
 765          $f1_2 = $f1 << 1;
 766          $f2_2 = $f2 << 1;
 767          $f3_2 = $f3 << 1;
 768          $f4_2 = $f4 << 1;
 769          $f5_2 = $f5 << 1;
 770          $f6_2 = $f6 << 1;
 771          $f7_2 = $f7 << 1;
 772          $f5_38 = self::mul($f5, 38, 6); /* 1.959375*2^30 */
 773          $f6_19 = self::mul($f6, 19, 5); /* 1.959375*2^30 */
 774          $f7_38 = self::mul($f7, 38, 6); /* 1.959375*2^30 */
 775          $f8_19 = self::mul($f8, 19, 5); /* 1.959375*2^30 */
 776          $f9_38 = self::mul($f9, 38, 6); /* 1.959375*2^30 */
 777          $f0f0 = self::mul($f0, $f0, 24);
 778          $f0f1_2 = self::mul($f0_2, $f1, 24);
 779          $f0f2_2 = self::mul($f0_2, $f2, 24);
 780          $f0f3_2 = self::mul($f0_2, $f3, 24);
 781          $f0f4_2 = self::mul($f0_2, $f4, 24);
 782          $f0f5_2 = self::mul($f0_2, $f5, 24);
 783          $f0f6_2 = self::mul($f0_2, $f6, 24);
 784          $f0f7_2 = self::mul($f0_2, $f7, 24);
 785          $f0f8_2 = self::mul($f0_2, $f8, 24);
 786          $f0f9_2 = self::mul($f0_2, $f9, 24);
 787          $f1f1_2 = self::mul($f1_2,  $f1, 24);
 788          $f1f2_2 = self::mul($f1_2,  $f2, 24);
 789          $f1f3_4 = self::mul($f1_2,  $f3_2, 24);
 790          $f1f4_2 = self::mul($f1_2,  $f4, 24);
 791          $f1f5_4 = self::mul($f1_2,  $f5_2, 24);
 792          $f1f6_2 = self::mul($f1_2,  $f6, 24);
 793          $f1f7_4 = self::mul($f1_2,  $f7_2, 24);
 794          $f1f8_2 = self::mul($f1_2,  $f8, 24);
 795          $f1f9_76 = self::mul($f9_38, $f1_2, 24);
 796          $f2f2 = self::mul($f2,  $f2, 24);
 797          $f2f3_2 = self::mul($f2_2,  $f3, 24);
 798          $f2f4_2 = self::mul($f2_2,  $f4, 24);
 799          $f2f5_2 = self::mul($f2_2,  $f5, 24);
 800          $f2f6_2 = self::mul($f2_2,  $f6, 24);
 801          $f2f7_2 = self::mul($f2_2,  $f7, 24);
 802          $f2f8_38 = self::mul($f8_19, $f2_2, 25);
 803          $f2f9_38 = self::mul($f9_38, $f2, 24);
 804          $f3f3_2 = self::mul($f3_2,  $f3, 24);
 805          $f3f4_2 = self::mul($f3_2,  $f4, 24);
 806          $f3f5_4 = self::mul($f3_2,  $f5_2, 24);
 807          $f3f6_2 = self::mul($f3_2,  $f6, 24);
 808          $f3f7_76 = self::mul($f7_38, $f3_2, 24);
 809          $f3f8_38 = self::mul($f8_19, $f3_2, 24);
 810          $f3f9_76 = self::mul($f9_38, $f3_2, 24);
 811          $f4f4 = self::mul($f4,  $f4, 24);
 812          $f4f5_2 = self::mul($f4_2,  $f5, 24);
 813          $f4f6_38 = self::mul($f6_19, $f4_2, 25);
 814          $f4f7_38 = self::mul($f7_38, $f4, 24);
 815          $f4f8_38 = self::mul($f8_19, $f4_2, 25);
 816          $f4f9_38 = self::mul($f9_38, $f4, 24);
 817          $f5f5_38 = self::mul($f5_38, $f5, 24);
 818          $f5f6_38 = self::mul($f6_19, $f5_2, 24);
 819          $f5f7_76 = self::mul($f7_38, $f5_2, 24);
 820          $f5f8_38 = self::mul($f8_19, $f5_2, 24);
 821          $f5f9_76 = self::mul($f9_38, $f5_2, 24);
 822          $f6f6_19 = self::mul($f6_19, $f6, 24);
 823          $f6f7_38 = self::mul($f7_38, $f6, 24);
 824          $f6f8_38 = self::mul($f8_19, $f6_2, 25);
 825          $f6f9_38 = self::mul($f9_38, $f6, 24);
 826          $f7f7_38 = self::mul($f7_38, $f7, 24);
 827          $f7f8_38 = self::mul($f8_19, $f7_2, 24);
 828          $f7f9_76 = self::mul($f9_38, $f7_2, 24);
 829          $f8f8_19 = self::mul($f8_19, $f8, 24);
 830          $f8f9_38 = self::mul($f9_38, $f8, 24);
 831          $f9f9_38 = self::mul($f9_38, $f9, 24);
 832  
 833          $h0 = (int) ($f0f0 + $f1f9_76 + $f2f8_38 + $f3f7_76 + $f4f6_38 + $f5f5_38) << 1;
 834          $h1 = (int) ($f0f1_2 + $f2f9_38 + $f3f8_38 + $f4f7_38 + $f5f6_38) << 1;
 835          $h2 = (int) ($f0f2_2 + $f1f1_2  + $f3f9_76 + $f4f8_38 + $f5f7_76 + $f6f6_19) << 1;
 836          $h3 = (int) ($f0f3_2 + $f1f2_2  + $f4f9_38 + $f5f8_38 + $f6f7_38) << 1;
 837          $h4 = (int) ($f0f4_2 + $f1f3_4  + $f2f2    + $f5f9_76 + $f6f8_38 + $f7f7_38) << 1;
 838          $h5 = (int) ($f0f5_2 + $f1f4_2  + $f2f3_2  + $f6f9_38 + $f7f8_38) << 1;
 839          $h6 = (int) ($f0f6_2 + $f1f5_4  + $f2f4_2  + $f3f3_2  + $f7f9_76 + $f8f8_19) << 1;
 840          $h7 = (int) ($f0f7_2 + $f1f6_2  + $f2f5_2  + $f3f4_2  + $f8f9_38) << 1;
 841          $h8 = (int) ($f0f8_2 + $f1f7_4  + $f2f6_2  + $f3f5_4  + $f4f4    + $f9f9_38) << 1;
 842          $h9 = (int) ($f0f9_2 + $f1f8_2  + $f2f7_2  + $f3f6_2  + $f4f5_2) << 1;
 843  
 844          $carry0 = ($h0 + (1 << 25)) >> 26;
 845          $h1 += $carry0;
 846          $h0 -= $carry0 << 26;
 847          $carry4 = ($h4 + (1 << 25)) >> 26;
 848          $h5 += $carry4;
 849          $h4 -= $carry4 << 26;
 850  
 851          $carry1 = ($h1 + (1 << 24)) >> 25;
 852          $h2 += $carry1;
 853          $h1 -= $carry1 << 25;
 854          $carry5 = ($h5 + (1 << 24)) >> 25;
 855          $h6 += $carry5;
 856          $h5 -= $carry5 << 25;
 857  
 858          $carry2 = ($h2 + (1 << 25)) >> 26;
 859          $h3 += $carry2;
 860          $h2 -= $carry2 << 26;
 861          $carry6 = ($h6 + (1 << 25)) >> 26;
 862          $h7 += $carry6;
 863          $h6 -= $carry6 << 26;
 864  
 865          $carry3 = ($h3 + (1 << 24)) >> 25;
 866          $h4 += $carry3;
 867          $h3 -= $carry3 << 25;
 868          $carry7 = ($h7 + (1 << 24)) >> 25;
 869          $h8 += $carry7;
 870          $h7 -= $carry7 << 25;
 871  
 872          $carry4 = ($h4 + (1 << 25)) >> 26;
 873          $h5 += $carry4;
 874          $h4 -= $carry4 << 26;
 875          $carry8 = ($h8 + (1 << 25)) >> 26;
 876          $h9 += $carry8;
 877          $h8 -= $carry8 << 26;
 878  
 879          $carry9 = ($h9 + (1 << 24)) >> 25;
 880          $h0 += self::mul($carry9, 19, 5);
 881          $h9 -= $carry9 << 25;
 882  
 883          $carry0 = ($h0 + (1 << 25)) >> 26;
 884          $h1 += $carry0;
 885          $h0 -= $carry0 << 26;
 886  
 887          return self::fe_normalize(
 888              ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
 889                  array(
 890                      (int) $h0,
 891                      (int) $h1,
 892                      (int) $h2,
 893                      (int) $h3,
 894                      (int) $h4,
 895                      (int) $h5,
 896                      (int) $h6,
 897                      (int) $h7,
 898                      (int) $h8,
 899                      (int) $h9
 900                  )
 901              )
 902          );
 903      }
 904  
 905      /**
 906       * @internal You should not use this directly from another application
 907       *
 908       * @param ParagonIE_Sodium_Core_Curve25519_Fe $Z
 909       * @return ParagonIE_Sodium_Core_Curve25519_Fe
 910       */
 911      public static function fe_invert(ParagonIE_Sodium_Core_Curve25519_Fe $Z)
 912      {
 913          $z = clone $Z;
 914          $t0 = self::fe_sq($z);
 915          $t1 = self::fe_sq($t0);
 916          $t1 = self::fe_sq($t1);
 917          $t1 = self::fe_mul($z, $t1);
 918          $t0 = self::fe_mul($t0, $t1);
 919          $t2 = self::fe_sq($t0);
 920          $t1 = self::fe_mul($t1, $t2);
 921          $t2 = self::fe_sq($t1);
 922          for ($i = 1; $i < 5; ++$i) {
 923              $t2 = self::fe_sq($t2);
 924          }
 925          $t1 = self::fe_mul($t2, $t1);
 926          $t2 = self::fe_sq($t1);
 927          for ($i = 1; $i < 10; ++$i) {
 928              $t2 = self::fe_sq($t2);
 929          }
 930          $t2 = self::fe_mul($t2, $t1);
 931          $t3 = self::fe_sq($t2);
 932          for ($i = 1; $i < 20; ++$i) {
 933              $t3 = self::fe_sq($t3);
 934          }
 935          $t2 = self::fe_mul($t3, $t2);
 936          $t2 = self::fe_sq($t2);
 937          for ($i = 1; $i < 10; ++$i) {
 938              $t2 = self::fe_sq($t2);
 939          }
 940          $t1 = self::fe_mul($t2, $t1);
 941          $t2 = self::fe_sq($t1);
 942          for ($i = 1; $i < 50; ++$i) {
 943              $t2 = self::fe_sq($t2);
 944          }
 945          $t2 = self::fe_mul($t2, $t1);
 946          $t3 = self::fe_sq($t2);
 947          for ($i = 1; $i < 100; ++$i) {
 948              $t3 = self::fe_sq($t3);
 949          }
 950          $t2 = self::fe_mul($t3, $t2);
 951          $t2 = self::fe_sq($t2);
 952          for ($i = 1; $i < 50; ++$i) {
 953              $t2 = self::fe_sq($t2);
 954          }
 955          $t1 = self::fe_mul($t2, $t1);
 956          $t1 = self::fe_sq($t1);
 957          for ($i = 1; $i < 5; ++$i) {
 958              $t1 = self::fe_sq($t1);
 959          }
 960          return self::fe_mul($t1, $t0);
 961      }
 962  
 963      /**
 964       * @internal You should not use this directly from another application
 965       *
 966       * @ref https://github.com/jedisct1/libsodium/blob/68564326e1e9dc57ef03746f85734232d20ca6fb/src/libsodium/crypto_core/curve25519/ref10/curve25519_ref10.c#L1054-L1106
 967       *
 968       * @param ParagonIE_Sodium_Core_Curve25519_Fe $z
 969       * @return ParagonIE_Sodium_Core_Curve25519_Fe
 970       */
 971      public static function fe_pow22523(ParagonIE_Sodium_Core_Curve25519_Fe $z)
 972      {
 973          $z = self::fe_normalize($z);
 974          # fe_sq(t0, z);
 975          # fe_sq(t1, t0);
 976          # fe_sq(t1, t1);
 977          # fe_mul(t1, z, t1);
 978          # fe_mul(t0, t0, t1);
 979          # fe_sq(t0, t0);
 980          # fe_mul(t0, t1, t0);
 981          # fe_sq(t1, t0);
 982          $t0 = self::fe_sq($z);
 983          $t1 = self::fe_sq($t0);
 984          $t1 = self::fe_sq($t1);
 985          $t1 = self::fe_mul($z, $t1);
 986          $t0 = self::fe_mul($t0, $t1);
 987          $t0 = self::fe_sq($t0);
 988          $t0 = self::fe_mul($t1, $t0);
 989          $t1 = self::fe_sq($t0);
 990  
 991          # for (i = 1; i < 5; ++i) {
 992          #     fe_sq(t1, t1);
 993          # }
 994          for ($i = 1; $i < 5; ++$i) {
 995              $t1 = self::fe_sq($t1);
 996          }
 997  
 998          # fe_mul(t0, t1, t0);
 999          # fe_sq(t1, t0);
1000          $t0 = self::fe_mul($t1, $t0);
1001          $t1 = self::fe_sq($t0);
1002  
1003          # for (i = 1; i < 10; ++i) {
1004          #     fe_sq(t1, t1);
1005          # }
1006          for ($i = 1; $i < 10; ++$i) {
1007              $t1 = self::fe_sq($t1);
1008          }
1009  
1010          # fe_mul(t1, t1, t0);
1011          # fe_sq(t2, t1);
1012          $t1 = self::fe_mul($t1, $t0);
1013          $t2 = self::fe_sq($t1);
1014  
1015          # for (i = 1; i < 20; ++i) {
1016          #     fe_sq(t2, t2);
1017          # }
1018          for ($i = 1; $i < 20; ++$i) {
1019              $t2 = self::fe_sq($t2);
1020          }
1021  
1022          # fe_mul(t1, t2, t1);
1023          # fe_sq(t1, t1);
1024          $t1 = self::fe_mul($t2, $t1);
1025          $t1 = self::fe_sq($t1);
1026  
1027          # for (i = 1; i < 10; ++i) {
1028          #     fe_sq(t1, t1);
1029          # }
1030          for ($i = 1; $i < 10; ++$i) {
1031              $t1 = self::fe_sq($t1);
1032          }
1033  
1034          # fe_mul(t0, t1, t0);
1035          # fe_sq(t1, t0);
1036          $t0 = self::fe_mul($t1, $t0);
1037          $t1 = self::fe_sq($t0);
1038  
1039          # for (i = 1; i < 50; ++i) {
1040          #     fe_sq(t1, t1);
1041          # }
1042          for ($i = 1; $i < 50; ++$i) {
1043              $t1 = self::fe_sq($t1);
1044          }
1045  
1046          # fe_mul(t1, t1, t0);
1047          # fe_sq(t2, t1);
1048          $t1 = self::fe_mul($t1, $t0);
1049          $t2 = self::fe_sq($t1);
1050  
1051          # for (i = 1; i < 100; ++i) {
1052          #     fe_sq(t2, t2);
1053          # }
1054          for ($i = 1; $i < 100; ++$i) {
1055              $t2 = self::fe_sq($t2);
1056          }
1057  
1058          # fe_mul(t1, t2, t1);
1059          # fe_sq(t1, t1);
1060          $t1 = self::fe_mul($t2, $t1);
1061          $t1 = self::fe_sq($t1);
1062  
1063          # for (i = 1; i < 50; ++i) {
1064          #     fe_sq(t1, t1);
1065          # }
1066          for ($i = 1; $i < 50; ++$i) {
1067              $t1 = self::fe_sq($t1);
1068          }
1069  
1070          # fe_mul(t0, t1, t0);
1071          # fe_sq(t0, t0);
1072          # fe_sq(t0, t0);
1073          # fe_mul(out, t0, z);
1074          $t0 = self::fe_mul($t1, $t0);
1075          $t0 = self::fe_sq($t0);
1076          $t0 = self::fe_sq($t0);
1077          return self::fe_mul($t0, $z);
1078      }
1079  
1080      /**
1081       * Subtract two field elements.
1082       *
1083       * h = f - g
1084       *
1085       * Preconditions:
1086       * |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
1087       * |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
1088       *
1089       * Postconditions:
1090       * |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
1091       *
1092       * @internal You should not use this directly from another application
1093       *
1094       * @param ParagonIE_Sodium_Core_Curve25519_Fe $f
1095       * @param ParagonIE_Sodium_Core_Curve25519_Fe $g
1096       * @return ParagonIE_Sodium_Core_Curve25519_Fe
1097       * @psalm-suppress MixedOperand
1098       */
1099      public static function fe_sub(ParagonIE_Sodium_Core_Curve25519_Fe $f, ParagonIE_Sodium_Core_Curve25519_Fe $g)
1100      {
1101          return self::fe_normalize(
1102              ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
1103                  array(
1104                      (int) ($f[0] - $g[0]),
1105                      (int) ($f[1] - $g[1]),
1106                      (int) ($f[2] - $g[2]),
1107                      (int) ($f[3] - $g[3]),
1108                      (int) ($f[4] - $g[4]),
1109                      (int) ($f[5] - $g[5]),
1110                      (int) ($f[6] - $g[6]),
1111                      (int) ($f[7] - $g[7]),
1112                      (int) ($f[8] - $g[8]),
1113                      (int) ($f[9] - $g[9])
1114                  )
1115              )
1116          );
1117      }
1118  
1119      /**
1120       * Add two group elements.
1121       *
1122       * r = p + q
1123       *
1124       * @internal You should not use this directly from another application
1125       *
1126       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
1127       * @param ParagonIE_Sodium_Core_Curve25519_Ge_Cached $q
1128       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
1129       */
1130      public static function ge_add(
1131          ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p,
1132          ParagonIE_Sodium_Core_Curve25519_Ge_Cached $q
1133      ) {
1134          $r = new ParagonIE_Sodium_Core_Curve25519_Ge_P1p1();
1135          $r->X = self::fe_add($p->Y, $p->X);
1136          $r->Y = self::fe_sub($p->Y, $p->X);
1137          $r->Z = self::fe_mul($r->X, $q->YplusX);
1138          $r->Y = self::fe_mul($r->Y, $q->YminusX);
1139          $r->T = self::fe_mul($q->T2d, $p->T);
1140          $r->X = self::fe_mul($p->Z, $q->Z);
1141          $t0   = self::fe_add($r->X, $r->X);
1142          $r->X = self::fe_sub($r->Z, $r->Y);
1143          $r->Y = self::fe_add($r->Z, $r->Y);
1144          $r->Z = self::fe_add($t0, $r->T);
1145          $r->T = self::fe_sub($t0, $r->T);
1146          return $r;
1147      }
1148  
1149      /**
1150       * @internal You should not use this directly from another application
1151       *
1152       * @ref https://github.com/jedisct1/libsodium/blob/157c4a80c13b117608aeae12178b2d38825f9f8f/src/libsodium/crypto_core/curve25519/ref10/curve25519_ref10.c#L1185-L1215
1153       * @param string $a
1154       * @return array<int, mixed>
1155       * @throws SodiumException
1156       * @throws TypeError
1157       */
1158      public static function slide($a)
1159      {
1160          if (self::strlen($a) < 256) {
1161              if (self::strlen($a) < 16) {
1162                  $a = str_pad($a, 256, '0', STR_PAD_RIGHT);
1163              }
1164          }
1165          /** @var array<int, int> $r */
1166          $r = array();
1167  
1168          /** @var int $i */
1169          for ($i = 0; $i < 256; ++$i) {
1170              $r[$i] = (int) (
1171                  1 & (
1172                      self::chrToInt($a[(int) ($i >> 3)])
1173                          >>
1174                      ($i & 7)
1175                  )
1176              );
1177          }
1178  
1179          for ($i = 0;$i < 256;++$i) {
1180              if ($r[$i]) {
1181                  for ($b = 1;$b <= 6 && $i + $b < 256;++$b) {
1182                      if ($r[$i + $b]) {
1183                          if ($r[$i] + ($r[$i + $b] << $b) <= 15) {
1184                              $r[$i] += $r[$i + $b] << $b;
1185                              $r[$i + $b] = 0;
1186                          } elseif ($r[$i] - ($r[$i + $b] << $b) >= -15) {
1187                              $r[$i] -= $r[$i + $b] << $b;
1188                              for ($k = $i + $b; $k < 256; ++$k) {
1189                                  if (!$r[$k]) {
1190                                      $r[$k] = 1;
1191                                      break;
1192                                  }
1193                                  $r[$k] = 0;
1194                              }
1195                          } else {
1196                              break;
1197                          }
1198                      }
1199                  }
1200              }
1201          }
1202          return $r;
1203      }
1204  
1205      /**
1206       * @internal You should not use this directly from another application
1207       *
1208       * @param string $s
1209       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
1210       * @throws SodiumException
1211       * @throws TypeError
1212       */
1213      public static function ge_frombytes_negate_vartime($s)
1214      {
1215          static $d = null;
1216          if (!$d) {
1217              $d = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d);
1218          }
1219  
1220          # fe_frombytes(h->Y,s);
1221          # fe_1(h->Z);
1222          $h = new ParagonIE_Sodium_Core_Curve25519_Ge_P3(
1223              self::fe_0(),
1224              self::fe_frombytes($s),
1225              self::fe_1()
1226          );
1227  
1228          # fe_sq(u,h->Y);
1229          # fe_mul(v,u,d);
1230          # fe_sub(u,u,h->Z);       /* u = y^2-1 */
1231          # fe_add(v,v,h->Z);       /* v = dy^2+1 */
1232          $u = self::fe_sq($h->Y);
1233          /** @var ParagonIE_Sodium_Core_Curve25519_Fe $d */
1234          $v = self::fe_mul($u, $d);
1235          $u = self::fe_sub($u, $h->Z); /* u =  y^2 - 1 */
1236          $v = self::fe_add($v, $h->Z); /* v = dy^2 + 1 */
1237  
1238          # fe_sq(v3,v);
1239          # fe_mul(v3,v3,v);        /* v3 = v^3 */
1240          # fe_sq(h->X,v3);
1241          # fe_mul(h->X,h->X,v);
1242          # fe_mul(h->X,h->X,u);    /* x = uv^7 */
1243          $v3 = self::fe_sq($v);
1244          $v3 = self::fe_mul($v3, $v); /* v3 = v^3 */
1245          $h->X = self::fe_sq($v3);
1246          $h->X = self::fe_mul($h->X, $v);
1247          $h->X = self::fe_mul($h->X, $u); /* x = uv^7 */
1248  
1249          # fe_pow22523(h->X,h->X); /* x = (uv^7)^((q-5)/8) */
1250          # fe_mul(h->X,h->X,v3);
1251          # fe_mul(h->X,h->X,u);    /* x = uv^3(uv^7)^((q-5)/8) */
1252          $h->X = self::fe_pow22523($h->X); /* x = (uv^7)^((q-5)/8) */
1253          $h->X = self::fe_mul($h->X, $v3);
1254          $h->X = self::fe_mul($h->X, $u); /* x = uv^3(uv^7)^((q-5)/8) */
1255  
1256          # fe_sq(vxx,h->X);
1257          # fe_mul(vxx,vxx,v);
1258          # fe_sub(check,vxx,u);    /* vx^2-u */
1259          $vxx = self::fe_sq($h->X);
1260          $vxx = self::fe_mul($vxx, $v);
1261          $check = self::fe_sub($vxx, $u); /* vx^2 - u */
1262  
1263          # if (fe_isnonzero(check)) {
1264          #     fe_add(check,vxx,u);  /* vx^2+u */
1265          #     if (fe_isnonzero(check)) {
1266          #         return -1;
1267          #     }
1268          #     fe_mul(h->X,h->X,sqrtm1);
1269          # }
1270          if (self::fe_isnonzero($check)) {
1271              $check = self::fe_add($vxx, $u); /* vx^2 + u */
1272              if (self::fe_isnonzero($check)) {
1273                  throw new RangeException('Internal check failed.');
1274              }
1275              $h->X = self::fe_mul(
1276                  $h->X,
1277                  ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1)
1278              );
1279          }
1280  
1281          # if (fe_isnegative(h->X) == (s[31] >> 7)) {
1282          #     fe_neg(h->X,h->X);
1283          # }
1284          $i = self::chrToInt($s[31]);
1285          if (self::fe_isnegative($h->X) === ($i >> 7)) {
1286              $h->X = self::fe_neg($h->X);
1287          }
1288  
1289          # fe_mul(h->T,h->X,h->Y);
1290          $h->T = self::fe_mul($h->X, $h->Y);
1291          return $h;
1292      }
1293  
1294      /**
1295       * @internal You should not use this directly from another application
1296       *
1297       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $R
1298       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
1299       * @param ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $q
1300       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
1301       */
1302      public static function ge_madd(
1303          ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $R,
1304          ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p,
1305          ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $q
1306      ) {
1307          $r = clone $R;
1308          $r->X = self::fe_add($p->Y, $p->X);
1309          $r->Y = self::fe_sub($p->Y, $p->X);
1310          $r->Z = self::fe_mul($r->X, $q->yplusx);
1311          $r->Y = self::fe_mul($r->Y, $q->yminusx);
1312          $r->T = self::fe_mul($q->xy2d, $p->T);
1313          $t0 = self::fe_add(clone $p->Z, clone $p->Z);
1314          $r->X = self::fe_sub($r->Z, $r->Y);
1315          $r->Y = self::fe_add($r->Z, $r->Y);
1316          $r->Z = self::fe_add($t0, $r->T);
1317          $r->T = self::fe_sub($t0, $r->T);
1318  
1319          return $r;
1320      }
1321  
1322      /**
1323       * @internal You should not use this directly from another application
1324       *
1325       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $R
1326       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
1327       * @param ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $q
1328       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
1329       */
1330      public static function ge_msub(
1331          ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $R,
1332          ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p,
1333          ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $q
1334      ) {
1335          $r = clone $R;
1336  
1337          $r->X = self::fe_add($p->Y, $p->X);
1338          $r->Y = self::fe_sub($p->Y, $p->X);
1339          $r->Z = self::fe_mul($r->X, $q->yminusx);
1340          $r->Y = self::fe_mul($r->Y, $q->yplusx);
1341          $r->T = self::fe_mul($q->xy2d, $p->T);
1342          $t0 = self::fe_add($p->Z, $p->Z);
1343          $r->X = self::fe_sub($r->Z, $r->Y);
1344          $r->Y = self::fe_add($r->Z, $r->Y);
1345          $r->Z = self::fe_sub($t0, $r->T);
1346          $r->T = self::fe_add($t0, $r->T);
1347  
1348          return $r;
1349      }
1350  
1351      /**
1352       * @internal You should not use this directly from another application
1353       *
1354       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $p
1355       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P2
1356       */
1357      public static function ge_p1p1_to_p2(ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $p)
1358      {
1359          $r = new ParagonIE_Sodium_Core_Curve25519_Ge_P2();
1360          $r->X = self::fe_mul($p->X, $p->T);
1361          $r->Y = self::fe_mul($p->Y, $p->Z);
1362          $r->Z = self::fe_mul($p->Z, $p->T);
1363          return $r;
1364      }
1365  
1366      /**
1367       * @internal You should not use this directly from another application
1368       *
1369       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $p
1370       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
1371       */
1372      public static function ge_p1p1_to_p3(ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $p)
1373      {
1374          $r = new ParagonIE_Sodium_Core_Curve25519_Ge_P3();
1375          $r->X = self::fe_mul($p->X, $p->T);
1376          $r->Y = self::fe_mul($p->Y, $p->Z);
1377          $r->Z = self::fe_mul($p->Z, $p->T);
1378          $r->T = self::fe_mul($p->X, $p->Y);
1379          return $r;
1380      }
1381  
1382      /**
1383       * @internal You should not use this directly from another application
1384       *
1385       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P2
1386       */
1387      public static function ge_p2_0()
1388      {
1389          return new ParagonIE_Sodium_Core_Curve25519_Ge_P2(
1390              self::fe_0(),
1391              self::fe_1(),
1392              self::fe_1()
1393          );
1394      }
1395  
1396      /**
1397       * @internal You should not use this directly from another application
1398       *
1399       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P2 $p
1400       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
1401       */
1402      public static function ge_p2_dbl(ParagonIE_Sodium_Core_Curve25519_Ge_P2 $p)
1403      {
1404          $r = new ParagonIE_Sodium_Core_Curve25519_Ge_P1p1();
1405  
1406          $r->X = self::fe_sq($p->X);
1407          $r->Z = self::fe_sq($p->Y);
1408          $r->T = self::fe_sq2($p->Z);
1409          $r->Y = self::fe_add($p->X, $p->Y);
1410          $t0   = self::fe_sq($r->Y);
1411          $r->Y = self::fe_add($r->Z, $r->X);
1412          $r->Z = self::fe_sub($r->Z, $r->X);
1413          $r->X = self::fe_sub($t0, $r->Y);
1414          $r->T = self::fe_sub($r->T, $r->Z);
1415  
1416          return $r;
1417      }
1418  
1419      /**
1420       * @internal You should not use this directly from another application
1421       *
1422       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
1423       */
1424      public static function ge_p3_0()
1425      {
1426          return new ParagonIE_Sodium_Core_Curve25519_Ge_P3(
1427              self::fe_0(),
1428              self::fe_1(),
1429              self::fe_1(),
1430              self::fe_0()
1431          );
1432      }
1433  
1434      /**
1435       * @internal You should not use this directly from another application
1436       *
1437       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
1438       * @return ParagonIE_Sodium_Core_Curve25519_Ge_Cached
1439       */
1440      public static function ge_p3_to_cached(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p)
1441      {
1442          static $d2 = null;
1443          if ($d2 === null) {
1444              $d2 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d2);
1445          }
1446          /** @var ParagonIE_Sodium_Core_Curve25519_Fe $d2 */
1447          $r = new ParagonIE_Sodium_Core_Curve25519_Ge_Cached();
1448          $r->YplusX = self::fe_add($p->Y, $p->X);
1449          $r->YminusX = self::fe_sub($p->Y, $p->X);
1450          $r->Z = self::fe_copy($p->Z);
1451          $r->T2d = self::fe_mul($p->T, $d2);
1452          return $r;
1453      }
1454  
1455      /**
1456       * @internal You should not use this directly from another application
1457       *
1458       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
1459       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P2
1460       */
1461      public static function ge_p3_to_p2(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p)
1462      {
1463          return new ParagonIE_Sodium_Core_Curve25519_Ge_P2(
1464              self::fe_copy($p->X),
1465              self::fe_copy($p->Y),
1466              self::fe_copy($p->Z)
1467          );
1468      }
1469  
1470      /**
1471       * @internal You should not use this directly from another application
1472       *
1473       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h
1474       * @return string
1475       * @throws SodiumException
1476       * @throws TypeError
1477       */
1478      public static function ge_p3_tobytes(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h)
1479      {
1480          $recip = self::fe_invert($h->Z);
1481          $x = self::fe_mul($h->X, $recip);
1482          $y = self::fe_mul($h->Y, $recip);
1483          $s = self::fe_tobytes($y);
1484          $s[31] = self::intToChr(
1485              self::chrToInt($s[31]) ^ (self::fe_isnegative($x) << 7)
1486          );
1487          return $s;
1488      }
1489  
1490      /**
1491       * @internal You should not use this directly from another application
1492       *
1493       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
1494       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
1495       */
1496      public static function ge_p3_dbl(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p)
1497      {
1498          $q = self::ge_p3_to_p2($p);
1499          return self::ge_p2_dbl($q);
1500      }
1501  
1502      /**
1503       * @return ParagonIE_Sodium_Core_Curve25519_Ge_Precomp
1504       */
1505      public static function ge_precomp_0()
1506      {
1507          return new ParagonIE_Sodium_Core_Curve25519_Ge_Precomp(
1508              self::fe_1(),
1509              self::fe_1(),
1510              self::fe_0()
1511          );
1512      }
1513  
1514      /**
1515       * @internal You should not use this directly from another application
1516       *
1517       * @param int $b
1518       * @param int $c
1519       * @return int
1520       */
1521      public static function equal($b, $c)
1522      {
1523          return (int) ((($b ^ $c) - 1) >> 31) & 1;
1524      }
1525  
1526      /**
1527       * @internal You should not use this directly from another application
1528       *
1529       * @param int|string $char
1530       * @return int (1 = yes, 0 = no)
1531       * @throws SodiumException
1532       * @throws TypeError
1533       */
1534      public static function negative($char)
1535      {
1536          if (is_int($char)) {
1537              return ($char >> 63) & 1;
1538          }
1539          $x = self::chrToInt(self::substr($char, 0, 1));
1540          return (int) ($x >> 63);
1541      }
1542  
1543      /**
1544       * Conditional move
1545       *
1546       * @internal You should not use this directly from another application
1547       *
1548       * @param ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $t
1549       * @param ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $u
1550       * @param int $b
1551       * @return ParagonIE_Sodium_Core_Curve25519_Ge_Precomp
1552       */
1553      public static function cmov(
1554          ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $t,
1555          ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $u,
1556          $b
1557      ) {
1558          if (!is_int($b)) {
1559              throw new InvalidArgumentException('Expected an integer.');
1560          }
1561          return new ParagonIE_Sodium_Core_Curve25519_Ge_Precomp(
1562              self::fe_cmov($t->yplusx,  $u->yplusx,  $b),
1563              self::fe_cmov($t->yminusx, $u->yminusx, $b),
1564              self::fe_cmov($t->xy2d,    $u->xy2d,    $b)
1565          );
1566      }
1567  
1568      /**
1569       * @param ParagonIE_Sodium_Core_Curve25519_Ge_Cached $t
1570       * @param ParagonIE_Sodium_Core_Curve25519_Ge_Cached $u
1571       * @param int $b
1572       * @return ParagonIE_Sodium_Core_Curve25519_Ge_Cached
1573       */
1574      public static function ge_cmov_cached(
1575          ParagonIE_Sodium_Core_Curve25519_Ge_Cached $t,
1576          ParagonIE_Sodium_Core_Curve25519_Ge_Cached $u,
1577          $b
1578      ) {
1579          $b &= 1;
1580          $ret = new ParagonIE_Sodium_Core_Curve25519_Ge_Cached();
1581          $ret->YplusX  = self::fe_cmov($t->YplusX,  $u->YplusX,  $b);
1582          $ret->YminusX = self::fe_cmov($t->YminusX, $u->YminusX, $b);
1583          $ret->Z       = self::fe_cmov($t->Z,       $u->Z,       $b);
1584          $ret->T2d     = self::fe_cmov($t->T2d,     $u->T2d,     $b);
1585          return $ret;
1586      }
1587  
1588      /**
1589       * @param ParagonIE_Sodium_Core_Curve25519_Ge_Cached[] $cached
1590       * @param int $b
1591       * @return ParagonIE_Sodium_Core_Curve25519_Ge_Cached
1592       * @throws SodiumException
1593       */
1594      public static function ge_cmov8_cached(array $cached, $b)
1595      {
1596          // const unsigned char bnegative = negative(b);
1597          // const unsigned char babs      = b - (((-bnegative) & b) * ((signed char) 1 << 1));
1598          $bnegative = self::negative($b);
1599          $babs = $b - (((-$bnegative) & $b) << 1);
1600  
1601          // ge25519_cached_0(t);
1602          $t = new ParagonIE_Sodium_Core_Curve25519_Ge_Cached(
1603              self::fe_1(),
1604              self::fe_1(),
1605              self::fe_1(),
1606              self::fe_0()
1607          );
1608  
1609          // ge25519_cmov_cached(t, &cached[0], equal(babs, 1));
1610          // ge25519_cmov_cached(t, &cached[1], equal(babs, 2));
1611          // ge25519_cmov_cached(t, &cached[2], equal(babs, 3));
1612          // ge25519_cmov_cached(t, &cached[3], equal(babs, 4));
1613          // ge25519_cmov_cached(t, &cached[4], equal(babs, 5));
1614          // ge25519_cmov_cached(t, &cached[5], equal(babs, 6));
1615          // ge25519_cmov_cached(t, &cached[6], equal(babs, 7));
1616          // ge25519_cmov_cached(t, &cached[7], equal(babs, 8));
1617          for ($x = 0; $x < 8; ++$x) {
1618              $t = self::ge_cmov_cached($t, $cached[$x], self::equal($babs, $x + 1));
1619          }
1620  
1621          // fe25519_copy(minust.YplusX, t->YminusX);
1622          // fe25519_copy(minust.YminusX, t->YplusX);
1623          // fe25519_copy(minust.Z, t->Z);
1624          // fe25519_neg(minust.T2d, t->T2d);
1625          $minust = new ParagonIE_Sodium_Core_Curve25519_Ge_Cached(
1626              self::fe_copy($t->YminusX),
1627              self::fe_copy($t->YplusX),
1628              self::fe_copy($t->Z),
1629              self::fe_neg($t->T2d)
1630          );
1631          return self::ge_cmov_cached($t, $minust, $bnegative);
1632      }
1633  
1634      /**
1635       * @internal You should not use this directly from another application
1636       *
1637       * @param int $pos
1638       * @param int $b
1639       * @return ParagonIE_Sodium_Core_Curve25519_Ge_Precomp
1640       * @throws SodiumException
1641       * @throws TypeError
1642       * @psalm-suppress MixedArgument
1643       * @psalm-suppress MixedArrayAccess
1644       * @psalm-suppress MixedArrayOffset
1645       */
1646      public static function ge_select($pos = 0, $b = 0)
1647      {
1648          static $base = null;
1649          if ($base === null) {
1650              $base = array();
1651              /** @var int $i */
1652              foreach (self::$base as $i => $bas) {
1653                  for ($j = 0; $j < 8; ++$j) {
1654                      $base[$i][$j] = new ParagonIE_Sodium_Core_Curve25519_Ge_Precomp(
1655                          ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($bas[$j][0]),
1656                          ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($bas[$j][1]),
1657                          ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($bas[$j][2])
1658                      );
1659                  }
1660              }
1661          }
1662          /** @var array<int, array<int, ParagonIE_Sodium_Core_Curve25519_Ge_Precomp>> $base */
1663          if (!is_int($pos)) {
1664              throw new InvalidArgumentException('Position must be an integer');
1665          }
1666          if ($pos < 0 || $pos > 31) {
1667              throw new RangeException('Position is out of range [0, 31]');
1668          }
1669  
1670          $bnegative = self::negative($b);
1671          $babs = $b - (((-$bnegative) & $b) << 1);
1672  
1673          $t = self::ge_precomp_0();
1674          for ($i = 0; $i < 8; ++$i) {
1675              $t = self::cmov(
1676                  $t,
1677                  $base[$pos][$i],
1678                  self::equal($babs, $i + 1)
1679              );
1680          }
1681          $minusT = new ParagonIE_Sodium_Core_Curve25519_Ge_Precomp(
1682              self::fe_copy($t->yminusx),
1683              self::fe_copy($t->yplusx),
1684              self::fe_neg($t->xy2d)
1685          );
1686          return self::cmov($t, $minusT, $bnegative);
1687      }
1688  
1689      /**
1690       * Subtract two group elements.
1691       *
1692       * r = p - q
1693       *
1694       * @internal You should not use this directly from another application
1695       *
1696       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
1697       * @param ParagonIE_Sodium_Core_Curve25519_Ge_Cached $q
1698       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
1699       */
1700      public static function ge_sub(
1701          ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p,
1702          ParagonIE_Sodium_Core_Curve25519_Ge_Cached $q
1703      ) {
1704          $r = new ParagonIE_Sodium_Core_Curve25519_Ge_P1p1();
1705  
1706          $r->X = self::fe_add($p->Y, $p->X);
1707          $r->Y = self::fe_sub($p->Y, $p->X);
1708          $r->Z = self::fe_mul($r->X, $q->YminusX);
1709          $r->Y = self::fe_mul($r->Y, $q->YplusX);
1710          $r->T = self::fe_mul($q->T2d, $p->T);
1711          $r->X = self::fe_mul($p->Z, $q->Z);
1712          $t0 = self::fe_add($r->X, $r->X);
1713          $r->X = self::fe_sub($r->Z, $r->Y);
1714          $r->Y = self::fe_add($r->Z, $r->Y);
1715          $r->Z = self::fe_sub($t0, $r->T);
1716          $r->T = self::fe_add($t0, $r->T);
1717  
1718          return $r;
1719      }
1720  
1721      /**
1722       * Convert a group element to a byte string.
1723       *
1724       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P2 $h
1725       * @return string
1726       * @throws SodiumException
1727       * @throws TypeError
1728       */
1729      public static function ge_tobytes(ParagonIE_Sodium_Core_Curve25519_Ge_P2 $h)
1730      {
1731          $recip = self::fe_invert($h->Z);
1732          $x = self::fe_mul($h->X, $recip);
1733          $y = self::fe_mul($h->Y, $recip);
1734          $s = self::fe_tobytes($y);
1735          $s[31] = self::intToChr(
1736              self::chrToInt($s[31]) ^ (self::fe_isnegative($x) << 7)
1737          );
1738          return $s;
1739      }
1740  
1741      /**
1742       * @internal You should not use this directly from another application
1743       *
1744       * @param string $a
1745       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A
1746       * @param string $b
1747       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P2
1748       * @throws SodiumException
1749       * @throws TypeError
1750       * @psalm-suppress MixedArgument
1751       * @psalm-suppress MixedArrayAccess
1752       */
1753      public static function ge_double_scalarmult_vartime(
1754          $a,
1755          ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A,
1756          $b
1757      ) {
1758          /** @var array<int, ParagonIE_Sodium_Core_Curve25519_Ge_Cached> $Ai */
1759          $Ai = array();
1760  
1761          /** @var array<int, ParagonIE_Sodium_Core_Curve25519_Ge_Precomp> $Bi */
1762          static $Bi = array();
1763          if (!$Bi) {
1764              for ($i = 0; $i < 8; ++$i) {
1765                  $Bi[$i] = new ParagonIE_Sodium_Core_Curve25519_Ge_Precomp(
1766                      ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$base2[$i][0]),
1767                      ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$base2[$i][1]),
1768                      ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$base2[$i][2])
1769                  );
1770              }
1771          }
1772          for ($i = 0; $i < 8; ++$i) {
1773              $Ai[$i] = new ParagonIE_Sodium_Core_Curve25519_Ge_Cached(
1774                  self::fe_0(),
1775                  self::fe_0(),
1776                  self::fe_0(),
1777                  self::fe_0()
1778              );
1779          }
1780  
1781          # slide(aslide,a);
1782          # slide(bslide,b);
1783          /** @var array<int, int> $aslide */
1784          $aslide = self::slide($a);
1785          /** @var array<int, int> $bslide */
1786          $bslide = self::slide($b);
1787  
1788          # ge_p3_to_cached(&Ai[0],A);
1789          # ge_p3_dbl(&t,A); ge_p1p1_to_p3(&A2,&t);
1790          $Ai[0] = self::ge_p3_to_cached($A);
1791          $t = self::ge_p3_dbl($A);
1792          $A2 = self::ge_p1p1_to_p3($t);
1793  
1794          # ge_add(&t,&A2,&Ai[0]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[1],&u);
1795          # ge_add(&t,&A2,&Ai[1]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[2],&u);
1796          # ge_add(&t,&A2,&Ai[2]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[3],&u);
1797          # ge_add(&t,&A2,&Ai[3]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[4],&u);
1798          # ge_add(&t,&A2,&Ai[4]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[5],&u);
1799          # ge_add(&t,&A2,&Ai[5]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[6],&u);
1800          # ge_add(&t,&A2,&Ai[6]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[7],&u);
1801          for ($i = 0; $i < 7; ++$i) {
1802              $t = self::ge_add($A2, $Ai[$i]);
1803              $u = self::ge_p1p1_to_p3($t);
1804              $Ai[$i + 1] = self::ge_p3_to_cached($u);
1805          }
1806  
1807          # ge_p2_0(r);
1808          $r = self::ge_p2_0();
1809  
1810          # for (i = 255;i >= 0;--i) {
1811          #     if (aslide[i] || bslide[i]) break;
1812          # }
1813          $i = 255;
1814          for (; $i >= 0; --$i) {
1815              if ($aslide[$i] || $bslide[$i]) {
1816                  break;
1817              }
1818          }
1819  
1820          # for (;i >= 0;--i) {
1821          for (; $i >= 0; --$i) {
1822              # ge_p2_dbl(&t,r);
1823              $t = self::ge_p2_dbl($r);
1824  
1825              # if (aslide[i] > 0) {
1826              if ($aslide[$i] > 0) {
1827                  # ge_p1p1_to_p3(&u,&t);
1828                  # ge_add(&t,&u,&Ai[aslide[i]/2]);
1829                  $u = self::ge_p1p1_to_p3($t);
1830                  $t = self::ge_add(
1831                      $u,
1832                      $Ai[(int) floor($aslide[$i] / 2)]
1833                  );
1834              # } else if (aslide[i] < 0) {
1835              } elseif ($aslide[$i] < 0) {
1836                  # ge_p1p1_to_p3(&u,&t);
1837                  # ge_sub(&t,&u,&Ai[(-aslide[i])/2]);
1838                  $u = self::ge_p1p1_to_p3($t);
1839                  $t = self::ge_sub(
1840                      $u,
1841                      $Ai[(int) floor(-$aslide[$i] / 2)]
1842                  );
1843              }
1844  
1845              # if (bslide[i] > 0) {
1846              if ($bslide[$i] > 0) {
1847                  /** @var int $index */
1848                  $index = (int) floor($bslide[$i] / 2);
1849                  # ge_p1p1_to_p3(&u,&t);
1850                  # ge_madd(&t,&u,&Bi[bslide[i]/2]);
1851                  $u = self::ge_p1p1_to_p3($t);
1852                  $t = self::ge_madd($t, $u, $Bi[$index]);
1853              # } else if (bslide[i] < 0) {
1854              } elseif ($bslide[$i] < 0) {
1855                  /** @var int $index */
1856                  $index = (int) floor(-$bslide[$i] / 2);
1857                  # ge_p1p1_to_p3(&u,&t);
1858                  # ge_msub(&t,&u,&Bi[(-bslide[i])/2]);
1859                  $u = self::ge_p1p1_to_p3($t);
1860                  $t = self::ge_msub($t, $u, $Bi[$index]);
1861              }
1862              # ge_p1p1_to_p2(r,&t);
1863              $r = self::ge_p1p1_to_p2($t);
1864          }
1865          return $r;
1866      }
1867  
1868      /**
1869       * @internal You should not use this directly from another application
1870       *
1871       * @param string $a
1872       * @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
1873       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
1874       * @throws SodiumException
1875       * @throws TypeError
1876       * @psalm-suppress MixedAssignment
1877       * @psalm-suppress MixedOperand
1878       */
1879      public static function ge_scalarmult($a, $p)
1880      {
1881          $e = array_fill(0, 64, 0);
1882  
1883          /** @var ParagonIE_Sodium_Core_Curve25519_Ge_Cached[] $pi */
1884          $pi = array();
1885  
1886          //        ge25519_p3_to_cached(&pi[1 - 1], p);   /* p */
1887          $pi[0] = self::ge_p3_to_cached($p);
1888  
1889          //        ge25519_p3_dbl(&t2, p);
1890          //        ge25519_p1p1_to_p3(&p2, &t2);
1891          //        ge25519_p3_to_cached(&pi[2 - 1], &p2); /* 2p = 2*p */
1892          $t2 = self::ge_p3_dbl($p);
1893          $p2 = self::ge_p1p1_to_p3($t2);
1894          $pi[1] = self::ge_p3_to_cached($p2);
1895  
1896          //        ge25519_add_cached(&t3, p, &pi[2 - 1]);
1897          //        ge25519_p1p1_to_p3(&p3, &t3);
1898          //        ge25519_p3_to_cached(&pi[3 - 1], &p3); /* 3p = 2p+p */
1899          $t3 = self::ge_add($p, $pi[1]);
1900          $p3 = self::ge_p1p1_to_p3($t3);
1901          $pi[2] = self::ge_p3_to_cached($p3);
1902  
1903          //        ge25519_p3_dbl(&t4, &p2);
1904          //        ge25519_p1p1_to_p3(&p4, &t4);
1905          //        ge25519_p3_to_cached(&pi[4 - 1], &p4); /* 4p = 2*2p */
1906          $t4 = self::ge_p3_dbl($p2);
1907          $p4 = self::ge_p1p1_to_p3($t4);
1908          $pi[3] = self::ge_p3_to_cached($p4);
1909  
1910          //        ge25519_add_cached(&t5, p, &pi[4 - 1]);
1911          //        ge25519_p1p1_to_p3(&p5, &t5);
1912          //        ge25519_p3_to_cached(&pi[5 - 1], &p5); /* 5p = 4p+p */
1913          $t5 = self::ge_add($p, $pi[3]);
1914          $p5 = self::ge_p1p1_to_p3($t5);
1915          $pi[4] = self::ge_p3_to_cached($p5);
1916  
1917          //        ge25519_p3_dbl(&t6, &p3);
1918          //        ge25519_p1p1_to_p3(&p6, &t6);
1919          //        ge25519_p3_to_cached(&pi[6 - 1], &p6); /* 6p = 2*3p */
1920          $t6 = self::ge_p3_dbl($p3);
1921          $p6 = self::ge_p1p1_to_p3($t6);
1922          $pi[5] = self::ge_p3_to_cached($p6);
1923  
1924          //        ge25519_add_cached(&t7, p, &pi[6 - 1]);
1925          //        ge25519_p1p1_to_p3(&p7, &t7);
1926          //        ge25519_p3_to_cached(&pi[7 - 1], &p7); /* 7p = 6p+p */
1927          $t7 = self::ge_add($p, $pi[5]);
1928          $p7 = self::ge_p1p1_to_p3($t7);
1929          $pi[6] = self::ge_p3_to_cached($p7);
1930  
1931          //        ge25519_p3_dbl(&t8, &p4);
1932          //        ge25519_p1p1_to_p3(&p8, &t8);
1933          //        ge25519_p3_to_cached(&pi[8 - 1], &p8); /* 8p = 2*4p */
1934          $t8 = self::ge_p3_dbl($p4);
1935          $p8 = self::ge_p1p1_to_p3($t8);
1936          $pi[7] = self::ge_p3_to_cached($p8);
1937  
1938  
1939          //        for (i = 0; i < 32; ++i) {
1940          //            e[2 * i + 0] = (a[i] >> 0) & 15;
1941          //            e[2 * i + 1] = (a[i] >> 4) & 15;
1942          //        }
1943          for ($i = 0; $i < 32; ++$i) {
1944              $e[($i << 1)    ] =  self::chrToInt($a[$i]) & 15;
1945              $e[($i << 1) + 1] = (self::chrToInt($a[$i]) >> 4) & 15;
1946          }
1947          //        /* each e[i] is between 0 and 15 */
1948          //        /* e[63] is between 0 and 7 */
1949  
1950          //        carry = 0;
1951          //        for (i = 0; i < 63; ++i) {
1952          //            e[i] += carry;
1953          //            carry = e[i] + 8;
1954          //            carry >>= 4;
1955          //            e[i] -= carry * ((signed char) 1 << 4);
1956          //        }
1957          $carry = 0;
1958          for ($i = 0; $i < 63; ++$i) {
1959              $e[$i] += $carry;
1960              $carry = $e[$i] + 8;
1961              $carry >>= 4;
1962              $e[$i] -= $carry << 4;
1963          }
1964          //        e[63] += carry;
1965          //        /* each e[i] is between -8 and 8 */
1966          $e[63] += $carry;
1967  
1968          //        ge25519_p3_0(h);
1969          $h = self::ge_p3_0();
1970  
1971          //        for (i = 63; i != 0; i--) {
1972          for ($i = 63; $i != 0; --$i) {
1973              // ge25519_cmov8_cached(&t, pi, e[i]);
1974              $t = self::ge_cmov8_cached($pi, $e[$i]);
1975              // ge25519_add_cached(&r, h, &t);
1976              $r = self::ge_add($h, $t);
1977  
1978              // ge25519_p1p1_to_p2(&s, &r);
1979              // ge25519_p2_dbl(&r, &s);
1980              // ge25519_p1p1_to_p2(&s, &r);
1981              // ge25519_p2_dbl(&r, &s);
1982              // ge25519_p1p1_to_p2(&s, &r);
1983              // ge25519_p2_dbl(&r, &s);
1984              // ge25519_p1p1_to_p2(&s, &r);
1985              // ge25519_p2_dbl(&r, &s);
1986              $s = self::ge_p1p1_to_p2($r);
1987              $r = self::ge_p2_dbl($s);
1988              $s = self::ge_p1p1_to_p2($r);
1989              $r = self::ge_p2_dbl($s);
1990              $s = self::ge_p1p1_to_p2($r);
1991              $r = self::ge_p2_dbl($s);
1992              $s = self::ge_p1p1_to_p2($r);
1993              $r = self::ge_p2_dbl($s);
1994  
1995              // ge25519_p1p1_to_p3(h, &r);  /* *16 */
1996              $h = self::ge_p1p1_to_p3($r); /* *16 */
1997          }
1998  
1999          //        ge25519_cmov8_cached(&t, pi, e[i]);
2000          //        ge25519_add_cached(&r, h, &t);
2001          //        ge25519_p1p1_to_p3(h, &r);
2002          $t = self::ge_cmov8_cached($pi, $e[0]);
2003          $r = self::ge_add($h, $t);
2004          return self::ge_p1p1_to_p3($r);
2005      }
2006  
2007      /**
2008       * @internal You should not use this directly from another application
2009       *
2010       * @param string $a
2011       * @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
2012       * @throws SodiumException
2013       * @throws TypeError
2014       * @psalm-suppress MixedAssignment
2015       * @psalm-suppress MixedOperand
2016       */
2017      public static function ge_scalarmult_base($a)
2018      {
2019          /** @var array<int, int> $e */
2020          $e = array();
2021          $r = new ParagonIE_Sodium_Core_Curve25519_Ge_P1p1();
2022  
2023          for ($i = 0; $i < 32; ++$i) {
2024              $dbl = (int) $i << 1;
2025              $e[$dbl] = (int) self::chrToInt($a[$i]) & 15;
2026              $e[$dbl + 1] = (int) (self::chrToInt($a[$i]) >> 4) & 15;
2027          }
2028  
2029          $carry = 0;
2030          for ($i = 0; $i < 63; ++$i) {
2031              $e[$i] += $carry;
2032              $carry = $e[$i] + 8;
2033              $carry >>= 4;
2034              $e[$i] -= $carry << 4;
2035          }
2036          $e[63] += (int) $carry;
2037  
2038          $h = self::ge_p3_0();
2039  
2040          for ($i = 1; $i < 64; $i += 2) {
2041              $t = self::ge_select((int) floor($i / 2), (int) $e[$i]);
2042              $r = self::ge_madd($r, $h, $t);
2043              $h = self::ge_p1p1_to_p3($r);
2044          }
2045  
2046          $r = self::ge_p3_dbl($h);
2047  
2048          $s = self::ge_p1p1_to_p2($r);
2049          $r = self::ge_p2_dbl($s);
2050          $s = self::ge_p1p1_to_p2($r);
2051          $r = self::ge_p2_dbl($s);
2052          $s = self::ge_p1p1_to_p2($r);
2053          $r = self::ge_p2_dbl($s);
2054  
2055          $h = self::ge_p1p1_to_p3($r);
2056  
2057          for ($i = 0; $i < 64; $i += 2) {
2058              $t = self::ge_select($i >> 1, (int) $e[$i]);
2059              $r = self::ge_madd($r, $h, $t);
2060              $h = self::ge_p1p1_to_p3($r);
2061          }
2062          return $h;
2063      }
2064  
2065      /**
2066       * Calculates (ab + c) mod l
2067       * where l = 2^252 + 27742317777372353535851937790883648493
2068       *
2069       * @internal You should not use this directly from another application
2070       *
2071       * @param string $a
2072       * @param string $b
2073       * @param string $c
2074       * @return string
2075       * @throws TypeError
2076       */
2077      public static function sc_muladd($a, $b, $c)
2078      {
2079          $a0 = 2097151 & self::load_3(self::substr($a, 0, 3));
2080          $a1 = 2097151 & (self::load_4(self::substr($a, 2, 4)) >> 5);
2081          $a2 = 2097151 & (self::load_3(self::substr($a, 5, 3)) >> 2);
2082          $a3 = 2097151 & (self::load_4(self::substr($a, 7, 4)) >> 7);
2083          $a4 = 2097151 & (self::load_4(self::substr($a, 10, 4)) >> 4);
2084          $a5 = 2097151 & (self::load_3(self::substr($a, 13, 3)) >> 1);
2085          $a6 = 2097151 & (self::load_4(self::substr($a, 15, 4)) >> 6);
2086          $a7 = 2097151 & (self::load_3(self::substr($a, 18, 3)) >> 3);
2087          $a8 = 2097151 & self::load_3(self::substr($a, 21, 3));
2088          $a9 = 2097151 & (self::load_4(self::substr($a, 23, 4)) >> 5);
2089          $a10 = 2097151 & (self::load_3(self::substr($a, 26, 3)) >> 2);
2090          $a11 = (self::load_4(self::substr($a, 28, 4)) >> 7);
2091  
2092          $b0 = 2097151 & self::load_3(self::substr($b, 0, 3));
2093          $b1 = 2097151 & (self::load_4(self::substr($b, 2, 4)) >> 5);
2094          $b2 = 2097151 & (self::load_3(self::substr($b, 5, 3)) >> 2);
2095          $b3 = 2097151 & (self::load_4(self::substr($b, 7, 4)) >> 7);
2096          $b4 = 2097151 & (self::load_4(self::substr($b, 10, 4)) >> 4);
2097          $b5 = 2097151 & (self::load_3(self::substr($b, 13, 3)) >> 1);
2098          $b6 = 2097151 & (self::load_4(self::substr($b, 15, 4)) >> 6);
2099          $b7 = 2097151 & (self::load_3(self::substr($b, 18, 3)) >> 3);
2100          $b8 = 2097151 & self::load_3(self::substr($b, 21, 3));
2101          $b9 = 2097151 & (self::load_4(self::substr($b, 23, 4)) >> 5);
2102          $b10 = 2097151 & (self::load_3(self::substr($b, 26, 3)) >> 2);
2103          $b11 = (self::load_4(self::substr($b, 28, 4)) >> 7);
2104  
2105          $c0 = 2097151 & self::load_3(self::substr($c, 0, 3));
2106          $c1 = 2097151 & (self::load_4(self::substr($c, 2, 4)) >> 5);
2107          $c2 = 2097151 & (self::load_3(self::substr($c, 5, 3)) >> 2);
2108          $c3 = 2097151 & (self::load_4(self::substr($c, 7, 4)) >> 7);
2109          $c4 = 2097151 & (self::load_4(self::substr($c, 10, 4)) >> 4);
2110          $c5 = 2097151 & (self::load_3(self::substr($c, 13, 3)) >> 1);
2111          $c6 = 2097151 & (self::load_4(self::substr($c, 15, 4)) >> 6);
2112          $c7 = 2097151 & (self::load_3(self::substr($c, 18, 3)) >> 3);
2113          $c8 = 2097151 & self::load_3(self::substr($c, 21, 3));
2114          $c9 = 2097151 & (self::load_4(self::substr($c, 23, 4)) >> 5);
2115          $c10 = 2097151 & (self::load_3(self::substr($c, 26, 3)) >> 2);
2116          $c11 = (self::load_4(self::substr($c, 28, 4)) >> 7);
2117  
2118          /* Can't really avoid the pyramid here: */
2119          $s0 = $c0 + self::mul($a0, $b0, 24);
2120          $s1 = $c1 + self::mul($a0, $b1, 24) + self::mul($a1, $b0, 24);
2121          $s2 = $c2 + self::mul($a0, $b2, 24) + self::mul($a1, $b1, 24) + self::mul($a2, $b0, 24);
2122          $s3 = $c3 + self::mul($a0, $b3, 24) + self::mul($a1, $b2, 24) + self::mul($a2, $b1, 24) + self::mul($a3, $b0, 24);
2123          $s4 = $c4 + self::mul($a0, $b4, 24) + self::mul($a1, $b3, 24) + self::mul($a2, $b2, 24) + self::mul($a3, $b1, 24) +
2124                 self::mul($a4, $b0, 24);
2125          $s5 = $c5 + self::mul($a0, $b5, 24) + self::mul($a1, $b4, 24) + self::mul($a2, $b3, 24) + self::mul($a3, $b2, 24) +
2126                 self::mul($a4, $b1, 24) + self::mul($a5, $b0, 24);
2127          $s6 = $c6 + self::mul($a0, $b6, 24) + self::mul($a1, $b5, 24) + self::mul($a2, $b4, 24) + self::mul($a3, $b3, 24) +
2128                 self::mul($a4, $b2, 24) + self::mul($a5, $b1, 24) + self::mul($a6, $b0, 24);
2129          $s7 = $c7 + self::mul($a0, $b7, 24) + self::mul($a1, $b6, 24) + self::mul($a2, $b5, 24) + self::mul($a3, $b4, 24) +
2130                 self::mul($a4, $b3, 24) + self::mul($a5, $b2, 24) + self::mul($a6, $b1, 24) + self::mul($a7, $b0, 24);
2131          $s8 = $c8 + self::mul($a0, $b8, 24) + self::mul($a1, $b7, 24) + self::mul($a2, $b6, 24) + self::mul($a3, $b5, 24) +
2132                 self::mul($a4, $b4, 24) + self::mul($a5, $b3, 24) + self::mul($a6, $b2, 24) + self::mul($a7, $b1, 24) +
2133                 self::mul($a8, $b0, 24);
2134          $s9 = $c9 + self::mul($a0, $b9, 24) + self::mul($a1, $b8, 24) + self::mul($a2, $b7, 24) + self::mul($a3, $b6, 24) +
2135                 self::mul($a4, $b5, 24) + self::mul($a5, $b4, 24) + self::mul($a6, $b3, 24) + self::mul($a7, $b2, 24) +
2136                 self::mul($a8, $b1, 24) + self::mul($a9, $b0, 24);
2137          $s10 = $c10 + self::mul($a0, $b10, 24) + self::mul($a1, $b9, 24) + self::mul($a2, $b8, 24) + self::mul($a3, $b7, 24) +
2138                 self::mul($a4, $b6, 24) + self::mul($a5, $b5, 24) + self::mul($a6, $b4, 24) + self::mul($a7, $b3, 24) +
2139                 self::mul($a8, $b2, 24) + self::mul($a9, $b1, 24) + self::mul($a10, $b0, 24);
2140          $s11 = $c11 + self::mul($a0, $b11, 24) + self::mul($a1, $b10, 24) + self::mul($a2, $b9, 24) + self::mul($a3, $b8, 24) +
2141                 self::mul($a4, $b7, 24) + self::mul($a5, $b6, 24) + self::mul($a6, $b5, 24) + self::mul($a7, $b4, 24) +
2142                 self::mul($a8, $b3, 24) + self::mul($a9, $b2, 24) + self::mul($a10, $b1, 24) + self::mul($a11, $b0, 24);
2143          $s12 = self::mul($a1, $b11, 24) + self::mul($a2, $b10, 24) + self::mul($a3, $b9, 24) + self::mul($a4, $b8, 24) +
2144                 self::mul($a5, $b7, 24) + self::mul($a6, $b6, 24) + self::mul($a7, $b5, 24) + self::mul($a8, $b4, 24) +
2145                 self::mul($a9, $b3, 24) + self::mul($a10, $b2, 24) + self::mul($a11, $b1, 24);
2146          $s13 = self::mul($a2, $b11, 24) + self::mul($a3, $b10, 24) + self::mul($a4, $b9, 24) + self::mul($a5, $b8, 24) +
2147                 self::mul($a6, $b7, 24) + self::mul($a7, $b6, 24) + self::mul($a8, $b5, 24) + self::mul($a9, $b4, 24) +
2148                 self::mul($a10, $b3, 24) + self::mul($a11, $b2, 24);
2149          $s14 = self::mul($a3, $b11, 24) + self::mul($a4, $b10, 24) + self::mul($a5, $b9, 24) + self::mul($a6, $b8, 24) +
2150                 self::mul($a7, $b7, 24) + self::mul($a8, $b6, 24) + self::mul($a9, $b5, 24) + self::mul($a10, $b4, 24) +
2151                 self::mul($a11, $b3, 24);
2152          $s15 = self::mul($a4, $b11, 24) + self::mul($a5, $b10, 24) + self::mul($a6, $b9, 24) + self::mul($a7, $b8, 24) +
2153                 self::mul($a8, $b7, 24) + self::mul($a9, $b6, 24) + self::mul($a10, $b5, 24) + self::mul($a11, $b4, 24);
2154          $s16 = self::mul($a5, $b11, 24) + self::mul($a6, $b10, 24) + self::mul($a7, $b9, 24) + self::mul($a8, $b8, 24) +
2155                 self::mul($a9, $b7, 24) + self::mul($a10, $b6, 24) + self::mul($a11, $b5, 24);
2156          $s17 = self::mul($a6, $b11, 24) + self::mul($a7, $b10, 24) + self::mul($a8, $b9, 24) + self::mul($a9, $b8, 24) +
2157                 self::mul($a10, $b7, 24) + self::mul($a11, $b6, 24);
2158          $s18 = self::mul($a7, $b11, 24) + self::mul($a8, $b10, 24) + self::mul($a9, $b9, 24) + self::mul($a10, $b8, 24) +
2159                 self::mul($a11, $b7, 24);
2160          $s19 = self::mul($a8, $b11, 24) + self::mul($a9, $b10, 24) + self::mul($a10, $b9, 24) + self::mul($a11, $b8, 24);
2161          $s20 = self::mul($a9, $b11, 24) + self::mul($a10, $b10, 24) + self::mul($a11, $b9, 24);
2162          $s21 = self::mul($a10, $b11, 24) + self::mul($a11, $b10, 24);
2163          $s22 = self::mul($a11, $b11, 24);
2164          $s23 = 0;
2165  
2166          $carry0 = ($s0 + (1 << 20)) >> 21;
2167          $s1 += $carry0;
2168          $s0 -= $carry0 << 21;
2169          $carry2 = ($s2 + (1 << 20)) >> 21;
2170          $s3 += $carry2;
2171          $s2 -= $carry2 << 21;
2172          $carry4 = ($s4 + (1 << 20)) >> 21;
2173          $s5 += $carry4;
2174          $s4 -= $carry4 << 21;
2175          $carry6 = ($s6 + (1 << 20)) >> 21;
2176          $s7 += $carry6;
2177          $s6 -= $carry6 << 21;
2178          $carry8 = ($s8 + (1 << 20)) >> 21;
2179          $s9 += $carry8;
2180          $s8 -= $carry8 << 21;
2181          $carry10 = ($s10 + (1 << 20)) >> 21;
2182          $s11 += $carry10;
2183          $s10 -= $carry10 << 21;
2184          $carry12 = ($s12 + (1 << 20)) >> 21;
2185          $s13 += $carry12;
2186          $s12 -= $carry12 << 21;
2187          $carry14 = ($s14 + (1 << 20)) >> 21;
2188          $s15 += $carry14;
2189          $s14 -= $carry14 << 21;
2190          $carry16 = ($s16 + (1 << 20)) >> 21;
2191          $s17 += $carry16;
2192          $s16 -= $carry16 << 21;
2193          $carry18 = ($s18 + (1 << 20)) >> 21;
2194          $s19 += $carry18;
2195          $s18 -= $carry18 << 21;
2196          $carry20 = ($s20 + (1 << 20)) >> 21;
2197          $s21 += $carry20;
2198          $s20 -= $carry20 << 21;
2199          $carry22 = ($s22 + (1 << 20)) >> 21;
2200          $s23 += $carry22;
2201          $s22 -= $carry22 << 21;
2202  
2203          $carry1 = ($s1 + (1 << 20)) >> 21;
2204          $s2 += $carry1;
2205          $s1 -= $carry1 << 21;
2206          $carry3 = ($s3 + (1 << 20)) >> 21;
2207          $s4 += $carry3;
2208          $s3 -= $carry3 << 21;
2209          $carry5 = ($s5 + (1 << 20)) >> 21;
2210          $s6 += $carry5;
2211          $s5 -= $carry5 << 21;
2212          $carry7 = ($s7 + (1 << 20)) >> 21;
2213          $s8 += $carry7;
2214          $s7 -= $carry7 << 21;
2215          $carry9 = ($s9 + (1 << 20)) >> 21;
2216          $s10 += $carry9;
2217          $s9 -= $carry9 << 21;
2218          $carry11 = ($s11 + (1 << 20)) >> 21;
2219          $s12 += $carry11;
2220          $s11 -= $carry11 << 21;
2221          $carry13 = ($s13 + (1 << 20)) >> 21;
2222          $s14 += $carry13;
2223          $s13 -= $carry13 << 21;
2224          $carry15 = ($s15 + (1 << 20)) >> 21;
2225          $s16 += $carry15;
2226          $s15 -= $carry15 << 21;
2227          $carry17 = ($s17 + (1 << 20)) >> 21;
2228          $s18 += $carry17;
2229          $s17 -= $carry17 << 21;
2230          $carry19 = ($s19 + (1 << 20)) >> 21;
2231          $s20 += $carry19;
2232          $s19 -= $carry19 << 21;
2233          $carry21 = ($s21 + (1 << 20)) >> 21;
2234          $s22 += $carry21;
2235          $s21 -= $carry21 << 21;
2236  
2237          $s11 += self::mul($s23, 666643, 20);
2238          $s12 += self::mul($s23, 470296, 19);
2239          $s13 += self::mul($s23, 654183, 20);
2240          $s14 -= self::mul($s23, 997805, 20);
2241          $s15 += self::mul($s23, 136657, 18);
2242          $s16 -= self::mul($s23, 683901, 20);
2243  
2244          $s10 += self::mul($s22, 666643, 20);
2245          $s11 += self::mul($s22, 470296, 19);
2246          $s12 += self::mul($s22, 654183, 20);
2247          $s13 -= self::mul($s22, 997805, 20);
2248          $s14 += self::mul($s22, 136657, 18);
2249          $s15 -= self::mul($s22, 683901, 20);
2250  
2251          $s9  += self::mul($s21,  666643, 20);
2252          $s10 += self::mul($s21,  470296, 19);
2253          $s11 += self::mul($s21,  654183, 20);
2254          $s12 -= self::mul($s21,  997805, 20);
2255          $s13 += self::mul($s21,  136657, 18);
2256          $s14 -= self::mul($s21,  683901, 20);
2257  
2258          $s8  += self::mul($s20,  666643, 20);
2259          $s9  += self::mul($s20,  470296, 19);
2260          $s10 += self::mul($s20,  654183, 20);
2261          $s11 -= self::mul($s20,  997805, 20);
2262          $s12 += self::mul($s20,  136657, 18);
2263          $s13 -= self::mul($s20,  683901, 20);
2264  
2265          $s7  += self::mul($s19,  666643, 20);
2266          $s8  += self::mul($s19,  470296, 19);
2267          $s9  += self::mul($s19,  654183, 20);
2268          $s10 -= self::mul($s19,  997805, 20);
2269          $s11 += self::mul($s19,  136657, 18);
2270          $s12 -= self::mul($s19,  683901, 20);
2271  
2272          $s6  += self::mul($s18,  666643, 20);
2273          $s7  += self::mul($s18,  470296, 19);
2274          $s8  += self::mul($s18,  654183, 20);
2275          $s9  -= self::mul($s18,  997805, 20);
2276          $s10 += self::mul($s18,  136657, 18);
2277          $s11 -= self::mul($s18,  683901, 20);
2278  
2279          $carry6 = ($s6 + (1 << 20)) >> 21;
2280          $s7 += $carry6;
2281          $s6 -= $carry6 << 21;
2282          $carry8 = ($s8 + (1 << 20)) >> 21;
2283          $s9 += $carry8;
2284          $s8 -= $carry8 << 21;
2285          $carry10 = ($s10 + (1 << 20)) >> 21;
2286          $s11 += $carry10;
2287          $s10 -= $carry10 << 21;
2288          $carry12 = ($s12 + (1 << 20)) >> 21;
2289          $s13 += $carry12;
2290          $s12 -= $carry12 << 21;
2291          $carry14 = ($s14 + (1 << 20)) >> 21;
2292          $s15 += $carry14;
2293          $s14 -= $carry14 << 21;
2294          $carry16 = ($s16 + (1 << 20)) >> 21;
2295          $s17 += $carry16;
2296          $s16 -= $carry16 << 21;
2297  
2298          $carry7 = ($s7 + (1 << 20)) >> 21;
2299          $s8 += $carry7;
2300          $s7 -= $carry7 << 21;
2301          $carry9 = ($s9 + (1 << 20)) >> 21;
2302          $s10 += $carry9;
2303          $s9 -= $carry9 << 21;
2304          $carry11 = ($s11 + (1 << 20)) >> 21;
2305          $s12 += $carry11;
2306          $s11 -= $carry11 << 21;
2307          $carry13 = ($s13 + (1 << 20)) >> 21;
2308          $s14 += $carry13;
2309          $s13 -= $carry13 << 21;
2310          $carry15 = ($s15 + (1 << 20)) >> 21;
2311          $s16 += $carry15;
2312          $s15 -= $carry15 << 21;
2313  
2314          $s5  += self::mul($s17,  666643, 20);
2315          $s6  += self::mul($s17,  470296, 19);
2316          $s7  += self::mul($s17,  654183, 20);
2317          $s8  -= self::mul($s17,  997805, 20);
2318          $s9  += self::mul($s17,  136657, 18);
2319          $s10 -= self::mul($s17,  683901, 20);
2320  
2321          $s4 += self::mul($s16,  666643, 20);
2322          $s5 += self::mul($s16,  470296, 19);
2323          $s6 += self::mul($s16,  654183, 20);
2324          $s7 -= self::mul($s16,  997805, 20);
2325          $s8 += self::mul($s16,  136657, 18);
2326          $s9 -= self::mul($s16,  683901, 20);
2327  
2328          $s3 += self::mul($s15,  666643, 20);
2329          $s4 += self::mul($s15,  470296, 19);
2330          $s5 += self::mul($s15,  654183, 20);
2331          $s6 -= self::mul($s15,  997805, 20);
2332          $s7 += self::mul($s15,  136657, 18);
2333          $s8 -= self::mul($s15,  683901, 20);
2334  
2335          $s2 += self::mul($s14,  666643, 20);
2336          $s3 += self::mul($s14,  470296, 19);
2337          $s4 += self::mul($s14,  654183, 20);
2338          $s5 -= self::mul($s14,  997805, 20);
2339          $s6 += self::mul($s14,  136657, 18);
2340          $s7 -= self::mul($s14,  683901, 20);
2341  
2342          $s1 += self::mul($s13,  666643, 20);
2343          $s2 += self::mul($s13,  470296, 19);
2344          $s3 += self::mul($s13,  654183, 20);
2345          $s4 -= self::mul($s13,  997805, 20);
2346          $s5 += self::mul($s13,  136657, 18);
2347          $s6 -= self::mul($s13,  683901, 20);
2348  
2349          $s0 += self::mul($s12,  666643, 20);
2350          $s1 += self::mul($s12,  470296, 19);
2351          $s2 += self::mul($s12,  654183, 20);
2352          $s3 -= self::mul($s12,  997805, 20);
2353          $s4 += self::mul($s12,  136657, 18);
2354          $s5 -= self::mul($s12,  683901, 20);
2355          $s12 = 0;
2356  
2357          $carry0 = ($s0 + (1 << 20)) >> 21;
2358          $s1 += $carry0;
2359          $s0 -= $carry0 << 21;
2360          $carry2 = ($s2 + (1 << 20)) >> 21;
2361          $s3 += $carry2;
2362          $s2 -= $carry2 << 21;
2363          $carry4 = ($s4 + (1 << 20)) >> 21;
2364          $s5 += $carry4;
2365          $s4 -= $carry4 << 21;
2366          $carry6 = ($s6 + (1 << 20)) >> 21;
2367          $s7 += $carry6;
2368          $s6 -= $carry6 << 21;
2369          $carry8 = ($s8 + (1 << 20)) >> 21;
2370          $s9 += $carry8;
2371          $s8 -= $carry8 << 21;
2372          $carry10 = ($s10 + (1 << 20)) >> 21;
2373          $s11 += $carry10;
2374          $s10 -= $carry10 << 21;
2375  
2376          $carry1 = ($s1 + (1 << 20)) >> 21;
2377          $s2 += $carry1;
2378          $s1 -= $carry1 << 21;
2379          $carry3 = ($s3 + (1 << 20)) >> 21;
2380          $s4 += $carry3;
2381          $s3 -= $carry3 << 21;
2382          $carry5 = ($s5 + (1 << 20)) >> 21;
2383          $s6 += $carry5;
2384          $s5 -= $carry5 << 21;
2385          $carry7 = ($s7 + (1 << 20)) >> 21;
2386          $s8 += $carry7;
2387          $s7 -= $carry7 << 21;
2388          $carry9 = ($s9 + (1 << 20)) >> 21;
2389          $s10 += $carry9;
2390          $s9 -= $carry9 << 21;
2391          $carry11 = ($s11 + (1 << 20)) >> 21;
2392          $s12 += $carry11;
2393          $s11 -= $carry11 << 21;
2394  
2395          $s0 += self::mul($s12,  666643, 20);
2396          $s1 += self::mul($s12,  470296, 19);
2397          $s2 += self::mul($s12,  654183, 20);
2398          $s3 -= self::mul($s12,  997805, 20);
2399          $s4 += self::mul($s12,  136657, 18);
2400          $s5 -= self::mul($s12,  683901, 20);
2401          $s12 = 0;
2402  
2403          $carry0 = $s0 >> 21;
2404          $s1 += $carry0;
2405          $s0 -= $carry0 << 21;
2406          $carry1 = $s1 >> 21;
2407          $s2 += $carry1;
2408          $s1 -= $carry1 << 21;
2409          $carry2 = $s2 >> 21;
2410          $s3 += $carry2;
2411          $s2 -= $carry2 << 21;
2412          $carry3 = $s3 >> 21;
2413          $s4 += $carry3;
2414          $s3 -= $carry3 << 21;
2415          $carry4 = $s4 >> 21;
2416          $s5 += $carry4;
2417          $s4 -= $carry4 << 21;
2418          $carry5 = $s5 >> 21;
2419          $s6 += $carry5;
2420          $s5 -= $carry5 << 21;
2421          $carry6 = $s6 >> 21;
2422          $s7 += $carry6;
2423          $s6 -= $carry6 << 21;
2424          $carry7 = $s7 >> 21;
2425          $s8 += $carry7;
2426          $s7 -= $carry7 << 21;
2427          $carry8 = $s8 >> 21;
2428          $s9 += $carry8;
2429          $s8 -= $carry8 << 21;
2430          $carry9 = $s9 >> 21;
2431          $s10 += $carry9;
2432          $s9 -= $carry9 << 21;
2433          $carry10 = $s10 >> 21;
2434          $s11 += $carry10;
2435          $s10 -= $carry10 << 21;
2436          $carry11 = $s11 >> 21;
2437          $s12 += $carry11;
2438          $s11 -= $carry11 << 21;
2439  
2440          $s0 += self::mul($s12,  666643, 20);
2441          $s1 += self::mul($s12,  470296, 19);
2442          $s2 += self::mul($s12,  654183, 20);
2443          $s3 -= self::mul($s12,  997805, 20);
2444          $s4 += self::mul($s12,  136657, 18);
2445          $s5 -= self::mul($s12,  683901, 20);
2446  
2447          $carry0 = $s0 >> 21;
2448          $s1 += $carry0;
2449          $s0 -= $carry0 << 21;
2450          $carry1 = $s1 >> 21;
2451          $s2 += $carry1;
2452          $s1 -= $carry1 << 21;
2453          $carry2 = $s2 >> 21;
2454          $s3 += $carry2;
2455          $s2 -= $carry2 << 21;
2456          $carry3 = $s3 >> 21;
2457          $s4 += $carry3;
2458          $s3 -= $carry3 << 21;
2459          $carry4 = $s4 >> 21;
2460          $s5 += $carry4;
2461          $s4 -= $carry4 << 21;
2462          $carry5 = $s5 >> 21;
2463          $s6 += $carry5;
2464          $s5 -= $carry5 << 21;
2465          $carry6 = $s6 >> 21;
2466          $s7 += $carry6;
2467          $s6 -= $carry6 << 21;
2468          $carry7 = $s7 >> 21;
2469          $s8 += $carry7;
2470          $s7 -= $carry7 << 21;
2471          $carry8 = $s8 >> 21;
2472          $s9 += $carry8;
2473          $s8 -= $carry8 << 21;
2474          $carry9 = $s9 >> 21;
2475          $s10 += $carry9;
2476          $s9 -= $carry9 << 21;
2477          $carry10 = $s10 >> 21;
2478          $s11 += $carry10;
2479          $s10 -= $carry10 << 21;
2480  
2481          /**
2482           * @var array<int, int>
2483           */
2484          $arr = array(
2485              (int) (0xff & ($s0 >> 0)),
2486              (int) (0xff & ($s0 >> 8)),
2487              (int) (0xff & (($s0 >> 16) | $s1 << 5)),
2488              (int) (0xff & ($s1 >> 3)),
2489              (int) (0xff & ($s1 >> 11)),
2490              (int) (0xff & (($s1 >> 19) | $s2 << 2)),
2491              (int) (0xff & ($s2 >> 6)),
2492              (int) (0xff & (($s2 >> 14) | $s3 << 7)),
2493              (int) (0xff & ($s3 >> 1)),
2494              (int) (0xff & ($s3 >> 9)),
2495              (int) (0xff & (($s3 >> 17) | $s4 << 4)),
2496              (int) (0xff & ($s4 >> 4)),
2497              (int) (0xff & ($s4 >> 12)),
2498              (int) (0xff & (($s4 >> 20) | $s5 << 1)),
2499              (int) (0xff & ($s5 >> 7)),
2500              (int) (0xff & (($s5 >> 15) | $s6 << 6)),
2501              (int) (0xff & ($s6 >> 2)),
2502              (int) (0xff & ($s6 >> 10)),
2503              (int) (0xff & (($s6 >> 18) | $s7 << 3)),
2504              (int) (0xff & ($s7 >> 5)),
2505              (int) (0xff & ($s7 >> 13)),
2506              (int) (0xff & ($s8 >> 0)),
2507              (int) (0xff & ($s8 >> 8)),
2508              (int) (0xff & (($s8 >> 16) | $s9 << 5)),
2509              (int) (0xff & ($s9 >> 3)),
2510              (int) (0xff & ($s9 >> 11)),
2511              (int) (0xff & (($s9 >> 19) | $s10 << 2)),
2512              (int) (0xff & ($s10 >> 6)),
2513              (int) (0xff & (($s10 >> 14) | $s11 << 7)),
2514              (int) (0xff & ($s11 >> 1)),
2515              (int) (0xff & ($s11 >> 9)),
2516              0xff & ($s11 >> 17)
2517          );
2518          return self::intArrayToString($arr);
2519      }
2520  
2521      /**
2522       * @internal You should not use this directly from another application
2523       *
2524       * @param string $s
2525       * @return string
2526       * @throws TypeError
2527       */
2528      public static function sc_reduce($s)
2529      {
2530          $s0 = 2097151 & self::load_3(self::substr($s, 0, 3));
2531          $s1 = 2097151 & (self::load_4(self::substr($s, 2, 4)) >> 5);
2532          $s2 = 2097151 & (self::load_3(self::substr($s, 5, 3)) >> 2);
2533          $s3 = 2097151 & (self::load_4(self::substr($s, 7, 4)) >> 7);
2534          $s4 = 2097151 & (self::load_4(self::substr($s, 10, 4)) >> 4);
2535          $s5 = 2097151 & (self::load_3(self::substr($s, 13, 3)) >> 1);
2536          $s6 = 2097151 & (self::load_4(self::substr($s, 15, 4)) >> 6);
2537          $s7 = 2097151 & (self::load_3(self::substr($s, 18, 4)) >> 3);
2538          $s8 = 2097151 & self::load_3(self::substr($s, 21, 3));
2539          $s9 = 2097151 & (self::load_4(self::substr($s, 23, 4)) >> 5);
2540          $s10 = 2097151 & (self::load_3(self::substr($s, 26, 3)) >> 2);
2541          $s11 = 2097151 & (self::load_4(self::substr($s, 28, 4)) >> 7);
2542          $s12 = 2097151 & (self::load_4(self::substr($s, 31, 4)) >> 4);
2543          $s13 = 2097151 & (self::load_3(self::substr($s, 34, 3)) >> 1);
2544          $s14 = 2097151 & (self::load_4(self::substr($s, 36, 4)) >> 6);
2545          $s15 = 2097151 & (self::load_3(self::substr($s, 39, 4)) >> 3);
2546          $s16 = 2097151 & self::load_3(self::substr($s, 42, 3));
2547          $s17 = 2097151 & (self::load_4(self::substr($s, 44, 4)) >> 5);
2548          $s18 = 2097151 & (self::load_3(self::substr($s, 47, 3)) >> 2);
2549          $s19 = 2097151 & (self::load_4(self::substr($s, 49, 4)) >> 7);
2550          $s20 = 2097151 & (self::load_4(self::substr($s, 52, 4)) >> 4);
2551          $s21 = 2097151 & (self::load_3(self::substr($s, 55, 3)) >> 1);
2552          $s22 = 2097151 & (self::load_4(self::substr($s, 57, 4)) >> 6);
2553          $s23 = 0x1fffffff & (self::load_4(self::substr($s, 60, 4)) >> 3);
2554  
2555          $s11 += self::mul($s23,  666643, 20);
2556          $s12 += self::mul($s23,  470296, 19);
2557          $s13 += self::mul($s23,  654183, 20);
2558          $s14 -= self::mul($s23,  997805, 20);
2559          $s15 += self::mul($s23,  136657, 18);
2560          $s16 -= self::mul($s23,  683901, 20);
2561  
2562          $s10 += self::mul($s22,  666643, 20);
2563          $s11 += self::mul($s22,  470296, 19);
2564          $s12 += self::mul($s22,  654183, 20);
2565          $s13 -= self::mul($s22,  997805, 20);
2566          $s14 += self::mul($s22,  136657, 18);
2567          $s15 -= self::mul($s22,  683901, 20);
2568  
2569          $s9  += self::mul($s21,  666643, 20);
2570          $s10 += self::mul($s21,  470296, 19);
2571          $s11 += self::mul($s21,  654183, 20);
2572          $s12 -= self::mul($s21,  997805, 20);
2573          $s13 += self::mul($s21,  136657, 18);
2574          $s14 -= self::mul($s21,  683901, 20);
2575  
2576          $s8  += self::mul($s20,  666643, 20);
2577          $s9  += self::mul($s20,  470296, 19);
2578          $s10 += self::mul($s20,  654183, 20);
2579          $s11 -= self::mul($s20,  997805, 20);
2580          $s12 += self::mul($s20,  136657, 18);
2581          $s13 -= self::mul($s20,  683901, 20);
2582  
2583          $s7  += self::mul($s19,  666643, 20);
2584          $s8  += self::mul($s19,  470296, 19);
2585          $s9  += self::mul($s19,  654183, 20);
2586          $s10 -= self::mul($s19,  997805, 20);
2587          $s11 += self::mul($s19,  136657, 18);
2588          $s12 -= self::mul($s19,  683901, 20);
2589  
2590          $s6  += self::mul($s18,  666643, 20);
2591          $s7  += self::mul($s18,  470296, 19);
2592          $s8  += self::mul($s18,  654183, 20);
2593          $s9  -= self::mul($s18,  997805, 20);
2594          $s10 += self::mul($s18,  136657, 18);
2595          $s11 -= self::mul($s18,  683901, 20);
2596  
2597          $carry6 = ($s6 + (1 << 20)) >> 21;
2598          $s7 += $carry6;
2599          $s6 -= $carry6 << 21;
2600          $carry8 = ($s8 + (1 << 20)) >> 21;
2601          $s9 += $carry8;
2602          $s8 -= $carry8 << 21;
2603          $carry10 = ($s10 + (1 << 20)) >> 21;
2604          $s11 += $carry10;
2605          $s10 -= $carry10 << 21;
2606          $carry12 = ($s12 + (1 << 20)) >> 21;
2607          $s13 += $carry12;
2608          $s12 -= $carry12 << 21;
2609          $carry14 = ($s14 + (1 << 20)) >> 21;
2610          $s15 += $carry14;
2611          $s14 -= $carry14 << 21;
2612          $carry16 = ($s16 + (1 << 20)) >> 21;
2613          $s17 += $carry16;
2614          $s16 -= $carry16 << 21;
2615  
2616          $carry7 = ($s7 + (1 << 20)) >> 21;
2617          $s8 += $carry7;
2618          $s7 -= $carry7 << 21;
2619          $carry9 = ($s9 + (1 << 20)) >> 21;
2620          $s10 += $carry9;
2621          $s9 -= $carry9 << 21;
2622          $carry11 = ($s11 + (1 << 20)) >> 21;
2623          $s12 += $carry11;
2624          $s11 -= $carry11 << 21;
2625          $carry13 = ($s13 + (1 << 20)) >> 21;
2626          $s14 += $carry13;
2627          $s13 -= $carry13 << 21;
2628          $carry15 = ($s15 + (1 << 20)) >> 21;
2629          $s16 += $carry15;
2630          $s15 -= $carry15 << 21;
2631  
2632          $s5  += self::mul($s17,  666643, 20);
2633          $s6  += self::mul($s17,  470296, 19);
2634          $s7  += self::mul($s17,  654183, 20);
2635          $s8  -= self::mul($s17,  997805, 20);
2636          $s9  += self::mul($s17,  136657, 18);
2637          $s10 -= self::mul($s17,  683901, 20);
2638  
2639          $s4 += self::mul($s16,  666643, 20);
2640          $s5 += self::mul($s16,  470296, 19);
2641          $s6 += self::mul($s16,  654183, 20);
2642          $s7 -= self::mul($s16,  997805, 20);
2643          $s8 += self::mul($s16,  136657, 18);
2644          $s9 -= self::mul($s16,  683901, 20);
2645  
2646          $s3 += self::mul($s15,  666643, 20);
2647          $s4 += self::mul($s15,  470296, 19);
2648          $s5 += self::mul($s15,  654183, 20);
2649          $s6 -= self::mul($s15,  997805, 20);
2650          $s7 += self::mul($s15,  136657, 18);
2651          $s8 -= self::mul($s15,  683901, 20);
2652  
2653          $s2 += self::mul($s14,  666643, 20);
2654          $s3 += self::mul($s14,  470296, 19);
2655          $s4 += self::mul($s14,  654183, 20);
2656          $s5 -= self::mul($s14,  997805, 20);
2657          $s6 += self::mul($s14,  136657, 18);
2658          $s7 -= self::mul($s14,  683901, 20);
2659  
2660          $s1 += self::mul($s13,  666643, 20);
2661          $s2 += self::mul($s13,  470296, 19);
2662          $s3 += self::mul($s13,  654183, 20);
2663          $s4 -= self::mul($s13,  997805, 20);
2664          $s5 += self::mul($s13,  136657, 18);
2665          $s6 -= self::mul($s13,  683901, 20);
2666  
2667          $s0 += self::mul($s12,  666643, 20);
2668          $s1 += self::mul($s12,  470296, 19);
2669          $s2 += self::mul($s12,  654183, 20);
2670          $s3 -= self::mul($s12,  997805, 20);
2671          $s4 += self::mul($s12,  136657, 18);
2672          $s5 -= self::mul($s12,  683901, 20);
2673          $s12 = 0;
2674  
2675          $carry0 = ($s0 + (1 << 20)) >> 21;
2676          $s1 += $carry0;
2677          $s0 -= $carry0 << 21;
2678          $carry2 = ($s2 + (1 << 20)) >> 21;
2679          $s3 += $carry2;
2680          $s2 -= $carry2 << 21;
2681          $carry4 = ($s4 + (1 << 20)) >> 21;
2682          $s5 += $carry4;
2683          $s4 -= $carry4 << 21;
2684          $carry6 = ($s6 + (1 << 20)) >> 21;
2685          $s7 += $carry6;
2686          $s6 -= $carry6 << 21;
2687          $carry8 = ($s8 + (1 << 20)) >> 21;
2688          $s9 += $carry8;
2689          $s8 -= $carry8 << 21;
2690          $carry10 = ($s10 + (1 << 20)) >> 21;
2691          $s11 += $carry10;
2692          $s10 -= $carry10 << 21;
2693  
2694          $carry1 = ($s1 + (1 << 20)) >> 21;
2695          $s2 += $carry1;
2696          $s1 -= $carry1 << 21;
2697          $carry3 = ($s3 + (1 << 20)) >> 21;
2698          $s4 += $carry3;
2699          $s3 -= $carry3 << 21;
2700          $carry5 = ($s5 + (1 << 20)) >> 21;
2701          $s6 += $carry5;
2702          $s5 -= $carry5 << 21;
2703          $carry7 = ($s7 + (1 << 20)) >> 21;
2704          $s8 += $carry7;
2705          $s7 -= $carry7 << 21;
2706          $carry9 = ($s9 + (1 << 20)) >> 21;
2707          $s10 += $carry9;
2708          $s9 -= $carry9 << 21;
2709          $carry11 = ($s11 + (1 << 20)) >> 21;
2710          $s12 += $carry11;
2711          $s11 -= $carry11 << 21;
2712  
2713          $s0 += self::mul($s12,  666643, 20);
2714          $s1 += self::mul($s12,  470296, 19);
2715          $s2 += self::mul($s12,  654183, 20);
2716          $s3 -= self::mul($s12,  997805, 20);
2717          $s4 += self::mul($s12,  136657, 18);
2718          $s5 -= self::mul($s12,  683901, 20);
2719          $s12 = 0;
2720  
2721          $carry0 = $s0 >> 21;
2722          $s1 += $carry0;
2723          $s0 -= $carry0 << 21;
2724          $carry1 = $s1 >> 21;
2725          $s2 += $carry1;
2726          $s1 -= $carry1 << 21;
2727          $carry2 = $s2 >> 21;
2728          $s3 += $carry2;
2729          $s2 -= $carry2 << 21;
2730          $carry3 = $s3 >> 21;
2731          $s4 += $carry3;
2732          $s3 -= $carry3 << 21;
2733          $carry4 = $s4 >> 21;
2734          $s5 += $carry4;
2735          $s4 -= $carry4 << 21;
2736          $carry5 = $s5 >> 21;
2737          $s6 += $carry5;
2738          $s5 -= $carry5 << 21;
2739          $carry6 = $s6 >> 21;
2740          $s7 += $carry6;
2741          $s6 -= $carry6 << 21;
2742          $carry7 = $s7 >> 21;
2743          $s8 += $carry7;
2744          $s7 -= $carry7 << 21;
2745          $carry8 = $s8 >> 21;
2746          $s9 += $carry8;
2747          $s8 -= $carry8 << 21;
2748          $carry9 = $s9 >> 21;
2749          $s10 += $carry9;
2750          $s9 -= $carry9 << 21;
2751          $carry10 = $s10 >> 21;
2752          $s11 += $carry10;
2753          $s10 -= $carry10 << 21;
2754          $carry11 = $s11 >> 21;
2755          $s12 += $carry11;
2756          $s11 -= $carry11 << 21;
2757  
2758          $s0 += self::mul($s12,  666643, 20);
2759          $s1 += self::mul($s12,  470296, 19);
2760          $s2 += self::mul($s12,  654183, 20);
2761          $s3 -= self::mul($s12,  997805, 20);
2762          $s4 += self::mul($s12,  136657, 18);
2763          $s5 -= self::mul($s12,  683901, 20);
2764  
2765          $carry0 = $s0 >> 21;
2766          $s1 += $carry0;
2767          $s0 -= $carry0 << 21;
2768          $carry1 = $s1 >> 21;
2769          $s2 += $carry1;
2770          $s1 -= $carry1 << 21;
2771          $carry2 = $s2 >> 21;
2772          $s3 += $carry2;
2773          $s2 -= $carry2 << 21;
2774          $carry3 = $s3 >> 21;
2775          $s4 += $carry3;
2776          $s3 -= $carry3 << 21;
2777          $carry4 = $s4 >> 21;
2778          $s5 += $carry4;
277