[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Fri Oct 10 08:20:03 2025 | Cross-referenced by PHPXref |