[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  
   3  if (class_exists('ParagonIE_Sodium_Core32_Ed25519', false)) {
   4      return;
   5  }
   6  if (!class_exists('ParagonIE_Sodium_Core32_Curve25519')) {
   7      require_once dirname(__FILE__) . '/Curve25519.php';
   8  }
   9  
  10  /**
  11   * Class ParagonIE_Sodium_Core32_Ed25519
  12   */
  13  abstract class ParagonIE_Sodium_Core32_Ed25519 extends ParagonIE_Sodium_Core32_Curve25519
  14  {
  15      const KEYPAIR_BYTES = 96;
  16      const SEED_BYTES = 32;
  17  
  18      /**
  19       * @internal You should not use this directly from another application
  20       *
  21       * @return string (96 bytes)
  22       * @throws Exception
  23       * @throws SodiumException
  24       * @throws TypeError
  25       */
  26      public static function keypair()
  27      {
  28          $seed = random_bytes(self::SEED_BYTES);
  29          $pk = '';
  30          $sk = '';
  31          self::seed_keypair($pk, $sk, $seed);
  32          return $sk . $pk;
  33      }
  34  
  35      /**
  36       * @internal You should not use this directly from another application
  37       *
  38       * @param string $pk
  39       * @param string $sk
  40       * @param string $seed
  41       * @return string
  42       * @throws SodiumException
  43       * @throws TypeError
  44       */
  45      public static function seed_keypair(&$pk, &$sk, $seed)
  46      {
  47          if (self::strlen($seed) !== self::SEED_BYTES) {
  48              throw new RangeException('crypto_sign keypair seed must be 32 bytes long');
  49          }
  50  
  51          /** @var string $pk */
  52          $pk = self::publickey_from_secretkey($seed);
  53          $sk = $seed . $pk;
  54          return $sk;
  55      }
  56  
  57      /**
  58       * @internal You should not use this directly from another application
  59       *
  60       * @param string $keypair
  61       * @return string
  62       * @throws TypeError
  63       */
  64      public static function secretkey($keypair)
  65      {
  66          if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
  67              throw new RangeException('crypto_sign keypair must be 96 bytes long');
  68          }
  69          return self::substr($keypair, 0, 64);
  70      }
  71  
  72      /**
  73       * @internal You should not use this directly from another application
  74       *
  75       * @param string $keypair
  76       * @return string
  77       * @throws RangeException
  78       * @throws TypeError
  79       */
  80      public static function publickey($keypair)
  81      {
  82          if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
  83              throw new RangeException('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_Core32_Curve25519_Ge_P3 $A
 114       * @return bool
 115       *
 116       * @throws SodiumException
 117       */
 118      public static function is_on_main_subgroup(ParagonIE_Sodium_Core32_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($pk);
 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  
 152          # fe_1(x);
 153          # fe_add(x, x, A.Y);
 154          # fe_mul(x, x, one_minus_y);
 155          $x = self::fe_mul(
 156              self::fe_add(self::fe_1(), $A->Y),
 157              $one_minux_y
 158          );
 159  
 160          # fe_tobytes(curve25519_pk, x);
 161          return self::fe_tobytes($x);
 162      }
 163  
 164      /**
 165       * @internal You should not use this directly from another application
 166       *
 167       * @param string $sk
 168       * @return string
 169       * @throws SodiumException
 170       * @throws TypeError
 171       */
 172      public static function sk_to_pk($sk)
 173      {
 174          return self::ge_p3_tobytes(
 175              self::ge_scalarmult_base(
 176                  self::substr($sk, 0, 32)
 177              )
 178          );
 179      }
 180  
 181      /**
 182       * @internal You should not use this directly from another application
 183       *
 184       * @param string $message
 185       * @param string $sk
 186       * @return string
 187       * @throws SodiumException
 188       * @throws TypeError
 189       */
 190      public static function sign($message, $sk)
 191      {
 192          /** @var string $signature */
 193          $signature = self::sign_detached($message, $sk);
 194          return $signature . $message;
 195      }
 196  
 197      /**
 198       * @internal You should not use this directly from another application
 199       *
 200       * @param string $message A signed message
 201       * @param string $pk      Public key
 202       * @return string         Message (without signature)
 203       * @throws SodiumException
 204       * @throws TypeError
 205       */
 206      public static function sign_open($message, $pk)
 207      {
 208          /** @var string $signature */
 209          $signature = self::substr($message, 0, 64);
 210  
 211          /** @var string $message */
 212          $message = self::substr($message, 64);
 213  
 214          if (self::verify_detached($signature, $message, $pk)) {
 215              return $message;
 216          }
 217          throw new SodiumException('Invalid signature');
 218      }
 219  
 220      /**
 221       * @internal You should not use this directly from another application
 222       *
 223       * @param string $message
 224       * @param string $sk
 225       * @return string
 226       * @throws SodiumException
 227       * @throws TypeError
 228       * @psalm-suppress PossiblyInvalidArgument
 229       */
 230      public static function sign_detached($message, $sk)
 231      {
 232          # crypto_hash_sha512(az, sk, 32);
 233          $az =  hash('sha512', self::substr($sk, 0, 32), true);
 234  
 235          # az[0] &= 248;
 236          # az[31] &= 63;
 237          # az[31] |= 64;
 238          $az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
 239          $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
 240  
 241          # crypto_hash_sha512_init(&hs);
 242          # crypto_hash_sha512_update(&hs, az + 32, 32);
 243          # crypto_hash_sha512_update(&hs, m, mlen);
 244          # crypto_hash_sha512_final(&hs, nonce);
 245          $hs = hash_init('sha512');
 246          self::hash_update($hs, self::substr($az, 32, 32));
 247          self::hash_update($hs, $message);
 248          $nonceHash = hash_final($hs, true);
 249  
 250          # memmove(sig + 32, sk + 32, 32);
 251          $pk = self::substr($sk, 32, 32);
 252  
 253          # sc_reduce(nonce);
 254          # ge_scalarmult_base(&R, nonce);
 255          # ge_p3_tobytes(sig, &R);
 256          $nonce = self::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
 257          $sig = self::ge_p3_tobytes(
 258              self::ge_scalarmult_base($nonce)
 259          );
 260  
 261          # crypto_hash_sha512_init(&hs);
 262          # crypto_hash_sha512_update(&hs, sig, 64);
 263          # crypto_hash_sha512_update(&hs, m, mlen);
 264          # crypto_hash_sha512_final(&hs, hram);
 265          $hs = hash_init('sha512');
 266          self::hash_update($hs, self::substr($sig, 0, 32));
 267          self::hash_update($hs, self::substr($pk, 0, 32));
 268          self::hash_update($hs, $message);
 269          $hramHash = hash_final($hs, true);
 270  
 271          # sc_reduce(hram);
 272          # sc_muladd(sig + 32, hram, az, nonce);
 273          $hram = self::sc_reduce($hramHash);
 274          $sigAfter = self::sc_muladd($hram, $az, $nonce);
 275          $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
 276  
 277          try {
 278              ParagonIE_Sodium_Compat::memzero($az);
 279          } catch (SodiumException $ex) {
 280              $az = null;
 281          }
 282          return $sig;
 283      }
 284  
 285      /**
 286       * @internal You should not use this directly from another application
 287       *
 288       * @param string $sig
 289       * @param string $message
 290       * @param string $pk
 291       * @return bool
 292       * @throws SodiumException
 293       * @throws TypeError
 294       */
 295      public static function verify_detached($sig, $message, $pk)
 296      {
 297          if (self::strlen($sig) < 64) {
 298              throw new SodiumException('Signature is too short');
 299          }
 300          if ((self::chrToInt($sig[63]) & 240) && self::check_S_lt_L(self::substr($sig, 32, 32))) {
 301              throw new SodiumException('S < L - Invalid signature');
 302          }
 303          if (self::small_order($sig)) {
 304              throw new SodiumException('Signature is on too small of an order');
 305          }
 306          if ((self::chrToInt($sig[63]) & 224) !== 0) {
 307              throw new SodiumException('Invalid signature');
 308          }
 309          $d = 0;
 310          for ($i = 0; $i < 32; ++$i) {
 311              $d |= self::chrToInt($pk[$i]);
 312          }
 313          if ($d === 0) {
 314              throw new SodiumException('All zero public key');
 315          }
 316  
 317          /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
 318          $orig = ParagonIE_Sodium_Compat::$fastMult;
 319  
 320          // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
 321          ParagonIE_Sodium_Compat::$fastMult = true;
 322  
 323          /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */
 324          $A = self::ge_frombytes_negate_vartime($pk);
 325          if (!self::is_on_main_subgroup($A)) {
 326              throw new SodiumException('Public key is not on a member of the main subgroup');
 327          }
 328  
 329          /** @var string $hDigest */
 330          $hDigest = hash(
 331              'sha512',
 332              self::substr($sig, 0, 32) .
 333              self::substr($pk, 0, 32) .
 334              $message,
 335              true
 336          );
 337  
 338          /** @var string $h */
 339          $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32);
 340  
 341          /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */
 342          $R = self::ge_double_scalarmult_vartime(
 343              $h,
 344              $A,
 345              self::substr($sig, 32)
 346          );
 347  
 348          /** @var string $rcheck */
 349          $rcheck = self::ge_tobytes($R);
 350  
 351          // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
 352          ParagonIE_Sodium_Compat::$fastMult = $orig;
 353  
 354          return self::verify_32($rcheck, self::substr($sig, 0, 32));
 355      }
 356  
 357      /**
 358       * @internal You should not use this directly from another application
 359       *
 360       * @param string $S
 361       * @return bool
 362       * @throws SodiumException
 363       * @throws TypeError
 364       */
 365      public static function check_S_lt_L($S)
 366      {
 367          if (self::strlen($S) < 32) {
 368              throw new SodiumException('Signature must be 32 bytes');
 369          }
 370          static $L = array(
 371              0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
 372              0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
 373              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 374              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
 375          );
 376          /** @var array<int, int> $L */
 377          $c = 0;
 378          $n = 1;
 379          $i = 32;
 380  
 381          do {
 382              --$i;
 383              $x = self::chrToInt($S[$i]);
 384              $c |= (
 385                  (($x - $L[$i]) >> 8) & $n
 386              );
 387              $n &= (
 388                  (($x ^ $L[$i]) - 1) >> 8
 389              );
 390          } while ($i !== 0);
 391  
 392          return $c === 0;
 393      }
 394  
 395      /**
 396       * @param string $R
 397       * @return bool
 398       * @throws SodiumException
 399       * @throws TypeError
 400       */
 401      public static function small_order($R)
 402      {
 403          static $blocklist = array(
 404              /* 0 (order 4) */
 405              array(
 406                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 407                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 408                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 409                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 410              ),
 411              /* 1 (order 1) */
 412              array(
 413                  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 414                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 415                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 416                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 417              ),
 418              /* 2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
 419              array(
 420                  0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
 421                  0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
 422                  0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
 423                  0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05
 424              ),
 425              /* 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
 426              array(
 427                  0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
 428                  0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
 429                  0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
 430                  0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a
 431              ),
 432              /* p-1 (order 2) */
 433              array(
 434                  0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
 435                  0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
 436                  0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
 437                  0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85
 438              ),
 439              /* p (order 4) */
 440              array(
 441                  0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
 442                  0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
 443                  0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
 444                  0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa
 445              ),
 446              /* p+1 (order 1) */
 447              array(
 448                  0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 449                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 450                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 451                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
 452              ),
 453              /* p+2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
 454              array(
 455                  0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 456                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 457                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 458                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
 459              ),
 460              /* p+55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
 461              array(
 462                  0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 463                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 464                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 465                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
 466              ),
 467              /* 2p-1 (order 2) */
 468              array(
 469                  0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 470                  0xff, 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              ),
 474              /* 2p (order 4) */
 475              array(
 476                  0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 477                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 478                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 479                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 480              ),
 481              /* 2p+1 (order 1) */
 482              array(
 483                  0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 484                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 485                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 486                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 487              )
 488          );
 489          /** @var array<int, array<int, int>> $blocklist */
 490          $countBlocklist = count($blocklist);
 491  
 492          for ($i = 0; $i < $countBlocklist; ++$i) {
 493              $c = 0;
 494              for ($j = 0; $j < 32; ++$j) {
 495                  $c |= self::chrToInt($R[$j]) ^ $blocklist[$i][$j];
 496              }
 497              if ($c === 0) {
 498                  return true;
 499              }
 500          }
 501          return false;
 502      }
 503  }


Generated : Thu Sep 10 08:20:30 2026 Cross-referenced by PHPXref