Subversion Repositories oidplus

Rev

Rev 846 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
827 daniel-mar 1
<?php
2
 
3
/**
4
 * Pure-PHP implementation of ChaCha20.
5
 *
6
 * PHP version 5
7
 *
874 daniel-mar 8
 * @category  Crypt
9
 * @package   ChaCha20
827 daniel-mar 10
 * @author    Jim Wigginton <terrafrost@php.net>
11
 * @copyright 2019 Jim Wigginton
12
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
13
 * @link      http://phpseclib.sourceforge.net
14
 */
15
 
16
namespace phpseclib3\Crypt;
17
 
18
use phpseclib3\Exception\BadDecryptionException;
19
use phpseclib3\Exception\InsufficientSetupException;
20
 
21
/**
22
 * Pure-PHP implementation of ChaCha20.
23
 *
874 daniel-mar 24
 * @package ChaCha20
827 daniel-mar 25
 * @author  Jim Wigginton <terrafrost@php.net>
874 daniel-mar 26
 * @access  public
827 daniel-mar 27
 */
28
class ChaCha20 extends Salsa20
29
{
30
    /**
31
     * The OpenSSL specific name of the cipher
32
     *
33
     * @var string
34
     */
35
    protected $cipher_name_openssl = 'chacha20';
36
 
37
    /**
38
     * Test for engine validity
39
     *
40
     * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
41
     *
42
     * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
43
     * @param int $engine
874 daniel-mar 44
     * @access protected
827 daniel-mar 45
     * @return bool
46
     */
47
    protected function isValidEngineHelper($engine)
48
    {
49
        switch ($engine) {
50
            case self::ENGINE_LIBSODIUM:
51
                // PHP 7.2.0 (30 Nov 2017) added support for libsodium
52
 
53
                // we could probably make it so that if $this->counter == 0 then the first block would be done with either OpenSSL
54
                // or PHP and then subsequent blocks would then be done with libsodium but idk - it's not a high priority atm
55
 
56
                // we could also make it so that if $this->counter == 0 and $this->continuousBuffer then do the first string
57
                // with libsodium and subsequent strings with openssl or pure-PHP but again not a high priority
58
                return function_exists('sodium_crypto_aead_chacha20poly1305_ietf_encrypt') &&
59
                       $this->key_length == 32 &&
60
                       (($this->usePoly1305 && !isset($this->poly1305Key) && $this->counter == 0) || $this->counter == 1) &&
61
                       !$this->continuousBuffer;
62
            case self::ENGINE_OPENSSL:
63
                // OpenSSL 1.1.0 (released 25 Aug 2016) added support for chacha20.
64
                // PHP didn't support OpenSSL 1.1.0 until 7.0.19 (11 May 2017)
65
 
66
                // if you attempt to provide openssl with a 128 bit key (as opposed to a 256 bit key) openssl will null
67
                // pad the key to 256 bits and still use the expansion constant for 256-bit keys. the fact that
68
                // openssl treats the IV as both the counter and nonce, however, let's us use openssl in continuous mode
69
                // whereas libsodium does not
70
                if ($this->key_length != 32) {
71
                    return false;
72
                }
73
        }
74
 
75
        return parent::isValidEngineHelper($engine);
76
    }
77
 
78
    /**
79
     * Encrypts a message.
80
     *
81
     * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
82
     * @see self::crypt()
83
     * @param string $plaintext
84
     * @return string $ciphertext
85
     */
86
    public function encrypt($plaintext)
87
    {
88
        $this->setup();
89
 
90
        if ($this->engine == self::ENGINE_LIBSODIUM) {
91
            return $this->encrypt_with_libsodium($plaintext);
92
        }
93
 
94
        return parent::encrypt($plaintext);
95
    }
96
 
97
    /**
98
     * Decrypts a message.
99
     *
100
     * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
101
     * At least if the continuous buffer is disabled.
102
     *
103
     * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
104
     * @see self::crypt()
105
     * @param string $ciphertext
106
     * @return string $plaintext
107
     */
108
    public function decrypt($ciphertext)
109
    {
110
        $this->setup();
111
 
112
        if ($this->engine == self::ENGINE_LIBSODIUM) {
113
            return $this->decrypt_with_libsodium($ciphertext);
114
        }
115
 
116
        return parent::decrypt($ciphertext);
117
    }
118
 
119
    /**
120
     * Encrypts a message with libsodium
121
     *
122
     * @see self::encrypt()
123
     * @param string $plaintext
124
     * @return string $text
125
     */
126
    private function encrypt_with_libsodium($plaintext)
127
    {
128
        $params = [$plaintext, $this->aad, $this->nonce, $this->key];
129
        $ciphertext = strlen($this->nonce) == 8 ?
130
            sodium_crypto_aead_chacha20poly1305_encrypt(...$params) :
131
            sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params);
132
        if (!$this->usePoly1305) {
133
            return substr($ciphertext, 0, strlen($plaintext));
134
        }
135
 
136
        $newciphertext = substr($ciphertext, 0, strlen($plaintext));
137
 
138
        $this->newtag = $this->usingGeneratedPoly1305Key && strlen($this->nonce) == 12 ?
139
            substr($ciphertext, strlen($plaintext)) :
140
            $this->poly1305($newciphertext);
141
 
142
        return $newciphertext;
143
    }
144
 
145
    /**
146
     * Decrypts a message with libsodium
147
     *
148
     * @see self::decrypt()
149
     * @param string $ciphertext
150
     * @return string $text
151
     */
152
    private function decrypt_with_libsodium($ciphertext)
153
    {
154
        $params = [$ciphertext, $this->aad, $this->nonce, $this->key];
155
 
156
        if (isset($this->poly1305Key)) {
157
            if ($this->oldtag === false) {
158
                throw new InsufficientSetupException('Authentication Tag has not been set');
159
            }
160
            if ($this->usingGeneratedPoly1305Key && strlen($this->nonce) == 12) {
161
                $plaintext = sodium_crypto_aead_chacha20poly1305_ietf_decrypt(...$params);
162
                $this->oldtag = false;
163
                if ($plaintext === false) {
164
                    throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
165
                }
166
                return $plaintext;
167
            }
168
            $newtag = $this->poly1305($ciphertext);
169
            if ($this->oldtag != substr($newtag, 0, strlen($this->oldtag))) {
170
                $this->oldtag = false;
171
                throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
172
            }
173
            $this->oldtag = false;
174
        }
175
 
176
        $plaintext = strlen($this->nonce) == 8 ?
177
            sodium_crypto_aead_chacha20poly1305_encrypt(...$params) :
178
            sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params);
179
 
180
        return substr($plaintext, 0, strlen($ciphertext));
181
    }
182
 
183
    /**
184
     * Sets the nonce.
185
     *
186
     * @param string $nonce
187
     */
188
    public function setNonce($nonce)
189
    {
190
        if (!is_string($nonce)) {
191
            throw new \UnexpectedValueException('The nonce should be a string');
192
        }
193
 
194
        /*
195
          from https://tools.ietf.org/html/rfc7539#page-7
196
 
197
          "Note also that the original ChaCha had a 64-bit nonce and 64-bit
198
           block count.  We have modified this here to be more consistent with
199
           recommendations in Section 3.2 of [RFC5116]."
200
         */
201
        switch (strlen($nonce)) {
202
            case 8:  // 64 bits
203
            case 12: // 96 bits
204
                break;
205
            default:
206
                throw new \LengthException('Nonce of size ' . strlen($nonce) . ' not supported by this algorithm. Only 64-bit nonces or 96-bit nonces are supported');
207
        }
208
 
209
        $this->nonce = $nonce;
210
        $this->changed = true;
211
        $this->setEngine();
212
    }
213
 
214
    /**
215
     * Setup the self::ENGINE_INTERNAL $engine
216
     *
217
     * (re)init, if necessary, the internal cipher $engine
218
     *
219
     * _setup() will be called each time if $changed === true
220
     * typically this happens when using one or more of following public methods:
221
     *
222
     * - setKey()
223
     *
224
     * - setNonce()
225
     *
226
     * - First run of encrypt() / decrypt() with no init-settings
227
     *
228
     * @see self::setKey()
229
     * @see self::setNonce()
230
     * @see self::disableContinuousBuffer()
231
     */
232
    protected function setup()
233
    {
234
        if (!$this->changed) {
235
            return;
236
        }
237
 
238
        $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter];
239
 
240
        $this->changed = $this->nonIVChanged = false;
241
 
242
        if ($this->nonce === false) {
243
            throw new InsufficientSetupException('No nonce has been defined');
244
        }
245
 
246
        if ($this->key === false) {
247
            throw new InsufficientSetupException('No key has been defined');
248
        }
249
 
250
        if ($this->usePoly1305 && !isset($this->poly1305Key)) {
251
            $this->usingGeneratedPoly1305Key = true;
252
            if ($this->engine == self::ENGINE_LIBSODIUM) {
253
                return;
254
            }
255
            $this->createPoly1305Key();
256
        }
257
 
258
        $key = $this->key;
259
        if (strlen($key) == 16) {
260
            $constant = 'expand 16-byte k';
261
            $key .= $key;
262
        } else {
263
            $constant = 'expand 32-byte k';
264
        }
265
 
266
        $this->p1 = $constant . $key;
267
        $this->p2 = $this->nonce;
268
        if (strlen($this->nonce) == 8) {
269
            $this->p2 = "\0\0\0\0" . $this->p2;
270
        }
271
    }
272
 
273
    /**
274
     * The quarterround function
275
     *
276
     * @param int $a
277
     * @param int $b
278
     * @param int $c
279
     * @param int $d
280
     */
281
    protected static function quarterRound(&$a, &$b, &$c, &$d)
282
    {
283
        // in https://datatracker.ietf.org/doc/html/rfc7539#section-2.1 the addition,
284
        // xor'ing and rotation are all on the same line so i'm keeping it on the same
285
        // line here as well
286
        // @codingStandardsIgnoreStart
287
        $a+= $b; $d = self::leftRotate($d ^ $a, 16);
288
        $c+= $d; $b = self::leftRotate($b ^ $c, 12);
289
        $a+= $b; $d = self::leftRotate($d ^ $a, 8);
290
        $c+= $d; $b = self::leftRotate($b ^ $c, 7);
291
        // @codingStandardsIgnoreEnd
292
    }
293
 
294
    /**
295
     * The doubleround function
296
     *
297
     * @param int $x0 (by reference)
298
     * @param int $x1 (by reference)
299
     * @param int $x2 (by reference)
300
     * @param int $x3 (by reference)
301
     * @param int $x4 (by reference)
302
     * @param int $x5 (by reference)
303
     * @param int $x6 (by reference)
304
     * @param int $x7 (by reference)
305
     * @param int $x8 (by reference)
306
     * @param int $x9 (by reference)
307
     * @param int $x10 (by reference)
308
     * @param int $x11 (by reference)
309
     * @param int $x12 (by reference)
310
     * @param int $x13 (by reference)
311
     * @param int $x14 (by reference)
312
     * @param int $x15 (by reference)
313
     */
314
    protected static function doubleRound(&$x0, &$x1, &$x2, &$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9, &$x10, &$x11, &$x12, &$x13, &$x14, &$x15)
315
    {
316
        // columnRound
317
        static::quarterRound($x0, $x4, $x8, $x12);
318
        static::quarterRound($x1, $x5, $x9, $x13);
319
        static::quarterRound($x2, $x6, $x10, $x14);
320
        static::quarterRound($x3, $x7, $x11, $x15);
321
        // rowRound
322
        static::quarterRound($x0, $x5, $x10, $x15);
323
        static::quarterRound($x1, $x6, $x11, $x12);
324
        static::quarterRound($x2, $x7, $x8, $x13);
325
        static::quarterRound($x3, $x4, $x9, $x14);
326
    }
327
 
328
    /**
329
     * The Salsa20 hash function function
330
     *
331
     * On my laptop this loop unrolled / function dereferenced version of parent::salsa20 encrypts 1mb of text in
332
     * 0.65s vs the 0.85s that it takes with the parent method.
333
     *
334
     * If we were free to assume that the host OS would always be 64-bits then the if condition in leftRotate could
335
     * be eliminated and we could knock this done to 0.60s.
336
     *
337
     * For comparison purposes, RC4 takes 0.16s and AES in CTR mode with the Eval engine takes 0.48s.
338
     * AES in CTR mode with the PHP engine takes 1.19s. Salsa20 / ChaCha20 do not benefit as much from the Eval
339
     * approach due to the fact that there are a lot less variables to de-reference, fewer loops to unroll, etc
340
     *
341
     * @param string $x
342
     */
343
    protected static function salsa20($x)
344
    {
345
        list(, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15) = unpack('V*', $x);
346
        $z0 = $x0;
347
        $z1 = $x1;
348
        $z2 = $x2;
349
        $z3 = $x3;
350
        $z4 = $x4;
351
        $z5 = $x5;
352
        $z6 = $x6;
353
        $z7 = $x7;
354
        $z8 = $x8;
355
        $z9 = $x9;
356
        $z10 = $x10;
357
        $z11 = $x11;
358
        $z12 = $x12;
359
        $z13 = $x13;
360
        $z14 = $x14;
361
        $z15 = $x15;
362
 
363
        // @codingStandardsIgnoreStart
364
        // columnRound
365
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 16);
366
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 12);
367
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 8);
368
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 7);
369
 
370
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 16);
371
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 12);
372
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 8);
373
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 7);
374
 
375
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 16);
376
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 12);
377
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 8);
378
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 7);
379
 
380
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 16);
381
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 12);
382
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 8);
383
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 7);
384
 
385
        // rowRound
386
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 16);
387
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 12);
388
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 8);
389
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 7);
390
 
391
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 16);
392
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 12);
393
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 8);
394
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 7);
395
 
396
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 16);
397
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 12);
398
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 8);
399
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 7);
400
 
401
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 16);
402
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 12);
403
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 8);
404
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 7);
405
 
406
        // columnRound
407
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 16);
408
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 12);
409
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 8);
410
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 7);
411
 
412
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 16);
413
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 12);
414
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 8);
415
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 7);
416
 
417
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 16);
418
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 12);
419
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 8);
420
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 7);
421
 
422
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 16);
423
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 12);
424
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 8);
425
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 7);
426
 
427
        // rowRound
428
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 16);
429
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 12);
430
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 8);
431
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 7);
432
 
433
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 16);
434
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 12);
435
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 8);
436
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 7);
437
 
438
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 16);
439
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 12);
440
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 8);
441
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 7);
442
 
443
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 16);
444
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 12);
445
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 8);
446
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 7);
447
 
448
        // columnRound
449
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 16);
450
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 12);
451
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 8);
452
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 7);
453
 
454
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 16);
455
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 12);
456
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 8);
457
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 7);
458
 
459
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 16);
460
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 12);
461
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 8);
462
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 7);
463
 
464
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 16);
465
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 12);
466
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 8);
467
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 7);
468
 
469
        // rowRound
470
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 16);
471
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 12);
472
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 8);
473
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 7);
474
 
475
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 16);
476
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 12);
477
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 8);
478
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 7);
479
 
480
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 16);
481
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 12);
482
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 8);
483
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 7);
484
 
485
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 16);
486
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 12);
487
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 8);
488
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 7);
489
 
490
        // columnRound
491
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 16);
492
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 12);
493
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 8);
494
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 7);
495
 
496
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 16);
497
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 12);
498
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 8);
499
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 7);
500
 
501
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 16);
502
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 12);
503
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 8);
504
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 7);
505
 
506
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 16);
507
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 12);
508
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 8);
509
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 7);
510
 
511
        // rowRound
512
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 16);
513
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 12);
514
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 8);
515
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 7);
516
 
517
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 16);
518
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 12);
519
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 8);
520
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 7);
521
 
522
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 16);
523
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 12);
524
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 8);
525
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 7);
526
 
527
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 16);
528
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 12);
529
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 8);
530
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 7);
531
 
532
        // columnRound
533
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 16);
534
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 12);
535
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 8);
536
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 7);
537
 
538
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 16);
539
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 12);
540
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 8);
541
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 7);
542
 
543
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 16);
544
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 12);
545
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 8);
546
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 7);
547
 
548
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 16);
549
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 12);
550
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 8);
551
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 7);
552
 
553
        // rowRound
554
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 16);
555
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 12);
556
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 8);
557
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 7);
558
 
559
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 16);
560
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 12);
561
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 8);
562
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 7);
563
 
564
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 16);
565
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 12);
566
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 8);
567
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 7);
568
 
569
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 16);
570
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 12);
571
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 8);
572
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 7);
573
 
574
        // columnRound
575
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 16);
576
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 12);
577
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 8);
578
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 7);
579
 
580
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 16);
581
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 12);
582
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 8);
583
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 7);
584
 
585
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 16);
586
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 12);
587
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 8);
588
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 7);
589
 
590
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 16);
591
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 12);
592
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 8);
593
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 7);
594
 
595
        // rowRound
596
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 16);
597
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 12);
598
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 8);
599
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 7);
600
 
601
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 16);
602
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 12);
603
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 8);
604
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 7);
605
 
606
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 16);
607
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 12);
608
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 8);
609
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 7);
610
 
611
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 16);
612
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 12);
613
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 8);
614
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 7);
615
 
616
        // columnRound
617
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 16);
618
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 12);
619
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 8);
620
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 7);
621
 
622
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 16);
623
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 12);
624
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 8);
625
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 7);
626
 
627
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 16);
628
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 12);
629
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 8);
630
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 7);
631
 
632
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 16);
633
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 12);
634
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 8);
635
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 7);
636
 
637
        // rowRound
638
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 16);
639
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 12);
640
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 8);
641
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 7);
642
 
643
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 16);
644
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 12);
645
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 8);
646
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 7);
647
 
648
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 16);
649
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 12);
650
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 8);
651
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 7);
652
 
653
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 16);
654
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 12);
655
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 8);
656
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 7);
657
 
658
        // columnRound
659
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 16);
660
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 12);
661
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 8);
662
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 7);
663
 
664
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 16);
665
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 12);
666
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 8);
667
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 7);
668
 
669
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 16);
670
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 12);
671
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 8);
672
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 7);
673
 
674
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 16);
675
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 12);
676
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 8);
677
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 7);
678
 
679
        // rowRound
680
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 16);
681
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 12);
682
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 8);
683
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 7);
684
 
685
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 16);
686
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 12);
687
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 8);
688
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 7);
689
 
690
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 16);
691
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 12);
692
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 8);
693
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 7);
694
 
695
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 16);
696
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 12);
697
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 8);
698
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 7);
699
 
700
        // columnRound
701
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 16);
702
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 12);
703
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 8);
704
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 7);
705
 
706
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 16);
707
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 12);
708
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 8);
709
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 7);
710
 
711
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 16);
712
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 12);
713
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 8);
714
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 7);
715
 
716
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 16);
717
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 12);
718
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 8);
719
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 7);
720
 
721
        // rowRound
722
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 16);
723
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 12);
724
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 8);
725
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 7);
726
 
727
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 16);
728
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 12);
729
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 8);
730
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 7);
731
 
732
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 16);
733
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 12);
734
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 8);
735
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 7);
736
 
737
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 16);
738
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 12);
739
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 8);
740
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 7);
741
 
742
        // columnRound
743
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 16);
744
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 12);
745
        $x0+= $x4; $x12 = self::leftRotate($x12 ^ $x0, 8);
746
        $x8+= $x12; $x4 = self::leftRotate($x4 ^ $x8, 7);
747
 
748
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 16);
749
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 12);
750
        $x1+= $x5; $x13 = self::leftRotate($x13 ^ $x1, 8);
751
        $x9+= $x13; $x5 = self::leftRotate($x5 ^ $x9, 7);
752
 
753
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 16);
754
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 12);
755
        $x2+= $x6; $x14 = self::leftRotate($x14 ^ $x2, 8);
756
        $x10+= $x14; $x6 = self::leftRotate($x6 ^ $x10, 7);
757
 
758
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 16);
759
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 12);
760
        $x3+= $x7; $x15 = self::leftRotate($x15 ^ $x3, 8);
761
        $x11+= $x15; $x7 = self::leftRotate($x7 ^ $x11, 7);
762
 
763
        // rowRound
764
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 16);
765
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 12);
766
        $x0+= $x5; $x15 = self::leftRotate($x15 ^ $x0, 8);
767
        $x10+= $x15; $x5 = self::leftRotate($x5 ^ $x10, 7);
768
 
769
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 16);
770
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 12);
771
        $x1+= $x6; $x12 = self::leftRotate($x12 ^ $x1, 8);
772
        $x11+= $x12; $x6 = self::leftRotate($x6 ^ $x11, 7);
773
 
774
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 16);
775
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 12);
776
        $x2+= $x7; $x13 = self::leftRotate($x13 ^ $x2, 8);
777
        $x8+= $x13; $x7 = self::leftRotate($x7 ^ $x8, 7);
778
 
779
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 16);
780
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 12);
781
        $x3+= $x4; $x14 = self::leftRotate($x14 ^ $x3, 8);
782
        $x9+= $x14; $x4 = self::leftRotate($x4 ^ $x9, 7);
783
        // @codingStandardsIgnoreEnd
784
 
785
        $x0 += $z0;
786
        $x1 += $z1;
787
        $x2 += $z2;
788
        $x3 += $z3;
789
        $x4 += $z4;
790
        $x5 += $z5;
791
        $x6 += $z6;
792
        $x7 += $z7;
793
        $x8 += $z8;
794
        $x9 += $z9;
795
        $x10 += $z10;
796
        $x11 += $z11;
797
        $x12 += $z12;
798
        $x13 += $z13;
799
        $x14 += $z14;
800
        $x15 += $z15;
801
 
802
        return pack('V*', $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15);
803
    }
804
}