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