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
160.83 KB
2026-03-11 16:18:52
R W Run
53.68 KB
2026-03-11 16:18:52
R W Run
53.83 KB
2026-03-11 16:18:52
R W Run
53.53 KB
2026-03-11 16:18:52
R W Run
158 By
2026-03-11 16:18:52
R W Run
error_log
📄File.php
1<?php
2
3if (class_exists('ParagonIE_Sodium_File', false)) {
4 return;
5}
6/**
7 * Class ParagonIE_Sodium_File
8 */
9class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
10{
11 /* PHP's default buffer size is 8192 for fread()/fwrite(). */
12 const BUFFER_SIZE = 8192;
13
14 /**
15 * Box a file (rather than a string). Uses less memory than
16 * ParagonIE_Sodium_Compat::crypto_box(), but produces
17 * the same result.
18 *
19 * @param string $inputFile Absolute path to a file on the filesystem
20 * @param string $outputFile Absolute path to a file on the filesystem
21 * @param string $nonce Number to be used only once
22 * @param string $keyPair ECDH secret key and ECDH public key concatenated
23 *
24 * @return bool
25 * @throws SodiumException
26 * @throws TypeError
27 */
28 public static function box(
29 $inputFile,
30 $outputFile,
31 $nonce,
32 #[\SensitiveParameter]
33 $keyPair
34 ) {
35 /* Type checks: */
36 if (!is_string($inputFile)) {
37 throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
38 }
39 if (!is_string($outputFile)) {
40 throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
41 }
42 if (!is_string($nonce)) {
43 throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.');
44 }
45
46 /* Input validation: */
47 if (!is_string($keyPair)) {
48 throw new TypeError('Argument 4 must be a string, ' . gettype($keyPair) . ' given.');
49 }
50 if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_NONCEBYTES) {
51 throw new TypeError('Argument 3 must be CRYPTO_BOX_NONCEBYTES bytes');
52 }
53 if (self::strlen($keyPair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
54 throw new TypeError('Argument 4 must be CRYPTO_BOX_KEYPAIRBYTES bytes');
55 }
56
57 /** @var int $size */
58 $size = filesize($inputFile);
59 if (!is_int($size)) {
60 throw new SodiumException('Could not obtain the file size');
61 }
62
63 /** @var resource $ifp */
64 $ifp = fopen($inputFile, 'rb');
65 if (!is_resource($ifp)) {
66 throw new SodiumException('Could not open input file for reading');
67 }
68
69 /** @var resource $ofp */
70 $ofp = fopen($outputFile, 'wb');
71 if (!is_resource($ofp)) {
72 fclose($ifp);
73 throw new SodiumException('Could not open output file for writing');
74 }
75
76 $res = self::box_encrypt($ifp, $ofp, $size, $nonce, $keyPair);
77 fclose($ifp);
78 fclose($ofp);
79 return $res;
80 }
81
82 /**
83 * Open a boxed file (rather than a string). Uses less memory than
84 * ParagonIE_Sodium_Compat::crypto_box_open(), but produces
85 * the same result.
86 *
87 * Warning: Does not protect against TOCTOU attacks. You should
88 * just load the file into memory and use crypto_box_open() if
89 * you are worried about those.
90 *
91 * @param string $inputFile
92 * @param string $outputFile
93 * @param string $nonce
94 * @param string $keypair
95 * @return bool
96 * @throws SodiumException
97 * @throws TypeError
98 */
99 public static function box_open(
100 $inputFile,
101 $outputFile,
102 $nonce,
103 #[\SensitiveParameter]
104 $keypair
105 ) {
106 /* Type checks: */
107 if (!is_string($inputFile)) {
108 throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
109 }
110 if (!is_string($outputFile)) {
111 throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
112 }
113 if (!is_string($nonce)) {
114 throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.');
115 }
116 if (!is_string($keypair)) {
117 throw new TypeError('Argument 4 must be a string, ' . gettype($keypair) . ' given.');
118 }
119
120 /* Input validation: */
121 if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_NONCEBYTES) {
122 throw new TypeError('Argument 4 must be CRYPTO_BOX_NONCEBYTES bytes');
123 }
124 if (self::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
125 throw new TypeError('Argument 4 must be CRYPTO_BOX_KEYPAIRBYTES bytes');
126 }
127
128 if (!file_exists($inputFile)) {
129 throw new SodiumException('Input file does not exist');
130 }
131 /** @var int $size */
132 $size = filesize($inputFile);
133 if (!is_int($size)) {
134 throw new SodiumException('Could not obtain the file size');
135 }
136
137 /** @var resource $ifp */
138 $ifp = fopen($inputFile, 'rb');
139 if (!is_resource($ifp)) {
140 throw new SodiumException('Could not open input file for reading');
141 }
142
143 /** @var resource $ofp */
144 $ofp = @fopen($outputFile, 'wb');
145 if (!is_resource($ofp)) {
146 fclose($ifp);
147 throw new SodiumException('Could not open output file for writing');
148 }
149
150 $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $keypair);
151 fclose($ifp);
152 fclose($ofp);
153 try {
154 ParagonIE_Sodium_Compat::memzero($nonce);
155 ParagonIE_Sodium_Compat::memzero($ephKeypair);
156 } catch (SodiumException $ex) {
157 if (isset($ephKeypair)) {
158 unset($ephKeypair);
159 }
160 }
161 return $res;
162 }
163
164 /**
165 * Seal a file (rather than a string). Uses less memory than
166 * ParagonIE_Sodium_Compat::crypto_box_seal(), but produces
167 * the same result.
168 *
169 * @param string $inputFile Absolute path to a file on the filesystem
170 * @param string $outputFile Absolute path to a file on the filesystem
171 * @param string $publicKey ECDH public key
172 *
173 * @return bool
174 * @throws SodiumException
175 * @throws TypeError
176 */
177 public static function box_seal(
178 $inputFile,
179 $outputFile,
180 #[\SensitiveParameter]
181 $publicKey
182 ) {
183 /* Type checks: */
184 if (!is_string($inputFile)) {
185 throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
186 }
187 if (!is_string($outputFile)) {
188 throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
189 }
190 if (!is_string($publicKey)) {
191 throw new TypeError('Argument 3 must be a string, ' . gettype($publicKey) . ' given.');
192 }
193
194 /* Input validation: */
195 if (self::strlen($publicKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) {
196 throw new TypeError('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES bytes');
197 }
198
199 if (!file_exists($inputFile)) {
200 throw new SodiumException('Input file does not exist');
201 }
202 /** @var int $size */
203 $size = filesize($inputFile);
204 if (!is_int($size)) {
205 throw new SodiumException('Could not obtain the file size');
206 }
207
208 /** @var resource $ifp */
209 $ifp = fopen($inputFile, 'rb');
210 if (!is_resource($ifp)) {
211 throw new SodiumException('Could not open input file for reading');
212 }
213
214 /** @var resource $ofp */
215 $ofp = @fopen($outputFile, 'wb');
216 if (!is_resource($ofp)) {
217 fclose($ifp);
218 throw new SodiumException('Could not open output file for writing');
219 }
220
221 /** @var string $ephKeypair */
222 $ephKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair();
223
224 /** @var string $msgKeypair */
225 $msgKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey(
226 ParagonIE_Sodium_Compat::crypto_box_secretkey($ephKeypair),
227 $publicKey
228 );
229
230 /** @var string $ephemeralPK */
231 $ephemeralPK = ParagonIE_Sodium_Compat::crypto_box_publickey($ephKeypair);
232
233 /** @var string $nonce */
234 $nonce = ParagonIE_Sodium_Compat::crypto_generichash(
235 $ephemeralPK . $publicKey,
236 '',
237 24
238 );
239
240 /** @var int $firstWrite */
241 $firstWrite = fwrite(
242 $ofp,
243 $ephemeralPK,
244 ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES
245 );
246 if (!is_int($firstWrite)) {
247 fclose($ifp);
248 fclose($ofp);
249 ParagonIE_Sodium_Compat::memzero($ephKeypair);
250 throw new SodiumException('Could not write to output file');
251 }
252 if ($firstWrite !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) {
253 ParagonIE_Sodium_Compat::memzero($ephKeypair);
254 fclose($ifp);
255 fclose($ofp);
256 throw new SodiumException('Error writing public key to output file');
257 }
258
259 $res = self::box_encrypt($ifp, $ofp, $size, $nonce, $msgKeypair);
260 fclose($ifp);
261 fclose($ofp);
262 try {
263 ParagonIE_Sodium_Compat::memzero($nonce);
264 ParagonIE_Sodium_Compat::memzero($ephKeypair);
265 } catch (SodiumException $ex) {
266 /** @psalm-suppress PossiblyUndefinedVariable */
267 unset($ephKeypair);
268 }
269 return $res;
270 }
271
272 /**
273 * Open a sealed file (rather than a string). Uses less memory than
274 * ParagonIE_Sodium_Compat::crypto_box_seal_open(), but produces
275 * the same result.
276 *
277 * Warning: Does not protect against TOCTOU attacks. You should
278 * just load the file into memory and use crypto_box_seal_open() if
279 * you are worried about those.
280 *
281 * @param string $inputFile
282 * @param string $outputFile
283 * @param string $ecdhKeypair
284 * @return bool
285 * @throws SodiumException
286 * @throws TypeError
287 */
288 public static function box_seal_open(
289 $inputFile,
290 $outputFile,
291 #[\SensitiveParameter]
292 $ecdhKeypair
293 ) {
294 /* Type checks: */
295 if (!is_string($inputFile)) {
296 throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
297 }
298 if (!is_string($outputFile)) {
299 throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
300 }
301 if (!is_string($ecdhKeypair)) {
302 throw new TypeError('Argument 3 must be a string, ' . gettype($ecdhKeypair) . ' given.');
303 }
304
305 /* Input validation: */
306 if (self::strlen($ecdhKeypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
307 throw new TypeError('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES bytes');
308 }
309
310 $publicKey = ParagonIE_Sodium_Compat::crypto_box_publickey($ecdhKeypair);
311
312 if (!file_exists($inputFile)) {
313 throw new SodiumException('Input file does not exist');
314 }
315 /** @var int $size */
316 $size = filesize($inputFile);
317 if (!is_int($size)) {
318 throw new SodiumException('Could not obtain the file size');
319 }
320
321 /** @var resource $ifp */
322 $ifp = fopen($inputFile, 'rb');
323 if (!is_resource($ifp)) {
324 throw new SodiumException('Could not open input file for reading');
325 }
326
327 /** @var resource $ofp */
328 $ofp = @fopen($outputFile, 'wb');
329 if (!is_resource($ofp)) {
330 fclose($ifp);
331 throw new SodiumException('Could not open output file for writing');
332 }
333
334 $ephemeralPK = fread($ifp, ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES);
335 if (!is_string($ephemeralPK)) {
336 throw new SodiumException('Could not read input file');
337 }
338 if (self::strlen($ephemeralPK) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) {
339 fclose($ifp);
340 fclose($ofp);
341 throw new SodiumException('Could not read public key from sealed file');
342 }
343
344 $nonce = ParagonIE_Sodium_Compat::crypto_generichash(
345 $ephemeralPK . $publicKey,
346 '',
347 24
348 );
349 $msgKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey(
350 ParagonIE_Sodium_Compat::crypto_box_secretkey($ecdhKeypair),
351 $ephemeralPK
352 );
353
354 $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $msgKeypair);
355 fclose($ifp);
356 fclose($ofp);
357 try {
358 ParagonIE_Sodium_Compat::memzero($nonce);
359 ParagonIE_Sodium_Compat::memzero($ephKeypair);
360 } catch (SodiumException $ex) {
361 if (isset($ephKeypair)) {
362 unset($ephKeypair);
363 }
364 }
365 return $res;
366 }
367
368 /**
369 * Calculate the BLAKE2b hash of a file.
370 *
371 * @param string $filePath Absolute path to a file on the filesystem
372 * @param string|null $key BLAKE2b key
373 * @param int $outputLength Length of hash output
374 *
375 * @return string BLAKE2b hash
376 * @throws SodiumException
377 * @throws TypeError
378 * @psalm-suppress FailedTypeResolution
379 */
380 public static function generichash(
381 $filePath,
382 #[\SensitiveParameter]
383 $key = '',
384 $outputLength = 32
385 ) {
386 /* Type checks: */
387 if (!is_string($filePath)) {
388 throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.');
389 }
390 if (!is_string($key)) {
391 if (is_null($key)) {
392 $key = '';
393 } else {
394 throw new TypeError('Argument 2 must be a string, ' . gettype($key) . ' given.');
395 }
396 }
397 if (!is_int($outputLength)) {
398 if (!is_numeric($outputLength)) {
399 throw new TypeError('Argument 3 must be an integer, ' . gettype($outputLength) . ' given.');
400 }
401 $outputLength = (int) $outputLength;
402 }
403
404 /* Input validation: */
405 if (!empty($key)) {
406 if (self::strlen($key) < ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
407 throw new TypeError('Argument 2 must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes');
408 }
409 if (self::strlen($key) > ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
410 throw new TypeError('Argument 2 must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes');
411 }
412 }
413 if ($outputLength < ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_BYTES_MIN) {
414 throw new SodiumException('Argument 3 must be at least CRYPTO_GENERICHASH_BYTES_MIN');
415 }
416 if ($outputLength > ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_BYTES_MAX) {
417 throw new SodiumException('Argument 3 must be at least CRYPTO_GENERICHASH_BYTES_MAX');
418 }
419
420 if (!file_exists($filePath)) {
421 throw new SodiumException('File does not exist');
422 }
423 /** @var int $size */
424 $size = filesize($filePath);
425 if (!is_int($size)) {
426 throw new SodiumException('Could not obtain the file size');
427 }
428
429 /** @var resource $fp */
430 $fp = fopen($filePath, 'rb');
431 if (!is_resource($fp)) {
432 throw new SodiumException('Could not open input file for reading');
433 }
434 $ctx = ParagonIE_Sodium_Compat::crypto_generichash_init($key, $outputLength);
435 while ($size > 0) {
436 $blockSize = $size > 64
437 ? 64
438 : $size;
439 $read = fread($fp, $blockSize);
440 if (!is_string($read)) {
441 throw new SodiumException('Could not read input file');
442 }
443 ParagonIE_Sodium_Compat::crypto_generichash_update($ctx, $read);
444 $size -= $blockSize;
445 }
446
447 fclose($fp);
448 return ParagonIE_Sodium_Compat::crypto_generichash_final($ctx, $outputLength);
449 }
450
451 /**
452 * Encrypt a file (rather than a string). Uses less memory than
453 * ParagonIE_Sodium_Compat::crypto_secretbox(), but produces
454 * the same result.
455 *
456 * @param string $inputFile Absolute path to a file on the filesystem
457 * @param string $outputFile Absolute path to a file on the filesystem
458 * @param string $nonce Number to be used only once
459 * @param string $key Encryption key
460 *
461 * @return bool
462 * @throws SodiumException
463 * @throws TypeError
464 */
465 public static function secretbox(
466 $inputFile,
467 $outputFile,
468 $nonce,
469 #[\SensitiveParameter]
470 $key
471 ) {
472 /* Type checks: */
473 if (!is_string($inputFile)) {
474 throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given..');
475 }
476 if (!is_string($outputFile)) {
477 throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
478 }
479 if (!is_string($nonce)) {
480 throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.');
481 }
482
483 /* Input validation: */
484 if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_NONCEBYTES) {
485 throw new TypeError('Argument 3 must be CRYPTO_SECRETBOX_NONCEBYTES bytes');
486 }
487 if (!is_string($key)) {
488 throw new TypeError('Argument 4 must be a string, ' . gettype($key) . ' given.');
489 }
490 if (self::strlen($key) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_KEYBYTES) {
491 throw new TypeError('Argument 4 must be CRYPTO_SECRETBOX_KEYBYTES bytes');
492 }
493
494 if (!file_exists($inputFile)) {
495 throw new SodiumException('Input file does not exist');
496 }
497 /** @var int $size */
498 $size = filesize($inputFile);
499 if (!is_int($size)) {
500 throw new SodiumException('Could not obtain the file size');
501 }
502
503 /** @var resource $ifp */
504 $ifp = @fopen($inputFile, 'rb');
505 if (!is_resource($ifp)) {
506 throw new SodiumException('Could not open input file for reading');
507 }
508
509 /** @var resource $ofp */
510 $ofp = fopen($outputFile, 'wb');
511 if (!is_resource($ofp)) {
512 fclose($ifp);
513 throw new SodiumException('Could not open output file for writing');
514 }
515
516 $res = self::secretbox_encrypt($ifp, $ofp, $size, $nonce, $key);
517 fclose($ifp);
518 fclose($ofp);
519 return $res;
520 }
521 /**
522 * Seal a file (rather than a string). Uses less memory than
523 * ParagonIE_Sodium_Compat::crypto_secretbox_open(), but produces
524 * the same result.
525 *
526 * Warning: Does not protect against TOCTOU attacks. You should
527 * just load the file into memory and use crypto_secretbox_open() if
528 * you are worried about those.
529 *
530 * @param string $inputFile
531 * @param string $outputFile
532 * @param string $nonce
533 * @param string $key
534 * @return bool
535 * @throws SodiumException
536 * @throws TypeError
537 */
538 public static function secretbox_open(
539 $inputFile,
540 $outputFile,
541 $nonce,
542 #[\SensitiveParameter]
543 $key
544 ) {
545 /* Type checks: */
546 if (!is_string($inputFile)) {
547 throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
548 }
549 if (!is_string($outputFile)) {
550 throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
551 }
552 if (!is_string($nonce)) {
553 throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.');
554 }
555 if (!is_string($key)) {
556 throw new TypeError('Argument 4 must be a string, ' . gettype($key) . ' given.');
557 }
558
559 /* Input validation: */
560 if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_NONCEBYTES) {
561 throw new TypeError('Argument 3 must be CRYPTO_SECRETBOX_NONCEBYTES bytes');
562 }
563 if (self::strlen($key) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_KEYBYTES) {
564 throw new TypeError('Argument 4 must be CRYPTO_SECRETBOX_KEYBYTES bytes');
565 }
566
567 if (!file_exists($inputFile)) {
568 throw new SodiumException('Input file does not exist');
569 }
570 /** @var int $size */
571 $size = filesize($inputFile);
572 if (!is_int($size)) {
573 throw new SodiumException('Could not obtain the file size');
574 }
575
576 /** @var resource $ifp */
577 $ifp = fopen($inputFile, 'rb');
578 if (!is_resource($ifp)) {
579 throw new SodiumException('Could not open input file for reading');
580 }
581
582 /** @var resource $ofp */
583 $ofp = @fopen($outputFile, 'wb');
584 if (!is_resource($ofp)) {
585 fclose($ifp);
586 throw new SodiumException('Could not open output file for writing');
587 }
588
589 $res = self::secretbox_decrypt($ifp, $ofp, $size, $nonce, $key);
590 fclose($ifp);
591 fclose($ofp);
592 try {
593 ParagonIE_Sodium_Compat::memzero($key);
594 } catch (SodiumException $ex) {
595 /** @psalm-suppress PossiblyUndefinedVariable */
596 unset($key);
597 }
598 return $res;
599 }
600
601 /**
602 * Sign a file (rather than a string). Uses less memory than
603 * ParagonIE_Sodium_Compat::crypto_sign_detached(), but produces
604 * the same result.
605 *
606 * @param string $filePath Absolute path to a file on the filesystem
607 * @param string $secretKey Secret signing key
608 *
609 * @return string Ed25519 signature
610 * @throws SodiumException
611 * @throws TypeError
612 */
613 public static function sign(
614 $filePath,
615 #[\SensitiveParameter]
616 $secretKey
617 ) {
618 /* Type checks: */
619 if (!is_string($filePath)) {
620 throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.');
621 }
622 if (!is_string($secretKey)) {
623 throw new TypeError('Argument 2 must be a string, ' . gettype($secretKey) . ' given.');
624 }
625
626 /* Input validation: */
627 if (self::strlen($secretKey) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_SECRETKEYBYTES) {
628 throw new TypeError('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES bytes');
629 }
630 if (PHP_INT_SIZE === 4) {
631 return self::sign_core32($filePath, $secretKey);
632 }
633
634 if (!file_exists($filePath)) {
635 throw new SodiumException('File does not exist');
636 }
637 /** @var int $size */
638 $size = filesize($filePath);
639 if (!is_int($size)) {
640 throw new SodiumException('Could not obtain the file size');
641 }
642
643 /** @var resource $fp */
644 $fp = fopen($filePath, 'rb');
645 if (!is_resource($fp)) {
646 throw new SodiumException('Could not open input file for reading');
647 }
648
649 /** @var string $az */
650 $az = hash('sha512', self::substr($secretKey, 0, 32), true);
651
652 $az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
653 $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
654
655 $hs = hash_init('sha512');
656 self::hash_update($hs, self::substr($az, 32, 32));
657 /** @var resource $hs */
658 $hs = self::updateHashWithFile($hs, $fp, $size);
659
660 /** @var string $nonceHash */
661 $nonceHash = hash_final($hs, true);
662
663 /** @var string $pk */
664 $pk = self::substr($secretKey, 32, 32);
665
666 /** @var string $nonce */
667 $nonce = ParagonIE_Sodium_Core_Ed25519::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
668
669 /** @var string $sig */
670 $sig = ParagonIE_Sodium_Core_Ed25519::ge_p3_tobytes(
671 ParagonIE_Sodium_Core_Ed25519::ge_scalarmult_base($nonce)
672 );
673
674 $hs = hash_init('sha512');
675 self::hash_update($hs, self::substr($sig, 0, 32));
676 self::hash_update($hs, self::substr($pk, 0, 32));
677 /** @var resource $hs */
678 $hs = self::updateHashWithFile($hs, $fp, $size);
679
680 /** @var string $hramHash */
681 $hramHash = hash_final($hs, true);
682
683 /** @var string $hram */
684 $hram = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hramHash);
685
686 /** @var string $sigAfter */
687 $sigAfter = ParagonIE_Sodium_Core_Ed25519::sc_muladd($hram, $az, $nonce);
688
689 /** @var string $sig */
690 $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
691
692 try {
693 ParagonIE_Sodium_Compat::memzero($az);
694 } catch (SodiumException $ex) {
695 $az = null;
696 }
697 fclose($fp);
698 return $sig;
699 }
700
701 /**
702 * Verify a file (rather than a string). Uses less memory than
703 * ParagonIE_Sodium_Compat::crypto_sign_verify_detached(), but
704 * produces the same result.
705 *
706 * @param string $sig Ed25519 signature
707 * @param string $filePath Absolute path to a file on the filesystem
708 * @param string $publicKey Signing public key
709 *
710 * @return bool
711 * @throws SodiumException
712 * @throws TypeError
713 * @throws Exception
714 */
715 public static function verify(
716 $sig,
717 $filePath,
718 $publicKey
719 ) {
720 /* Type checks: */
721 if (!is_string($sig)) {
722 throw new TypeError('Argument 1 must be a string, ' . gettype($sig) . ' given.');
723 }
724 if (!is_string($filePath)) {
725 throw new TypeError('Argument 2 must be a string, ' . gettype($filePath) . ' given.');
726 }
727 if (!is_string($publicKey)) {
728 throw new TypeError('Argument 3 must be a string, ' . gettype($publicKey) . ' given.');
729 }
730
731 /* Input validation: */
732 if (self::strlen($sig) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_BYTES) {
733 throw new TypeError('Argument 1 must be CRYPTO_SIGN_BYTES bytes');
734 }
735 if (self::strlen($publicKey) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_PUBLICKEYBYTES) {
736 throw new TypeError('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES bytes');
737 }
738 if (self::strlen($sig) < 64) {
739 throw new SodiumException('Signature is too short');
740 }
741
742 if (PHP_INT_SIZE === 4) {
743 return self::verify_core32($sig, $filePath, $publicKey);
744 }
745
746 /* Security checks */
747 if (
748 (ParagonIE_Sodium_Core_Ed25519::chrToInt($sig[63]) & 240)
749 &&
750 ParagonIE_Sodium_Core_Ed25519::check_S_lt_L(self::substr($sig, 32, 32))
751 ) {
752 throw new SodiumException('S < L - Invalid signature');
753 }
754 if (ParagonIE_Sodium_Core_Ed25519::small_order($sig)) {
755 throw new SodiumException('Signature is on too small of an order');
756 }
757 if ((self::chrToInt($sig[63]) & 224) !== 0) {
758 throw new SodiumException('Invalid signature');
759 }
760 $d = 0;
761 for ($i = 0; $i < 32; ++$i) {
762 $d |= self::chrToInt($publicKey[$i]);
763 }
764 if ($d === 0) {
765 throw new SodiumException('All zero public key');
766 }
767
768 if (!file_exists($filePath)) {
769 throw new SodiumException('File does not exist');
770 }
771 /** @var int $size */
772 $size = filesize($filePath);
773 if (!is_int($size)) {
774 throw new SodiumException('Could not obtain the file size');
775 }
776
777 /** @var resource $fp */
778 $fp = fopen($filePath, 'rb');
779 if (!is_resource($fp)) {
780 throw new SodiumException('Could not open input file for reading');
781 }
782
783 /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
784 $orig = ParagonIE_Sodium_Compat::$fastMult;
785
786 // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
787 ParagonIE_Sodium_Compat::$fastMult = true;
788
789 if (ParagonIE_Sodium_Core_Ed25519::small_order($publicKey)) {
790 throw new SodiumException('Public key has small order');
791 }
792 /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */
793 $A = ParagonIE_Sodium_Core_Ed25519::ge_frombytes_negate_vartime($publicKey);
794 if (!ParagonIE_Sodium_Core_Ed25519::is_on_main_subgroup($A)) {
795 throw new SodiumException('Public key is not on a member of the main subgroup');
796 }
797
798 $hs = hash_init('sha512');
799 self::hash_update($hs, self::substr($sig, 0, 32));
800 self::hash_update($hs, self::substr($publicKey, 0, 32));
801 /** @var resource $hs */
802 $hs = self::updateHashWithFile($hs, $fp, $size);
803 /** @var string $hDigest */
804 $hDigest = hash_final($hs, true);
805
806 /** @var string $h */
807 $h = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32);
808
809 /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */
810 $R = ParagonIE_Sodium_Core_Ed25519::ge_double_scalarmult_vartime(
811 $h,
812 $A,
813 self::substr($sig, 32)
814 );
815
816 /** @var string $rcheck */
817 $rcheck = ParagonIE_Sodium_Core_Ed25519::ge_tobytes($R);
818
819 // Close the file handle
820 fclose($fp);
821
822 // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
823 ParagonIE_Sodium_Compat::$fastMult = $orig;
824 return self::verify_32($rcheck, self::substr($sig, 0, 32));
825 }
826
827 /**
828 * @param resource $ifp
829 * @param resource $ofp
830 * @param int $mlen
831 * @param string $nonce
832 * @param string $boxKeypair
833 * @return bool
834 * @throws SodiumException
835 * @throws TypeError
836 */
837 protected static function box_encrypt($ifp, $ofp, $mlen, $nonce, $boxKeypair)
838 {
839 if (PHP_INT_SIZE === 4) {
840 return self::secretbox_encrypt(
841 $ifp,
842 $ofp,
843 $mlen,
844 $nonce,
845 ParagonIE_Sodium_Crypto32::box_beforenm(
846 ParagonIE_Sodium_Crypto32::box_secretkey($boxKeypair),
847 ParagonIE_Sodium_Crypto32::box_publickey($boxKeypair)
848 )
849 );
850 }
851 return self::secretbox_encrypt(
852 $ifp,
853 $ofp,
854 $mlen,
855 $nonce,
856 ParagonIE_Sodium_Crypto::box_beforenm(
857 ParagonIE_Sodium_Crypto::box_secretkey($boxKeypair),
858 ParagonIE_Sodium_Crypto::box_publickey($boxKeypair)
859 )
860 );
861 }
862
863
864 /**
865 * @param resource $ifp
866 * @param resource $ofp
867 * @param int $mlen
868 * @param string $nonce
869 * @param string $boxKeypair
870 * @return bool
871 * @throws SodiumException
872 * @throws TypeError
873 */
874 protected static function box_decrypt($ifp, $ofp, $mlen, $nonce, $boxKeypair)
875 {
876 if (PHP_INT_SIZE === 4) {
877 return self::secretbox_decrypt(
878 $ifp,
879 $ofp,
880 $mlen,
881 $nonce,
882 ParagonIE_Sodium_Crypto32::box_beforenm(
883 ParagonIE_Sodium_Crypto32::box_secretkey($boxKeypair),
884 ParagonIE_Sodium_Crypto32::box_publickey($boxKeypair)
885 )
886 );
887 }
888 return self::secretbox_decrypt(
889 $ifp,
890 $ofp,
891 $mlen,
892 $nonce,
893 ParagonIE_Sodium_Crypto::box_beforenm(
894 ParagonIE_Sodium_Crypto::box_secretkey($boxKeypair),
895 ParagonIE_Sodium_Crypto::box_publickey($boxKeypair)
896 )
897 );
898 }
899
900 /**
901 * Encrypt a file
902 *
903 * @param resource $ifp
904 * @param resource $ofp
905 * @param int $mlen
906 * @param string $nonce
907 * @param string $key
908 * @return bool
909 * @throws SodiumException
910 * @throws TypeError
911 */
912 protected static function secretbox_encrypt($ifp, $ofp, $mlen, $nonce, $key)
913 {
914 if (PHP_INT_SIZE === 4) {
915 return self::secretbox_encrypt_core32($ifp, $ofp, $mlen, $nonce, $key);
916 }
917
918 $plaintext = fread($ifp, 32);
919 if (!is_string($plaintext)) {
920 throw new SodiumException('Could not read input file');
921 }
922 $first32 = self::ftell($ifp);
923
924 /** @var string $subkey */
925 $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
926
927 /** @var string $realNonce */
928 $realNonce = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
929
930 /** @var string $block0 */
931 $block0 = str_repeat("\x00", 32);
932
933 /** @var int $mlen - Length of the plaintext message */
934 $mlen0 = $mlen;
935 if ($mlen0 > 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES) {
936 $mlen0 = 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES;
937 }
938 $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
939
940 /** @var string $block0 */
941 $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor(
942 $block0,
943 $realNonce,
944 $subkey
945 );
946
947 $state = new ParagonIE_Sodium_Core_Poly1305_State(
948 ParagonIE_Sodium_Core_Util::substr(
949 $block0,
950 0,
951 ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES
952 )
953 );
954
955 // Pre-write 16 blank bytes for the Poly1305 tag
956 $start = self::ftell($ofp);
957 fwrite($ofp, str_repeat("\x00", 16));
958
959 /** @var string $c */
960 $cBlock = ParagonIE_Sodium_Core_Util::substr(
961 $block0,
962 ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES
963 );
964 $state->update($cBlock);
965 fwrite($ofp, $cBlock);
966 $mlen -= 32;
967
968 /** @var int $iter */
969 $iter = 1;
970
971 /** @var int $incr */
972 $incr = self::BUFFER_SIZE >> 6;
973
974 /*
975 * Set the cursor to the end of the first half-block. All future bytes will
976 * generated from salsa20_xor_ic, starting from 1 (second block).
977 */
978 fseek($ifp, $first32, SEEK_SET);
979
980 while ($mlen > 0) {
981 $blockSize = $mlen > self::BUFFER_SIZE
982 ? self::BUFFER_SIZE
983 : $mlen;
984 $plaintext = fread($ifp, $blockSize);
985 if (!is_string($plaintext)) {
986 throw new SodiumException('Could not read input file');
987 }
988 $cBlock = ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
989 $plaintext,
990 $realNonce,
991 $iter,
992 $subkey
993 );
994 fwrite($ofp, $cBlock, $blockSize);
995 $state->update($cBlock);
996
997 $mlen -= $blockSize;
998 $iter += $incr;
999 }
1000 try {
1001 ParagonIE_Sodium_Compat::memzero($block0);
1002 ParagonIE_Sodium_Compat::memzero($subkey);
1003 } catch (SodiumException $ex) {
1004 $block0 = null;
1005 $subkey = null;
1006 }
1007 $end = self::ftell($ofp);
1008
1009 /*
1010 * Write the Poly1305 authentication tag that provides integrity
1011 * over the ciphertext (encrypt-then-MAC)
1012 */
1013 fseek($ofp, $start, SEEK_SET);
1014 fwrite($ofp, $state->finish(), ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_MACBYTES);
1015 fseek($ofp, $end, SEEK_SET);
1016 unset($state);
1017
1018 return true;
1019 }
1020
1021 /**
1022 * Decrypt a file
1023 *
1024 * @param resource $ifp
1025 * @param resource $ofp
1026 * @param int $mlen
1027 * @param string $nonce
1028 * @param string $key
1029 * @return bool
1030 * @throws SodiumException
1031 * @throws TypeError
1032 */
1033 protected static function secretbox_decrypt($ifp, $ofp, $mlen, $nonce, $key)
1034 {
1035 if (PHP_INT_SIZE === 4) {
1036 return self::secretbox_decrypt_core32($ifp, $ofp, $mlen, $nonce, $key);
1037 }
1038 $tag = fread($ifp, 16);
1039 if (!is_string($tag)) {
1040 throw new SodiumException('Could not read input file');
1041 }
1042
1043 /** @var string $subkey */
1044 $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
1045
1046 /** @var string $realNonce */
1047 $realNonce = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
1048
1049 /** @var string $block0 */
1050 $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20(
1051 64,
1052 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1053 $subkey
1054 );
1055
1056 /* Verify the Poly1305 MAC -before- attempting to decrypt! */
1057 $state = new ParagonIE_Sodium_Core_Poly1305_State(self::substr($block0, 0, 32));
1058 if (!self::onetimeauth_verify($state, $ifp, $tag, $mlen)) {
1059 throw new SodiumException('Invalid MAC');
1060 }
1061
1062 /*
1063 * Set the cursor to the end of the first half-block. All future bytes will
1064 * generated from salsa20_xor_ic, starting from 1 (second block).
1065 */
1066 $first32 = fread($ifp, 32);
1067 if (!is_string($first32)) {
1068 throw new SodiumException('Could not read input file');
1069 }
1070 $first32len = self::strlen($first32);
1071 fwrite(
1072 $ofp,
1073 self::xorStrings(
1074 self::substr($block0, 32, $first32len),
1075 self::substr($first32, 0, $first32len)
1076 )
1077 );
1078 $mlen -= 32;
1079
1080 /** @var int $iter */
1081 $iter = 1;
1082
1083 /** @var int $incr */
1084 $incr = self::BUFFER_SIZE >> 6;
1085
1086 /* Decrypts ciphertext, writes to output file. */
1087 while ($mlen > 0) {
1088 $blockSize = $mlen > self::BUFFER_SIZE
1089 ? self::BUFFER_SIZE
1090 : $mlen;
1091 $ciphertext = fread($ifp, $blockSize);
1092 if (!is_string($ciphertext)) {
1093 throw new SodiumException('Could not read input file');
1094 }
1095 $pBlock = ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
1096 $ciphertext,
1097 $realNonce,
1098 $iter,
1099 $subkey
1100 );
1101 fwrite($ofp, $pBlock, $blockSize);
1102 $mlen -= $blockSize;
1103 $iter += $incr;
1104 }
1105 return true;
1106 }
1107
1108 /**
1109 * @param ParagonIE_Sodium_Core_Poly1305_State $state
1110 * @param resource $ifp
1111 * @param string $tag
1112 * @param int $mlen
1113 * @return bool
1114 * @throws SodiumException
1115 * @throws TypeError
1116 */
1117 protected static function onetimeauth_verify(
1118 ParagonIE_Sodium_Core_Poly1305_State $state,
1119 $ifp,
1120 $tag = '',
1121 $mlen = 0
1122 ) {
1123 /** @var int $pos */
1124 $pos = self::ftell($ifp);
1125
1126 /** @var int $iter */
1127 $iter = 1;
1128
1129 /** @var int $incr */
1130 $incr = self::BUFFER_SIZE >> 6;
1131
1132 while ($mlen > 0) {
1133 $blockSize = $mlen > self::BUFFER_SIZE
1134 ? self::BUFFER_SIZE
1135 : $mlen;
1136 $ciphertext = fread($ifp, $blockSize);
1137 if (!is_string($ciphertext)) {
1138 throw new SodiumException('Could not read input file');
1139 }
1140 $state->update($ciphertext);
1141 $mlen -= $blockSize;
1142 $iter += $incr;
1143 }
1144 $res = ParagonIE_Sodium_Core_Util::verify_16($tag, $state->finish());
1145
1146 fseek($ifp, $pos, SEEK_SET);
1147 return $res;
1148 }
1149
1150 /**
1151 * Update a hash context with the contents of a file, without
1152 * loading the entire file into memory.
1153 *
1154 * @param resource|HashContext $hash
1155 * @param resource $fp
1156 * @param int $size
1157 * @return resource|object Resource on PHP < 7.2, HashContext object on PHP >= 7.2
1158 * @throws SodiumException
1159 * @throws TypeError
1160 * @psalm-suppress PossiblyInvalidArgument
1161 * PHP 7.2 changes from a resource to an object,
1162 * which causes Psalm to complain about an error.
1163 * @psalm-suppress TypeCoercion
1164 * Ditto.
1165 */
1166 public static function updateHashWithFile($hash, $fp, $size = 0)
1167 {
1168 /* Type checks: */
1169 if (PHP_VERSION_ID < 70200) {
1170 if (!is_resource($hash)) {
1171 throw new TypeError('Argument 1 must be a resource, ' . gettype($hash) . ' given.');
1172 }
1173 } else {
1174 if (!is_object($hash)) {
1175 throw new TypeError('Argument 1 must be an object (PHP 7.2+), ' . gettype($hash) . ' given.');
1176 }
1177 }
1178
1179 if (!is_resource($fp)) {
1180 throw new TypeError('Argument 2 must be a resource, ' . gettype($fp) . ' given.');
1181 }
1182 if (!is_int($size)) {
1183 throw new TypeError('Argument 3 must be an integer, ' . gettype($size) . ' given.');
1184 }
1185
1186 /** @var int $originalPosition */
1187 $originalPosition = self::ftell($fp);
1188
1189 // Move file pointer to beginning of file
1190 fseek($fp, 0, SEEK_SET);
1191 for ($i = 0; $i < $size; $i += self::BUFFER_SIZE) {
1192 /** @var string|bool $message */
1193 $message = fread(
1194 $fp,
1195 ($size - $i) > self::BUFFER_SIZE
1196 ? $size - $i
1197 : self::BUFFER_SIZE
1198 );
1199 if (!is_string($message)) {
1200 throw new SodiumException('Unexpected error reading from file.');
1201 }
1202 /** @var string $message */
1203 /** @psalm-suppress InvalidArgument */
1204 self::hash_update($hash, $message);
1205 }
1206 // Reset file pointer's position
1207 fseek($fp, $originalPosition, SEEK_SET);
1208 return $hash;
1209 }
1210
1211 /**
1212 * Sign a file (rather than a string). Uses less memory than
1213 * ParagonIE_Sodium_Compat::crypto_sign_detached(), but produces
1214 * the same result. (32-bit)
1215 *
1216 * @param string $filePath Absolute path to a file on the filesystem
1217 * @param string $secretKey Secret signing key
1218 *
1219 * @return string Ed25519 signature
1220 * @throws SodiumException
1221 * @throws TypeError
1222 */
1223 private static function sign_core32($filePath, $secretKey)
1224 {
1225 $size = filesize($filePath);
1226 if (!is_int($size)) {
1227 throw new SodiumException('Could not obtain the file size');
1228 }
1229
1230 $fp = fopen($filePath, 'rb');
1231 if (!is_resource($fp)) {
1232 throw new SodiumException('Could not open input file for reading');
1233 }
1234
1235 /** @var string $az */
1236 $az = hash('sha512', self::substr($secretKey, 0, 32), true);
1237
1238 $az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
1239 $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
1240
1241 $hs = hash_init('sha512');
1242 self::hash_update($hs, self::substr($az, 32, 32));
1243 /** @var resource $hs */
1244 $hs = self::updateHashWithFile($hs, $fp, $size);
1245
1246 $nonceHash = hash_final($hs, true);
1247 $pk = self::substr($secretKey, 32, 32);
1248 $nonce = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
1249 $sig = ParagonIE_Sodium_Core32_Ed25519::ge_p3_tobytes(
1250 ParagonIE_Sodium_Core32_Ed25519::ge_scalarmult_base($nonce)
1251 );
1252
1253 $hs = hash_init('sha512');
1254 self::hash_update($hs, self::substr($sig, 0, 32));
1255 self::hash_update($hs, self::substr($pk, 0, 32));
1256 /** @var resource $hs */
1257 $hs = self::updateHashWithFile($hs, $fp, $size);
1258
1259 $hramHash = hash_final($hs, true);
1260
1261 $hram = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hramHash);
1262
1263 $sigAfter = ParagonIE_Sodium_Core32_Ed25519::sc_muladd($hram, $az, $nonce);
1264
1265 /** @var string $sig */
1266 $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
1267
1268 try {
1269 ParagonIE_Sodium_Compat::memzero($az);
1270 } catch (SodiumException $ex) {
1271 $az = null;
1272 }
1273 fclose($fp);
1274 return $sig;
1275 }
1276
1277 /**
1278 *
1279 * Verify a file (rather than a string). Uses less memory than
1280 * ParagonIE_Sodium_Compat::crypto_sign_verify_detached(), but
1281 * produces the same result. (32-bit)
1282 *
1283 * @param string $sig Ed25519 signature
1284 * @param string $filePath Absolute path to a file on the filesystem
1285 * @param string $publicKey Signing public key
1286 *
1287 * @return bool
1288 * @throws SodiumException
1289 * @throws Exception
1290 */
1291 public static function verify_core32($sig, $filePath, $publicKey)
1292 {
1293 /* Security checks */
1294 if (ParagonIE_Sodium_Core32_Ed25519::check_S_lt_L(self::substr($sig, 32, 32))) {
1295 throw new SodiumException('S < L - Invalid signature');
1296 }
1297 if (ParagonIE_Sodium_Core32_Ed25519::small_order($sig)) {
1298 throw new SodiumException('Signature is on too small of an order');
1299 }
1300
1301 if ((self::chrToInt($sig[63]) & 224) !== 0) {
1302 throw new SodiumException('Invalid signature');
1303 }
1304 $d = 0;
1305 for ($i = 0; $i < 32; ++$i) {
1306 $d |= self::chrToInt($publicKey[$i]);
1307 }
1308 if ($d === 0) {
1309 throw new SodiumException('All zero public key');
1310 }
1311
1312 /** @var int|bool $size */
1313 $size = filesize($filePath);
1314 if (!is_int($size)) {
1315 throw new SodiumException('Could not obtain the file size');
1316 }
1317 /** @var int $size */
1318
1319 /** @var resource|bool $fp */
1320 $fp = fopen($filePath, 'rb');
1321 if (!is_resource($fp)) {
1322 throw new SodiumException('Could not open input file for reading');
1323 }
1324 /** @var resource $fp */
1325
1326 /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
1327 $orig = ParagonIE_Sodium_Compat::$fastMult;
1328
1329 // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
1330 ParagonIE_Sodium_Compat::$fastMult = true;
1331
1332 /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */
1333 $A = ParagonIE_Sodium_Core32_Ed25519::ge_frombytes_negate_vartime($publicKey);
1334
1335 $hs = hash_init('sha512');
1336 self::hash_update($hs, self::substr($sig, 0, 32));
1337 self::hash_update($hs, self::substr($publicKey, 0, 32));
1338 /** @var resource $hs */
1339 $hs = self::updateHashWithFile($hs, $fp, $size);
1340 /** @var string $hDigest */
1341 $hDigest = hash_final($hs, true);
1342
1343 /** @var string $h */
1344 $h = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32);
1345
1346 /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */
1347 $R = ParagonIE_Sodium_Core32_Ed25519::ge_double_scalarmult_vartime(
1348 $h,
1349 $A,
1350 self::substr($sig, 32)
1351 );
1352
1353 /** @var string $rcheck */
1354 $rcheck = ParagonIE_Sodium_Core32_Ed25519::ge_tobytes($R);
1355
1356 // Close the file handle
1357 fclose($fp);
1358
1359 // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
1360 ParagonIE_Sodium_Compat::$fastMult = $orig;
1361 return self::verify_32($rcheck, self::substr($sig, 0, 32));
1362 }
1363
1364 /**
1365 * Encrypt a file (32-bit)
1366 *
1367 * @param resource $ifp
1368 * @param resource $ofp
1369 * @param int $mlen
1370 * @param string $nonce
1371 * @param string $key
1372 * @return bool
1373 * @throws SodiumException
1374 * @throws TypeError
1375 */
1376 protected static function secretbox_encrypt_core32($ifp, $ofp, $mlen, $nonce, $key)
1377 {
1378 $plaintext = fread($ifp, 32);
1379 if (!is_string($plaintext)) {
1380 throw new SodiumException('Could not read input file');
1381 }
1382 $first32 = self::ftell($ifp);
1383
1384 /** @var string $subkey */
1385 $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
1386
1387 /** @var string $realNonce */
1388 $realNonce = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
1389
1390 /** @var string $block0 */
1391 $block0 = str_repeat("\x00", 32);
1392
1393 /** @var int $mlen - Length of the plaintext message */
1394 $mlen0 = $mlen;
1395 if ($mlen0 > 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES) {
1396 $mlen0 = 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES;
1397 }
1398 $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
1399
1400 /** @var string $block0 */
1401 $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor(
1402 $block0,
1403 $realNonce,
1404 $subkey
1405 );
1406
1407 $state = new ParagonIE_Sodium_Core32_Poly1305_State(
1408 ParagonIE_Sodium_Core32_Util::substr(
1409 $block0,
1410 0,
1411 ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES
1412 )
1413 );
1414
1415 // Pre-write 16 blank bytes for the Poly1305 tag
1416 $start = self::ftell($ofp);
1417 fwrite($ofp, str_repeat("\x00", 16));
1418
1419 /** @var string $c */
1420 $cBlock = ParagonIE_Sodium_Core32_Util::substr(
1421 $block0,
1422 ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES
1423 );
1424 $state->update($cBlock);
1425 fwrite($ofp, $cBlock);
1426 $mlen -= 32;
1427
1428 /** @var int $iter */
1429 $iter = 1;
1430
1431 /** @var int $incr */
1432 $incr = self::BUFFER_SIZE >> 6;
1433
1434 /*
1435 * Set the cursor to the end of the first half-block. All future bytes will
1436 * generated from salsa20_xor_ic, starting from 1 (second block).
1437 */
1438 fseek($ifp, $first32, SEEK_SET);
1439
1440 while ($mlen > 0) {
1441 $blockSize = $mlen > self::BUFFER_SIZE
1442 ? self::BUFFER_SIZE
1443 : $mlen;
1444 $plaintext = fread($ifp, $blockSize);
1445 if (!is_string($plaintext)) {
1446 throw new SodiumException('Could not read input file');
1447 }
1448 $cBlock = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
1449 $plaintext,
1450 $realNonce,
1451 $iter,
1452 $subkey
1453 );
1454 fwrite($ofp, $cBlock, $blockSize);
1455 $state->update($cBlock);
1456
1457 $mlen -= $blockSize;
1458 $iter += $incr;
1459 }
1460 try {
1461 ParagonIE_Sodium_Compat::memzero($block0);
1462 ParagonIE_Sodium_Compat::memzero($subkey);
1463 } catch (SodiumException $ex) {
1464 $block0 = null;
1465 $subkey = null;
1466 }
1467 $end = self::ftell($ofp);
1468
1469 /*
1470 * Write the Poly1305 authentication tag that provides integrity
1471 * over the ciphertext (encrypt-then-MAC)
1472 */
1473 fseek($ofp, $start, SEEK_SET);
1474 fwrite($ofp, $state->finish(), ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_MACBYTES);
1475 fseek($ofp, $end, SEEK_SET);
1476 unset($state);
1477
1478 return true;
1479 }
1480
1481 /**
1482 * Decrypt a file (32-bit)
1483 *
1484 * @param resource $ifp
1485 * @param resource $ofp
1486 * @param int $mlen
1487 * @param string $nonce
1488 * @param string $key
1489 * @return bool
1490 * @throws SodiumException
1491 * @throws TypeError
1492 */
1493 protected static function secretbox_decrypt_core32($ifp, $ofp, $mlen, $nonce, $key)
1494 {
1495 $tag = fread($ifp, 16);
1496 if (!is_string($tag)) {
1497 throw new SodiumException('Could not read input file');
1498 }
1499
1500 /** @var string $subkey */
1501 $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
1502
1503 /** @var string $realNonce */
1504 $realNonce = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
1505
1506 /** @var string $block0 */
1507 $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20(
1508 64,
1509 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1510 $subkey
1511 );
1512
1513 /* Verify the Poly1305 MAC -before- attempting to decrypt! */
1514 $state = new ParagonIE_Sodium_Core32_Poly1305_State(self::substr($block0, 0, 32));
1515 if (!self::onetimeauth_verify_core32($state, $ifp, $tag, $mlen)) {
1516 throw new SodiumException('Invalid MAC');
1517 }
1518
1519 /*
1520 * Set the cursor to the end of the first half-block. All future bytes will
1521 * generated from salsa20_xor_ic, starting from 1 (second block).
1522 */
1523 $first32 = fread($ifp, 32);
1524 if (!is_string($first32)) {
1525 throw new SodiumException('Could not read input file');
1526 }
1527 $first32len = self::strlen($first32);
1528 fwrite(
1529 $ofp,
1530 self::xorStrings(
1531 self::substr($block0, 32, $first32len),
1532 self::substr($first32, 0, $first32len)
1533 )
1534 );
1535 $mlen -= 32;
1536
1537 /** @var int $iter */
1538 $iter = 1;
1539
1540 /** @var int $incr */
1541 $incr = self::BUFFER_SIZE >> 6;
1542
1543 /* Decrypts ciphertext, writes to output file. */
1544 while ($mlen > 0) {
1545 $blockSize = $mlen > self::BUFFER_SIZE
1546 ? self::BUFFER_SIZE
1547 : $mlen;
1548 $ciphertext = fread($ifp, $blockSize);
1549 if (!is_string($ciphertext)) {
1550 throw new SodiumException('Could not read input file');
1551 }
1552 $pBlock = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
1553 $ciphertext,
1554 $realNonce,
1555 $iter,
1556 $subkey
1557 );
1558 fwrite($ofp, $pBlock, $blockSize);
1559 $mlen -= $blockSize;
1560 $iter += $incr;
1561 }
1562 return true;
1563 }
1564
1565 /**
1566 * One-time message authentication for 32-bit systems
1567 *
1568 * @param ParagonIE_Sodium_Core32_Poly1305_State $state
1569 * @param resource $ifp
1570 * @param string $tag
1571 * @param int $mlen
1572 * @return bool
1573 * @throws SodiumException
1574 * @throws TypeError
1575 */
1576 protected static function onetimeauth_verify_core32(
1577 ParagonIE_Sodium_Core32_Poly1305_State $state,
1578 $ifp,
1579 $tag = '',
1580 $mlen = 0
1581 ) {
1582 /** @var int $pos */
1583 $pos = self::ftell($ifp);
1584
1585 while ($mlen > 0) {
1586 $blockSize = $mlen > self::BUFFER_SIZE
1587 ? self::BUFFER_SIZE
1588 : $mlen;
1589 $ciphertext = fread($ifp, $blockSize);
1590 if (!is_string($ciphertext)) {
1591 throw new SodiumException('Could not read input file');
1592 }
1593 $state->update($ciphertext);
1594 $mlen -= $blockSize;
1595 }
1596 $res = ParagonIE_Sodium_Core32_Util::verify_16($tag, $state->finish());
1597
1598 fseek($ifp, $pos, SEEK_SET);
1599 return $res;
1600 }
1601
1602 /**
1603 * @param resource $resource
1604 * @return int
1605 * @throws SodiumException
1606 */
1607 private static function ftell($resource)
1608 {
1609 $return = ftell($resource);
1610 if (!is_int($return)) {
1611 throw new SodiumException('ftell() returned false');
1612 }
1613 return (int) $return;
1614 }
1615}
1616
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1