[ 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  
 321          /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
 322          $orig = ParagonIE_Sodium_Compat::$fastMult;
 323  
 324          // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
 325          ParagonIE_Sodium_Compat::$fastMult = true;
 326  
 327          /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */
 328          $A = self::ge_frombytes_negate_vartime($pk);
 329          if (!self::is_on_main_subgroup($A)) {
 330              throw new SodiumException('Public key is not on a member of the main subgroup');
 331          }
 332  
 333          /** @var string $hDigest */
 334          $hDigest = hash(
 335              'sha512',
 336              self::substr($sig, 0, 32) .
 337                  self::substr($pk, 0, 32) .
 338                  $message,
 339              true
 340          );
 341  
 342          /** @var string $h */
 343          $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32);
 344  
 345          /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */
 346          $R = self::ge_double_scalarmult_vartime(
 347              $h,
 348              $A,
 349              self::substr($sig, 32)
 350          );
 351  
 352          /** @var string $rcheck */
 353          $rcheck = self::ge_tobytes($R);
 354  
 355          // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
 356          ParagonIE_Sodium_Compat::$fastMult = $orig;
 357  
 358          return self::verify_32($rcheck, self::substr($sig, 0, 32));
 359      }
 360  
 361      /**
 362       * @internal You should not use this directly from another application
 363       *
 364       * @param string $S
 365       * @return bool
 366       * @throws SodiumException
 367       * @throws TypeError
 368       */
 369      public static function check_S_lt_L($S)
 370      {
 371          if (self::strlen($S) < 32) {
 372              throw new SodiumException('Signature must be 32 bytes');
 373          }
 374          $L = array(
 375              0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
 376              0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
 377              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 378              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
 379          );
 380          $c = 0;
 381          $n = 1;
 382          $i = 32;
 383  
 384          /** @var array<int, int> $L */
 385          do {
 386              --$i;
 387              $x = self::chrToInt($S[$i]);
 388              $c |= (
 389                  (($x - $L[$i]) >> 8) & $n
 390              );
 391              $n &= (
 392                  (($x ^ $L[$i]) - 1) >> 8
 393              );
 394          } while ($i !== 0);
 395  
 396          return $c === 0;
 397      }
 398  
 399      /**
 400       * @param string $R
 401       * @return bool
 402       * @throws SodiumException
 403       * @throws TypeError
 404       */
 405      public static function small_order($R)
 406      {
 407          /** @var array<int, array<int, int>> $blocklist */
 408          $blocklist = array(
 409              /* 0 (order 4) */
 410              array(
 411                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 412                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 413                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 414                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 415              ),
 416              /* 1 (order 1) */
 417              array(
 418                  0x01, 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              /* 2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
 424              array(
 425                  0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
 426                  0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
 427                  0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
 428                  0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05
 429              ),
 430              /* 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
 431              array(
 432                  0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
 433                  0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
 434                  0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
 435                  0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a
 436              ),
 437              /* p-1 (order 2) */
 438              array(
 439                  0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
 440                  0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
 441                  0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
 442                  0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85
 443              ),
 444              /* p (order 4) */
 445              array(
 446                  0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
 447                  0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
 448                  0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
 449                  0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa
 450              ),
 451              /* p+1 (order 1) */
 452              array(
 453                  0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 454                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 455                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 456                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
 457              ),
 458              /* p+2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
 459              array(
 460                  0xed, 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+55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
 466              array(
 467                  0xee, 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              /* 2p-1 (order 2) */
 473              array(
 474                  0xd9, 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, 0xff
 478              ),
 479              /* 2p (order 4) */
 480              array(
 481                  0xda, 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+1 (order 1) */
 487              array(
 488                  0xdb, 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          );
 494          /** @var int $countBlocklist */
 495          $countBlocklist = count($blocklist);
 496  
 497          for ($i = 0; $i < $countBlocklist; ++$i) {
 498              $c = 0;
 499              for ($j = 0; $j < 32; ++$j) {
 500                  $c |= self::chrToInt($R[$j]) ^ (int) $blocklist[$i][$j];
 501              }
 502              if ($c === 0) {
 503                  return true;
 504              }
 505          }
 506          return false;
 507      }
 508  
 509      /**
 510       * @param string $s
 511       * @return string
 512       * @throws SodiumException
 513       */
 514      public static function scalar_complement($s)
 515      {
 516          $t_ = self::L . str_repeat("\x00", 32);
 517          sodium_increment($t_);
 518          $s_ = $s . str_repeat("\x00", 32);
 519          ParagonIE_Sodium_Compat::sub($t_, $s_);
 520          return self::sc_reduce($t_);
 521      }
 522  
 523      /**
 524       * @return string
 525       * @throws SodiumException
 526       */
 527      public static function scalar_random()
 528      {
 529          do {
 530              $r = ParagonIE_Sodium_Compat::randombytes_buf(self::SCALAR_BYTES);
 531              $r[self::SCALAR_BYTES - 1] = self::intToChr(
 532                  self::chrToInt($r[self::SCALAR_BYTES - 1]) & 0x1f
 533              );
 534          } while (
 535              !self::check_S_lt_L($r) || ParagonIE_Sodium_Compat::is_zero($r)
 536          );
 537          return $r;
 538      }
 539  
 540      /**
 541       * @param string $s
 542       * @return string
 543       * @throws SodiumException
 544       */
 545      public static function scalar_negate($s)
 546      {
 547          $t_ = self::L . str_repeat("\x00", 32) ;
 548          $s_ = $s . str_repeat("\x00", 32) ;
 549          ParagonIE_Sodium_Compat::sub($t_, $s_);
 550          return self::sc_reduce($t_);
 551      }
 552  
 553      /**
 554       * @param string $a
 555       * @param string $b
 556       * @return string
 557       * @throws SodiumException
 558       */
 559      public static function scalar_add($a, $b)
 560      {
 561          $a_ = $a . str_repeat("\x00", 32);
 562          $b_ = $b . str_repeat("\x00", 32);
 563          ParagonIE_Sodium_Compat::add($a_, $b_);
 564          return self::sc_reduce($a_);
 565      }
 566  
 567      /**
 568       * @param string $x
 569       * @param string $y
 570       * @return string
 571       * @throws SodiumException
 572       */
 573      public static function scalar_sub($x, $y)
 574      {
 575          $yn = self::scalar_negate($y);
 576          return self::scalar_add($x, $yn);
 577      }
 578  }


Generated : Thu Feb 26 08:20:04 2026 Cross-referenced by PHPXref