run:R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
3.58 KB
2026-03-11 16:18:52
R W Run
3.51 KB
2026-03-11 16:18:52
R W Run
15.51 KB
2026-03-11 16:18:52
R W Run
23.63 KB
2026-03-11 16:18:52
R W Run
12.59 KB
2026-03-11 16:18:52
R W Run
140.27 KB
2026-03-11 16:18:52
R W Run
18.05 KB
2026-03-11 16:18:52
R W Run
4.04 KB
2026-03-11 16:18:52
R W Run
3.59 KB
2026-03-11 16:18:52
R W Run
1.54 KB
2026-03-11 16:18:52
R W Run
21.37 KB
2026-03-11 16:18:52
R W Run
8.04 KB
2026-03-11 16:18:52
R W Run
8.04 KB
2026-03-11 16:18:52
R W Run
28.23 KB
2026-03-11 16:18:52
R W Run
8.22 KB
2026-03-11 16:18:52
R W Run
3.24 KB
2026-03-11 16:18:52
R W Run
1.34 KB
2026-03-11 16:18:52
R W Run
error_log
📄Ed25519.php
1<?php
2
3if (class_exists('ParagonIE_Sodium_Core_Ed25519', false)) {
4 return;
5}
6if (!class_exists('ParagonIE_Sodium_Core_Curve25519', false)) {
7 require_once dirname(__FILE__) . '/Curve25519.php';
8}
9
10/**
11 * Class ParagonIE_Sodium_Core_Ed25519
12 */
13abstract 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}
579