[ 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       * @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($pk);
 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  
 137          # fe_1(x);
 138          # fe_add(x, x, A.Y);
 139          # fe_mul(x, x, one_minus_y);
 140          $x = self::fe_mul(
 141              self::fe_add(self::fe_1(), $A->Y),
 142              $one_minux_y
 143          );
 144  
 145          # fe_tobytes(curve25519_pk, x);
 146          return self::fe_tobytes($x);
 147      }
 148  
 149      /**
 150       * @internal You should not use this directly from another application
 151       *
 152       * @param string $sk
 153       * @return string
 154       * @throws SodiumException
 155       * @throws TypeError
 156       */
 157      public static function sk_to_pk($sk)
 158      {
 159          return self::ge_p3_tobytes(
 160              self::ge_scalarmult_base(
 161                  self::substr($sk, 0, 32)
 162              )
 163          );
 164      }
 165  
 166      /**
 167       * @internal You should not use this directly from another application
 168       *
 169       * @param string $message
 170       * @param string $sk
 171       * @return string
 172       * @throws SodiumException
 173       * @throws TypeError
 174       */
 175      public static function sign($message, $sk)
 176      {
 177          /** @var string $signature */
 178          $signature = self::sign_detached($message, $sk);
 179          return $signature . $message;
 180      }
 181  
 182      /**
 183       * @internal You should not use this directly from another application
 184       *
 185       * @param string $message A signed message
 186       * @param string $pk      Public key
 187       * @return string         Message (without signature)
 188       * @throws SodiumException
 189       * @throws TypeError
 190       */
 191      public static function sign_open($message, $pk)
 192      {
 193          /** @var string $signature */
 194          $signature = self::substr($message, 0, 64);
 195  
 196          /** @var string $message */
 197          $message = self::substr($message, 64);
 198  
 199          if (self::verify_detached($signature, $message, $pk)) {
 200              return $message;
 201          }
 202          throw new SodiumException('Invalid signature');
 203      }
 204  
 205      /**
 206       * @internal You should not use this directly from another application
 207       *
 208       * @param string $message
 209       * @param string $sk
 210       * @return string
 211       * @throws SodiumException
 212       * @throws TypeError
 213       * @psalm-suppress PossiblyInvalidArgument
 214       */
 215      public static function sign_detached($message, $sk)
 216      {
 217          # crypto_hash_sha512(az, sk, 32);
 218          $az =  hash('sha512', self::substr($sk, 0, 32), true);
 219  
 220          # az[0] &= 248;
 221          # az[31] &= 63;
 222          # az[31] |= 64;
 223          $az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
 224          $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
 225  
 226          # crypto_hash_sha512_init(&hs);
 227          # crypto_hash_sha512_update(&hs, az + 32, 32);
 228          # crypto_hash_sha512_update(&hs, m, mlen);
 229          # crypto_hash_sha512_final(&hs, nonce);
 230          $hs = hash_init('sha512');
 231          self::hash_update($hs, self::substr($az, 32, 32));
 232          self::hash_update($hs, $message);
 233          $nonceHash = hash_final($hs, true);
 234  
 235          # memmove(sig + 32, sk + 32, 32);
 236          $pk = self::substr($sk, 32, 32);
 237  
 238          # sc_reduce(nonce);
 239          # ge_scalarmult_base(&R, nonce);
 240          # ge_p3_tobytes(sig, &R);
 241          $nonce = self::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
 242          $sig = self::ge_p3_tobytes(
 243              self::ge_scalarmult_base($nonce)
 244          );
 245  
 246          # crypto_hash_sha512_init(&hs);
 247          # crypto_hash_sha512_update(&hs, sig, 64);
 248          # crypto_hash_sha512_update(&hs, m, mlen);
 249          # crypto_hash_sha512_final(&hs, hram);
 250          $hs = hash_init('sha512');
 251          self::hash_update($hs, self::substr($sig, 0, 32));
 252          self::hash_update($hs, self::substr($pk, 0, 32));
 253          self::hash_update($hs, $message);
 254          $hramHash = hash_final($hs, true);
 255  
 256          # sc_reduce(hram);
 257          # sc_muladd(sig + 32, hram, az, nonce);
 258          $hram = self::sc_reduce($hramHash);
 259          $sigAfter = self::sc_muladd($hram, $az, $nonce);
 260          $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
 261  
 262          try {
 263              ParagonIE_Sodium_Compat::memzero($az);
 264          } catch (SodiumException $ex) {
 265              $az = null;
 266          }
 267          return $sig;
 268      }
 269  
 270      /**
 271       * @internal You should not use this directly from another application
 272       *
 273       * @param string $sig
 274       * @param string $message
 275       * @param string $pk
 276       * @return bool
 277       * @throws SodiumException
 278       * @throws TypeError
 279       */
 280      public static function verify_detached($sig, $message, $pk)
 281      {
 282          if (self::strlen($sig) < 64) {
 283              throw new SodiumException('Signature is too short');
 284          }
 285          if ((self::chrToInt($sig[63]) & 240) && self::check_S_lt_L(self::substr($sig, 32, 32))) {
 286              throw new SodiumException('S < L - Invalid signature');
 287          }
 288          if (self::small_order($sig)) {
 289              throw new SodiumException('Signature is on too small of an order');
 290          }
 291          if ((self::chrToInt($sig[63]) & 224) !== 0) {
 292              throw new SodiumException('Invalid signature');
 293          }
 294          $d = 0;
 295          for ($i = 0; $i < 32; ++$i) {
 296              $d |= self::chrToInt($pk[$i]);
 297          }
 298          if ($d === 0) {
 299              throw new SodiumException('All zero public key');
 300          }
 301  
 302          /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
 303          $orig = ParagonIE_Sodium_Compat::$fastMult;
 304  
 305          // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
 306          ParagonIE_Sodium_Compat::$fastMult = true;
 307  
 308          /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */
 309          $A = self::ge_frombytes_negate_vartime($pk);
 310  
 311          /** @var string $hDigest */
 312          $hDigest = hash(
 313              'sha512',
 314              self::substr($sig, 0, 32) .
 315              self::substr($pk, 0, 32) .
 316              $message,
 317              true
 318          );
 319  
 320          /** @var string $h */
 321          $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32);
 322  
 323          /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */
 324          $R = self::ge_double_scalarmult_vartime(
 325              $h,
 326              $A,
 327              self::substr($sig, 32)
 328          );
 329  
 330          /** @var string $rcheck */
 331          $rcheck = self::ge_tobytes($R);
 332  
 333          // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
 334          ParagonIE_Sodium_Compat::$fastMult = $orig;
 335  
 336          return self::verify_32($rcheck, self::substr($sig, 0, 32));
 337      }
 338  
 339      /**
 340       * @internal You should not use this directly from another application
 341       *
 342       * @param string $S
 343       * @return bool
 344       * @throws SodiumException
 345       * @throws TypeError
 346       */
 347      public static function check_S_lt_L($S)
 348      {
 349          if (self::strlen($S) < 32) {
 350              throw new SodiumException('Signature must be 32 bytes');
 351          }
 352          static $L = array(
 353              0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
 354              0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
 355              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 356              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
 357          );
 358          /** @var array<int, int> $L */
 359          $c = 0;
 360          $n = 1;
 361          $i = 32;
 362  
 363          do {
 364              --$i;
 365              $x = self::chrToInt($S[$i]);
 366              $c |= (
 367                  (($x - $L[$i]) >> 8) & $n
 368              );
 369              $n &= (
 370                  (($x ^ $L[$i]) - 1) >> 8
 371              );
 372          } while ($i !== 0);
 373  
 374          return $c === 0;
 375      }
 376  
 377      /**
 378       * @param string $R
 379       * @return bool
 380       * @throws SodiumException
 381       * @throws TypeError
 382       */
 383      public static function small_order($R)
 384      {
 385          static $blocklist = array(
 386              /* 0 (order 4) */
 387              array(
 388                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 389                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 390                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 391                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 392              ),
 393              /* 1 (order 1) */
 394              array(
 395                  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 396                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 397                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 398                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 399              ),
 400              /* 2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
 401              array(
 402                  0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
 403                  0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
 404                  0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
 405                  0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05
 406              ),
 407              /* 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
 408              array(
 409                  0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
 410                  0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
 411                  0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
 412                  0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a
 413              ),
 414              /* p-1 (order 2) */
 415              array(
 416                  0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
 417                  0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
 418                  0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
 419                  0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85
 420              ),
 421              /* p (order 4) */
 422              array(
 423                  0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
 424                  0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
 425                  0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
 426                  0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa
 427              ),
 428              /* p+1 (order 1) */
 429              array(
 430                  0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 431                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 432                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 433                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
 434              ),
 435              /* p+2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
 436              array(
 437                  0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 438                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 439                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 440                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
 441              ),
 442              /* p+55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
 443              array(
 444                  0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 445                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 446                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 447                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
 448              ),
 449              /* 2p-1 (order 2) */
 450              array(
 451                  0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 452                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 453                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 454                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 455              ),
 456              /* 2p (order 4) */
 457              array(
 458                  0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 459                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 460                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 461                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 462              ),
 463              /* 2p+1 (order 1) */
 464              array(
 465                  0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 466                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 467                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 468                  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 469              )
 470          );
 471          /** @var array<int, array<int, int>> $blocklist */
 472          $countBlocklist = count($blocklist);
 473  
 474          for ($i = 0; $i < $countBlocklist; ++$i) {
 475              $c = 0;
 476              for ($j = 0; $j < 32; ++$j) {
 477                  $c |= self::chrToInt($R[$j]) ^ $blocklist[$i][$j];
 478              }
 479              if ($c === 0) {
 480                  return true;
 481              }
 482          }
 483          return false;
 484      }
 485  }


Generated : Sat Apr 27 08:20:02 2024 Cross-referenced by PHPXref