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


Generated : Sat Nov 14 08:20:01 2026 Cross-referenced by PHPXref