[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  
   3  if (class_exists('ParagonIE_Sodium_Core_Ed25519', false)) {
   4      return;
   5  }
   6  if (!class_exists('ParagonIE_Sodium_Core_Curve25519', false)) {
   7      require_once dirname(__FILE__) . '/Curve25519.php';
   8  }
   9  
  10  /**
  11   * Class ParagonIE_Sodium_Core_Ed25519
  12   */
  13  abstract class ParagonIE_Sodium_Core_Ed25519 extends ParagonIE_Sodium_Core_Curve25519
  14  {
  15      const KEYPAIR_BYTES = 96;
  16      const SEED_BYTES = 32;
  17      const SCALAR_BYTES = 32;
  18  
  19      /**
  20       * @internal You should not use this directly from another application
  21       *
  22       * @return string (96 bytes)
  23       * @throws Exception
  24       * @throws SodiumException
  25       * @throws TypeError
  26       */
  27      public static function keypair()
  28      {
  29          $seed = random_bytes(self::SEED_BYTES);
  30          $pk = '';
  31          $sk = '';
  32          self::seed_keypair($pk, $sk, $seed);
  33          return $sk . $pk;
  34      }
  35  
  36      /**
  37       * @internal You should not use this directly from another application
  38       *
  39       * @param string $pk
  40       * @param string $sk
  41       * @param string $seed
  42       * @return string
  43       * @throws SodiumException
  44       * @throws TypeError
  45       */
  46      public static function seed_keypair(&$pk, &$sk, $seed)
  47      {
  48          if (self::strlen($seed) !== self::SEED_BYTES) {
  49              throw new SodiumException('crypto_sign keypair seed must be 32 bytes long');
  50          }
  51  
  52          /** @var string $pk */
  53          $pk = self::publickey_from_secretkey($seed);
  54          $sk = $seed . $pk;
  55          return $sk;
  56      }
  57  
  58      /**
  59       * @internal You should not use this directly from another application
  60       *
  61       * @param string $keypair
  62       * @return string
  63       * @throws TypeError
  64       */
  65      public static function secretkey($keypair)
  66      {
  67          if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
  68              throw new SodiumException('crypto_sign keypair must be 96 bytes long');
  69          }
  70          return self::substr($keypair, 0, 64);
  71      }
  72  
  73      /**
  74       * @internal You should not use this directly from another application
  75       *
  76       * @param string $keypair
  77       * @return string
  78       * @throws TypeError
  79       */
  80      public static function publickey($keypair)
  81      {
  82          if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
  83              throw new SodiumException('crypto_sign keypair must be 96 bytes long');
  84          }
  85          return self::substr($keypair, 64, 32);
  86      }
  87  
  88      /**
  89       * @internal You should not use this directly from another application
  90       *
  91       * @param string $sk
  92       * @return string
  93       * @throws SodiumException
  94       * @throws TypeError
  95       */
  96      public static function publickey_from_secretkey($sk)
  97      {
  98          /** @var string $sk */
  99          $sk = hash('sha512', self::substr($sk, 0, 32), true);
 100          $sk[0] = self::intToChr(
 101              self::chrToInt($sk[0]) & 248
 102          );
 103          $sk[31] = self::intToChr(
 104              (self::chrToInt($sk[31]) & 63) | 64
 105          );
 106          return self::sk_to_pk($sk);
 107      }
 108  
 109      /**
 110       * @param string $pk
 111       * @return string
 112       * @throws SodiumException
 113       * @throws TypeError
 114       */
 115      public static function pk_to_curve25519($pk)
 116      {
 117          if (self::small_order($pk)) {
 118              throw new SodiumException('Public key is on a small order');
 119          }
 120          $A = self::ge_frombytes_negate_vartime(self::substr($pk, 0, 32));
 121          $p1 = self::ge_mul_l($A);
 122          if (!self::fe_isnonzero($p1->X)) {
 123              throw new SodiumException('Unexpected zero result');
 124          }
 125  
 126          # fe_1(one_minus_y);
 127          # fe_sub(one_minus_y, one_minus_y, A.Y);
 128          # fe_invert(one_minus_y, one_minus_y);
 129          $one_minux_y = self::fe_invert(
 130              self::fe_sub(
 131                  self::fe_1(),
 132                  $A->Y
 133              )
 134          );
 135  
 136          # fe_1(x);
 137          # fe_add(x, x, A.Y);
 138          # fe_mul(x, x, one_minus_y);
 139          $x = self::fe_mul(
 140              self::fe_add(self::fe_1(), $A->Y),
 141              $one_minux_y
 142          );
 143  
 144          # fe_tobytes(curve25519_pk, x);
 145          return self::fe_tobytes($x);
 146      }
 147  
 148      /**
 149       * @internal You should not use this directly from another application
 150       *
 151       * @param string $sk
 152       * @return string
 153       * @throws SodiumException
 154       * @throws TypeError
 155       */
 156      public static function sk_to_pk($sk)
 157      {
 158          return self::ge_p3_tobytes(
 159              self::ge_scalarmult_base(
 160                  self::substr($sk, 0, 32)
 161              )
 162          );
 163      }
 164  
 165      /**
 166       * @internal You should not use this directly from another application
 167       *
 168       * @param string $message
 169       * @param string $sk
 170       * @return string
 171       * @throws SodiumException
 172       * @throws TypeError
 173       */
 174      public static function sign($message, $sk)
 175      {
 176          /** @var string $signature */
 177          $signature = self::sign_detached($message, $sk);
 178          return $signature . $message;
 179      }
 180  
 181      /**
 182       * @internal You should not use this directly from another application
 183       *
 184       * @param string $message A signed message
 185       * @param string $pk      Public key
 186       * @return string         Message (without signature)
 187       * @throws SodiumException
 188       * @throws TypeError
 189       */
 190      public static function sign_open($message, $pk)
 191      {
 192          /** @var string $signature */
 193          $signature = self::substr($message, 0, 64);
 194  
 195          /** @var string $message */
 196          $message = self::substr($message, 64);
 197  
 198          if (self::verify_detached($signature, $message, $pk)) {
 199              return $message;
 200          }
 201          throw new SodiumException('Invalid signature');
 202      }
 203  
 204      /**
 205       * @internal You should not use this directly from another application
 206       *
 207       * @param string $message
 208       * @param string $sk
 209       * @return string
 210       * @throws SodiumException
 211       * @throws TypeError
 212       */
 213      public static function sign_detached($message, $sk)
 214      {
 215          if (self::strlen($sk) !== 64) {
 216              throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long');
 217          }
 218          # crypto_hash_sha512(az, sk, 32);
 219          $az =  hash('sha512', self::substr($sk, 0, 32), true);
 220  
 221          # az[0] &= 248;
 222          # az[31] &= 63;
 223          # az[31] |= 64;
 224          $az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
 225          $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
 226  
 227          # crypto_hash_sha512_init(&hs);
 228          # crypto_hash_sha512_update(&hs, az + 32, 32);
 229          # crypto_hash_sha512_update(&hs, m, mlen);
 230          # crypto_hash_sha512_final(&hs, nonce);
 231          $hs = hash_init('sha512');
 232          hash_update($hs, self::substr($az, 32, 32));
 233          hash_update($hs, $message);
 234          $nonceHash = hash_final($hs, true);
 235  
 236          # memmove(sig + 32, sk + 32, 32);
 237          $pk = self::substr($sk, 32, 32);
 238  
 239          # sc_reduce(nonce);
 240          # ge_scalarmult_base(&R, nonce);
 241          # ge_p3_tobytes(sig, &R);
 242          $nonce = self::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
 243          $sig = self::ge_p3_tobytes(
 244              self::ge_scalarmult_base($nonce)
 245          );
 246  
 247          # crypto_hash_sha512_init(&hs);
 248          # crypto_hash_sha512_update(&hs, sig, 64);
 249          # crypto_hash_sha512_update(&hs, m, mlen);
 250          # crypto_hash_sha512_final(&hs, hram);
 251          $hs = hash_init('sha512');
 252          hash_update($hs, self::substr($sig, 0, 32));
 253          hash_update($hs, self::substr($pk, 0, 32));
 254          hash_update($hs, $message);
 255          $hramHash = hash_final($hs, true);
 256  
 257          # sc_reduce(hram);
 258          # sc_muladd(sig + 32, hram, az, nonce);
 259          $hram = self::sc_reduce($hramHash);
 260          $sigAfter = self::sc_muladd($hram, $az, $nonce);
 261          $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
 262  
 263          try {
 264              ParagonIE_Sodium_Compat::memzero($az);
 265          } catch (SodiumException $ex) {
 266              $az = null;
 267          }
 268          return $sig;
 269      }
 270  
 271      /**
 272       * @internal You should not use this directly from another application
 273       *
 274       * @param string $sig
 275       * @param string $message
 276       * @param string $pk
 277       * @return bool
 278       * @throws SodiumException
 279       * @throws TypeError
 280       */
 281      public static function verify_detached($sig, $message, $pk)
 282      {
 283          if (self::strlen($sig) !== 64) {
 284              throw new SodiumException('Argument 1 must be CRYPTO_SIGN_BYTES long');
 285          }
 286          if (self::strlen($pk) !== 32) {
 287              throw new SodiumException('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES long');
 288          }
 289          if ((self::chrToInt($sig[63]) & 240) && self::check_S_lt_L(self::substr($sig, 32, 32))) {
 290              throw new SodiumException('S < L - Invalid signature');
 291          }
 292          if (self::small_order($sig)) {
 293              throw new SodiumException('Signature is on too small of an order');
 294          }
 295          if ((self::chrToInt($sig[63]) & 224) !== 0) {
 296              throw new SodiumException('Invalid signature');
 297          }
 298          $d = 0;
 299          for ($i = 0; $i < 32; ++$i) {
 300              $d |= self::chrToInt($pk[$i]);
 301          }
 302          if ($d === 0) {
 303              throw new SodiumException('All zero public key');
 304          }
 305  
 306          /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
 307          $orig = ParagonIE_Sodium_Compat::$fastMult;
 308  
 309          // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
 310          ParagonIE_Sodium_Compat::$fastMult = true;
 311  
 312          /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */
 313          $A = self::ge_frombytes_negate_vartime($pk);
 314  
 315          /** @var string $hDigest */
 316          $hDigest = hash(
 317              'sha512',
 318              self::substr($sig, 0, 32) .
 319                  self::substr($pk, 0, 32) .
 320                  $message,
 321              true
 322          );
 323  
 324          /** @var string $h */
 325          $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32);
 326  
 327          /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */
 328          $R = self::ge_double_scalarmult_vartime(
 329              $h,
 330              $A,
 331              self::substr($sig, 32)
 332          );
 333  
 334          /** @var string $rcheck */
 335          $rcheck = self::ge_tobytes($R);
 336  
 337          // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
 338          ParagonIE_Sodium_Compat::$fastMult = $orig;
 339  
 340          return self::verify_32($rcheck, self::substr($sig, 0, 32));
 341      }
 342  
 343      /**
 344       * @internal You should not use this directly from another application
 345       *
 346       * @param string $S
 347       * @return bool
 348       * @throws SodiumException
 349       * @throws TypeError
 350       */
 351      public static function check_S_lt_L($S)
 352      {
 353          if (self::strlen($S) < 32) {
 354              throw new SodiumException('Signature must be 32 bytes');
 355          }
 356          $L = array(
 357              0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
 358              0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
 359              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 360              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
 361          );
 362          $c = 0;
 363          $n = 1;
 364          $i = 32;
 365  
 366          /** @var array<int, int> $L */
 367          do {
 368              --$i;
 369              $x = self::chrToInt($S[$i]);
 370              $c |= (
 371                  (($x - $L[$i]) >> 8) & $n
 372              );
 373              $n &= (
 374                  (($x ^ $L[$i]) - 1) >> 8
 375              );
 376          } while ($i !== 0);
 377  
 378          return $c === 0;
 379      }
 380  
 381      /**
 382       * @param string $R
 383       * @return bool
 384       * @throws SodiumException
 385       * @throws TypeError
 386       */
 387      public static function small_order($R)
 388      {
 389          /** @var array<int, array<int, int>> $blocklist */
 390          $blocklist = array(
 391              /* 0 (order 4) */
 392              array(
 393                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 394                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 395                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 396                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 397              ),
 398              /* 1 (order 1) */
 399              array(
 400                  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 401                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 402                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 403                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 404              ),
 405              /* 2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
 406              array(
 407                  0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
 408                  0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
 409                  0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
 410                  0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05
 411              ),
 412              /* 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
 413              array(
 414                  0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
 415                  0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
 416                  0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
 417                  0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a
 418              ),
 419              /* p-1 (order 2) */
 420              array(
 421                  0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
 422                  0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
 423                  0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
 424                  0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85
 425              ),
 426              /* p (order 4) */
 427              array(
 428                  0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
 429                  0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
 430                  0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
 431                  0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa
 432              ),
 433              /* p+1 (order 1) */
 434              array(
 435                  0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 436                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 437                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 438                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
 439              ),
 440              /* p+2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
 441              array(
 442                  0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 443                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 444                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 445                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
 446              ),
 447              /* p+55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
 448              array(
 449                  0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 450                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 451                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 452                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
 453              ),
 454              /* 2p-1 (order 2) */
 455              array(
 456                  0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 457                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 458                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 459                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 460              ),
 461              /* 2p (order 4) */
 462              array(
 463                  0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 464                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 465                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 466                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 467              ),
 468              /* 2p+1 (order 1) */
 469              array(
 470                  0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 471                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 472                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 473                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 474              )
 475          );
 476          /** @var int $countBlocklist */
 477          $countBlocklist = count($blocklist);
 478  
 479          for ($i = 0; $i < $countBlocklist; ++$i) {
 480              $c = 0;
 481              for ($j = 0; $j < 32; ++$j) {
 482                  $c |= self::chrToInt($R[$j]) ^ (int) $blocklist[$i][$j];
 483              }
 484              if ($c === 0) {
 485                  return true;
 486              }
 487          }
 488          return false;
 489      }
 490  
 491      /**
 492       * @param string $s
 493       * @return string
 494       * @throws SodiumException
 495       */
 496      public static function scalar_complement($s)
 497      {
 498          $t_ = self::L . str_repeat("\x00", 32);
 499          sodium_increment($t_);
 500          $s_ = $s . str_repeat("\x00", 32);
 501          ParagonIE_Sodium_Compat::sub($t_, $s_);
 502          return self::sc_reduce($t_);
 503      }
 504  
 505      /**
 506       * @return string
 507       * @throws SodiumException
 508       */
 509      public static function scalar_random()
 510      {
 511          do {
 512              $r = ParagonIE_Sodium_Compat::randombytes_buf(self::SCALAR_BYTES);
 513              $r[self::SCALAR_BYTES - 1] = self::intToChr(
 514                  self::chrToInt($r[self::SCALAR_BYTES - 1]) & 0x1f
 515              );
 516          } while (
 517              !self::check_S_lt_L($r) || ParagonIE_Sodium_Compat::is_zero($r)
 518          );
 519          return $r;
 520      }
 521  
 522      /**
 523       * @param string $s
 524       * @return string
 525       * @throws SodiumException
 526       */
 527      public static function scalar_negate($s)
 528      {
 529          $t_ = self::L . str_repeat("\x00", 32) ;
 530          $s_ = $s . str_repeat("\x00", 32) ;
 531          ParagonIE_Sodium_Compat::sub($t_, $s_);
 532          return self::sc_reduce($t_);
 533      }
 534  
 535      /**
 536       * @param string $a
 537       * @param string $b
 538       * @return string
 539       * @throws SodiumException
 540       */
 541      public static function scalar_add($a, $b)
 542      {
 543          $a_ = $a . str_repeat("\x00", 32);
 544          $b_ = $b . str_repeat("\x00", 32);
 545          ParagonIE_Sodium_Compat::add($a_, $b_);
 546          return self::sc_reduce($a_);
 547      }
 548  
 549      /**
 550       * @param string $x
 551       * @param string $y
 552       * @return string
 553       * @throws SodiumException
 554       */
 555      public static function scalar_sub($x, $y)
 556      {
 557          $yn = self::scalar_negate($y);
 558          return self::scalar_add($x, $yn);
 559      }
 560  }


Generated : Fri Oct 10 08:20:03 2025 Cross-referenced by PHPXref