Subversion Repositories oidplus

Rev

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

  1. <?php
  2.  
  3. /**
  4.  * Pure-PHP implementation of ChaCha20.
  5.  *
  6.  * PHP version 5
  7.  *
  8.  * @category  Crypt
  9.  * @package   ChaCha20
  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.  *
  24.  * @package ChaCha20
  25.  * @author  Jim Wigginton <terrafrost@php.net>
  26.  * @access  public
  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
  44.      * @access protected
  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. }
  805.