PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / 3.1.22.4
JetBackup – Backup, Restore & Migrate v3.1.22.4
3.1.22.4 3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / 3rdparty / phpseclib3 / Crypt / Salsa20.php
backup / src / JetBackup / 3rdparty / phpseclib3 / Crypt Last commit date
Common 1 year ago DH 1 year ago DSA 1 year ago EC 1 year ago RSA 1 year ago .htaccess 1 year ago AES.php 1 year ago Blowfish.php 1 year ago ChaCha20.php 1 year ago DES.php 1 year ago DH.php 1 year ago DSA.php 1 year ago EC.php 1 year ago Hash.php 1 year ago PublicKeyLoader.php 1 year ago RC2.php 1 year ago RC4.php 1 year ago RSA.php 1 year ago Random.php 1 year ago Rijndael.php 1 year ago Salsa20.php 1 year ago TripleDES.php 1 year ago Twofish.php 1 year ago index.html 1 year ago web.config 1 year ago
Salsa20.php
504 lines
1 <?php
2
3 /**
4 * Pure-PHP implementation of Salsa20.
5 *
6 * PHP version 5
7 *
8 * @author Jim Wigginton <terrafrost@php.net>
9 * @copyright 2019 Jim Wigginton
10 * @license http://www.opensource.org/licenses/mit-license.html MIT License
11 * @link http://phpseclib.sourceforge.net
12 */
13
14 declare(strict_types=1);
15
16 namespace phpseclib3\Crypt;
17
18 use phpseclib3\Common\Functions\Strings;
19 use phpseclib3\Crypt\Common\StreamCipher;
20 use phpseclib3\Exception\BadDecryptionException;
21 use phpseclib3\Exception\InsufficientSetupException;
22 use phpseclib3\Exception\LengthException;
23
24 /**
25 * Pure-PHP implementation of Salsa20.
26 *
27 * @author Jim Wigginton <terrafrost@php.net>
28 */
29 class Salsa20 extends StreamCipher
30 {
31 /**
32 * Part 1 of the state
33 *
34 * @var string|false
35 */
36 protected $p1 = false;
37
38 /**
39 * Part 2 of the state
40 *
41 * @var string|false
42 */
43 protected $p2 = false;
44
45 /**
46 * Key Length (in bytes)
47 *
48 * @var int
49 */
50 protected $key_length = 32; // = 256 bits
51
52 /**
53 * @see \phpseclib3\Crypt\Salsa20::crypt()
54 */
55 public const ENCRYPT = 0;
56
57 /**
58 * @see \phpseclib3\Crypt\Salsa20::crypt()
59 */
60 public const DECRYPT = 1;
61
62 /**
63 * Encryption buffer for continuous mode
64 *
65 * @var array
66 */
67 protected $enbuffer;
68
69 /**
70 * Decryption buffer for continuous mode
71 *
72 * @var array
73 */
74 protected $debuffer;
75
76 /**
77 * Counter
78 *
79 * @var int
80 */
81 protected $counter = 0;
82
83 /**
84 * Using Generated Poly1305 Key
85 *
86 * @var boolean
87 */
88 protected $usingGeneratedPoly1305Key = false;
89
90 /**
91 * Salsa20 uses a nonce
92 */
93 public function usesNonce(): bool
94 {
95 return true;
96 }
97
98 /**
99 * Sets the key.
100 *
101 * @throws LengthException if the key length isn't supported
102 */
103 public function setKey(string $key): void
104 {
105 switch (strlen($key)) {
106 case 16:
107 case 32:
108 break;
109 default:
110 throw new LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 32 are supported');
111 }
112
113 parent::setKey($key);
114 }
115
116 /**
117 * Sets the nonce.
118 */
119 public function setNonce(string $nonce): void
120 {
121 if (strlen($nonce) != 8) {
122 throw new LengthException('Nonce of size ' . strlen($key) . ' not supported by this algorithm. Only an 64-bit nonce is supported');
123 }
124
125 $this->nonce = $nonce;
126 $this->changed = true;
127 $this->setEngine();
128 }
129
130 /**
131 * Sets the counter.
132 */
133 public function setCounter(int $counter): void
134 {
135 $this->counter = $counter;
136 $this->setEngine();
137 }
138
139 /**
140 * Creates a Poly1305 key using the method discussed in RFC8439
141 *
142 * See https://tools.ietf.org/html/rfc8439#section-2.6.1
143 */
144 protected function createPoly1305Key(): void
145 {
146 if ($this->nonce === false) {
147 throw new InsufficientSetupException('No nonce has been defined');
148 }
149
150 if ($this->key === false) {
151 throw new InsufficientSetupException('No key has been defined');
152 }
153
154 $c = clone $this;
155 $c->setCounter(0);
156 $c->usePoly1305 = false;
157 $block = $c->encrypt(str_repeat("\0", 256));
158 $this->setPoly1305Key(substr($block, 0, 32));
159
160 if ($this->counter == 0) {
161 $this->counter++;
162 }
163 }
164
165 /**
166 * Setup the self::ENGINE_INTERNAL $engine
167 *
168 * (re)init, if necessary, the internal cipher $engine
169 *
170 * _setup() will be called each time if $changed === true
171 * typically this happens when using one or more of following public methods:
172 *
173 * - setKey()
174 *
175 * - setNonce()
176 *
177 * - First run of encrypt() / decrypt() with no init-settings
178 *
179 * @see self::setKey()
180 * @see self::setNonce()
181 * @see self::disableContinuousBuffer()
182 */
183 protected function setup(): void
184 {
185 if (!$this->changed) {
186 return;
187 }
188
189 $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter];
190
191 $this->changed = $this->nonIVChanged = false;
192
193 if ($this->nonce === false) {
194 throw new InsufficientSetupException('No nonce has been defined');
195 }
196
197 if ($this->key === false) {
198 throw new InsufficientSetupException('No key has been defined');
199 }
200
201 if ($this->usePoly1305 && !isset($this->poly1305Key)) {
202 $this->usingGeneratedPoly1305Key = true;
203 $this->createPoly1305Key();
204 }
205
206 $key = $this->key;
207 if (strlen($key) == 16) {
208 $constant = 'expand 16-byte k';
209 $key .= $key;
210 } else {
211 $constant = 'expand 32-byte k';
212 }
213
214 $this->p1 = substr($constant, 0, 4) .
215 substr($key, 0, 16) .
216 substr($constant, 4, 4) .
217 $this->nonce .
218 "\0\0\0\0";
219 $this->p2 = substr($constant, 8, 4) .
220 substr($key, 16, 16) .
221 substr($constant, 12, 4);
222 }
223
224 /**
225 * Setup the key (expansion)
226 */
227 protected function setupKey(): void
228 {
229 // Salsa20 does not utilize this method
230 }
231
232 /**
233 * Encrypts a message.
234 *
235 * @return string $ciphertext
236 * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
237 * @see self::crypt()
238 */
239 public function encrypt(string $plaintext): string
240 {
241 $ciphertext = $this->crypt($plaintext, self::ENCRYPT);
242 if (isset($this->poly1305Key)) {
243 $this->newtag = $this->poly1305($ciphertext);
244 }
245 return $ciphertext;
246 }
247
248 /**
249 * Decrypts a message.
250 *
251 * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
252 * At least if the continuous buffer is disabled.
253 *
254 * @return string $plaintext
255 * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
256 * @see self::crypt()
257 */
258 public function decrypt(string $ciphertext): string
259 {
260 if (isset($this->poly1305Key)) {
261 if ($this->oldtag === false) {
262 throw new InsufficientSetupException('Authentication Tag has not been set');
263 }
264 $newtag = $this->poly1305($ciphertext);
265 if ($this->oldtag != substr($newtag, 0, strlen($this->oldtag))) {
266 $this->oldtag = false;
267 throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
268 }
269 $this->oldtag = false;
270 }
271
272 return $this->crypt($ciphertext, self::DECRYPT);
273 }
274
275 /**
276 * Encrypts a block
277 */
278 protected function encryptBlock(string $in): string
279 {
280 // Salsa20 does not utilize this method
281 return '';
282 }
283
284 /**
285 * Decrypts a block
286 */
287 protected function decryptBlock(string $in): string
288 {
289 // Salsa20 does not utilize this method
290 return '';
291 }
292
293 /**
294 * Encrypts or decrypts a message.
295 *
296 * @return string $text
297 * @see self::decrypt()
298 * @see self::encrypt()
299 */
300 private function crypt(string $text, int $mode): string
301 {
302 $this->setup();
303 if (!$this->continuousBuffer) {
304 if ($this->engine == self::ENGINE_OPENSSL) {
305 $iv = pack('V', $this->counter) . $this->p2;
306 return openssl_encrypt(
307 $text,
308 $this->cipher_name_openssl,
309 $this->key,
310 OPENSSL_RAW_DATA,
311 $iv
312 );
313 }
314 $i = $this->counter;
315 $blocks = str_split($text, 64);
316 foreach ($blocks as &$block) {
317 $block ^= static::salsa20($this->p1 . pack('V', $i++) . $this->p2);
318 }
319
320 return implode('', $blocks);
321 }
322
323 if ($mode == self::ENCRYPT) {
324 $buffer = &$this->enbuffer;
325 } else {
326 $buffer = &$this->debuffer;
327 }
328 if (!strlen($buffer['ciphertext'])) {
329 $ciphertext = '';
330 } else {
331 $ciphertext = $text ^ Strings::shift($buffer['ciphertext'], strlen($text));
332 $text = substr($text, strlen($ciphertext));
333 if (!strlen($text)) {
334 return $ciphertext;
335 }
336 }
337
338 $overflow = strlen($text) % 64; // & 0x3F
339 if ($overflow) {
340 $text2 = Strings::pop($text, $overflow);
341 if ($this->engine == self::ENGINE_OPENSSL) {
342 $iv = pack('V', $buffer['counter']) . $this->p2;
343 // at this point $text should be a multiple of 64
344 $buffer['counter'] += (strlen($text) >> 6) + 1; // ie. divide by 64
345 $encrypted = openssl_encrypt(
346 $text . str_repeat("\0", 64),
347 $this->cipher_name_openssl,
348 $this->key,
349 OPENSSL_RAW_DATA,
350 $iv
351 );
352 $temp = Strings::pop($encrypted, 64);
353 } else {
354 $blocks = str_split($text, 64);
355 if (strlen($text)) {
356 foreach ($blocks as &$block) {
357 $block ^= static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
358 }
359 }
360 $encrypted = implode('', $blocks);
361 $temp = static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
362 }
363 $ciphertext .= $encrypted . ($text2 ^ $temp);
364 $buffer['ciphertext'] = substr($temp, $overflow);
365 } elseif (!strlen($buffer['ciphertext'])) {
366 if ($this->engine == self::ENGINE_OPENSSL) {
367 $iv = pack('V', $buffer['counter']) . $this->p2;
368 $buffer['counter'] += (strlen($text) >> 6);
369 $ciphertext .= openssl_encrypt(
370 $text,
371 $this->cipher_name_openssl,
372 $this->key,
373 OPENSSL_RAW_DATA,
374 $iv
375 );
376 } else {
377 $blocks = str_split($text, 64);
378 foreach ($blocks as &$block) {
379 $block ^= static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
380 }
381 $ciphertext .= implode('', $blocks);
382 }
383 }
384
385 return $ciphertext;
386 }
387
388 /**
389 * Left Rotate
390 */
391 protected static function leftRotate(int $x, int $n): int
392 {
393 if (PHP_INT_SIZE == 8) {
394 $r1 = $x << $n;
395 $r1 &= 0xFFFFFFFF;
396 $r2 = ($x & 0xFFFFFFFF) >> (32 - $n);
397 } else {
398 $x = (int) $x;
399 $r1 = $x << $n;
400 $r2 = $x >> (32 - $n);
401 $r2 &= (1 << $n) - 1;
402 }
403 return $r1 | $r2;
404 }
405
406 /**
407 * The quarterround function
408 */
409 protected static function quarterRound(int &$a, int &$b, int &$c, int &$d): void
410 {
411 $b ^= self::leftRotate($a + $d, 7);
412 $c ^= self::leftRotate($b + $a, 9);
413 $d ^= self::leftRotate($c + $b, 13);
414 $a ^= self::leftRotate($d + $c, 18);
415 }
416
417 /**
418 * The doubleround function
419 *
420 * @param int $x0 (by reference)
421 * @param int $x1 (by reference)
422 * @param int $x2 (by reference)
423 * @param int $x3 (by reference)
424 * @param int $x4 (by reference)
425 * @param int $x5 (by reference)
426 * @param int $x6 (by reference)
427 * @param int $x7 (by reference)
428 * @param int $x8 (by reference)
429 * @param int $x9 (by reference)
430 * @param int $x10 (by reference)
431 * @param int $x11 (by reference)
432 * @param int $x12 (by reference)
433 * @param int $x13 (by reference)
434 * @param int $x14 (by reference)
435 * @param int $x15 (by reference)
436 */
437 protected static function doubleRound(int &$x0, int &$x1, int &$x2, int &$x3, int &$x4, int &$x5, int &$x6, int &$x7, int &$x8, int &$x9, int &$x10, int &$x11, int &$x12, int &$x13, int &$x14, int &$x15): void
438 {
439 // columnRound
440 static::quarterRound($x0, $x4, $x8, $x12);
441 static::quarterRound($x5, $x9, $x13, $x1);
442 static::quarterRound($x10, $x14, $x2, $x6);
443 static::quarterRound($x15, $x3, $x7, $x11);
444 // rowRound
445 static::quarterRound($x0, $x1, $x2, $x3);
446 static::quarterRound($x5, $x6, $x7, $x4);
447 static::quarterRound($x10, $x11, $x8, $x9);
448 static::quarterRound($x15, $x12, $x13, $x14);
449 }
450
451 /**
452 * The Salsa20 hash function function
453 */
454 protected static function salsa20(string $x)
455 {
456 $z = $x = unpack('V*', $x);
457 for ($i = 0; $i < 10; $i++) {
458 static::doubleRound($z[1], $z[2], $z[3], $z[4], $z[5], $z[6], $z[7], $z[8], $z[9], $z[10], $z[11], $z[12], $z[13], $z[14], $z[15], $z[16]);
459 }
460
461 for ($i = 1; $i <= 16; $i++) {
462 $x[$i] += $z[$i];
463 }
464
465 return pack('V*', ...$x);
466 }
467
468 /**
469 * Calculates Poly1305 MAC
470 *
471 * @see self::decrypt()
472 * @see self::encrypt()
473 */
474 protected function poly1305(string $text): string
475 {
476 if (!$this->usingGeneratedPoly1305Key) {
477 return parent::poly1305($this->aad . $text);
478 } else {
479 /*
480 sodium_crypto_aead_chacha20poly1305_encrypt does not calculate the poly1305 tag
481 the same way sodium_crypto_aead_chacha20poly1305_ietf_encrypt does. you can see
482 how the latter encrypts it in Salsa20::encrypt(). here's how the former encrypts
483 it:
484
485 $this->newtag = $this->poly1305(
486 $this->aad .
487 pack('V', strlen($this->aad)) . "\0\0\0\0" .
488 $ciphertext .
489 pack('V', strlen($ciphertext)) . "\0\0\0\0"
490 );
491
492 phpseclib opts to use the IETF construction, even when the nonce is 64-bits
493 instead of 96-bits
494 */
495 return parent::poly1305(
496 self::nullPad128($this->aad) .
497 self::nullPad128($text) .
498 pack('V', strlen($this->aad)) . "\0\0\0\0" .
499 pack('V', strlen($text)) . "\0\0\0\0"
500 );
501 }
502 }
503 }
504