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