<?php

if (class_exists('ParagonIE_Sodium_Core_AEGIS_State256', false)) {
    return;
}

if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
    define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
}
if (!defined('SODIUM_COMPAT_AEGIS_C1')) {
    define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd");
}

class ParagonIE_Sodium_Core_AEGIS_State256
{
     
    protected $state;
    public function __construct()
    {
        $this->state = array_fill(0, 6, '');
    }

    



    public function getState()
    {
        return array_values($this->state);
    }

    






    public static function initForUnitTests(array $input)
    {
        if (count($input) < 6) {
            throw new SodiumException('invalid input');
        }
        $state = new self();
        for ($i = 0; $i < 6; ++$i) {
            $state->state[$i] = $input[$i];
        }
        return $state;
    }

    




    public static function init($key, $nonce)
    {
        $state = new self();
        $k0 = ParagonIE_Sodium_Core_Util::substr($key, 0, 16);
        $k1 = ParagonIE_Sodium_Core_Util::substr($key, 16, 16);
        $n0 = ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16);
        $n1 = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 16);

         
         
         
         
         
         
        $k0_n0 = $k0 ^ $n0;
        $k1_n1 = $k1 ^ $n1;
        $state->state[0] = $k0_n0;
        $state->state[1] = $k1_n1;
        $state->state[2] = SODIUM_COMPAT_AEGIS_C1;
        $state->state[3] = SODIUM_COMPAT_AEGIS_C0;
        $state->state[4] = $k0 ^ SODIUM_COMPAT_AEGIS_C0;
        $state->state[5] = $k1 ^ SODIUM_COMPAT_AEGIS_C1;

         
         
         
         
         
         
        for ($i = 0; $i < 4; ++$i) {
            $state->update($k0);
            $state->update($k1);
            $state->update($k0 ^ $n0);
            $state->update($k1 ^ $n1);
        }
        return $state;
    }

    




    public function absorb($ai)
    {
        if (ParagonIE_Sodium_Core_Util::strlen($ai) !== 16) {
            throw new SodiumException('Input must be an AES block in size');
        }
        return $this->update($ai);
    }

    




    public function dec($ci)
    {
        if (ParagonIE_Sodium_Core_Util::strlen($ci) !== 16) {
            throw new SodiumException('Input must be an AES block in size');
        }
         
        $z = $this->state[1]
            ^ $this->state[4]
            ^ $this->state[5]
            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
        $xi = $ci ^ $z;
        $this->update($xi);
        return $xi;
    }

    



    public function decPartial($cn)
    {
        $len = ParagonIE_Sodium_Core_Util::strlen($cn);
         
        $z = $this->state[1]
            ^ $this->state[4]
            ^ $this->state[5]
            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);

         
        $t = str_pad($cn, 16, "\0", STR_PAD_RIGHT);

         
        $out = $t ^ $z;

         
        $xn = ParagonIE_Sodium_Core_Util::substr($out, 0, $len);

         
        $v = str_pad($xn, 16, "\0", STR_PAD_RIGHT);
         
        $this->update($v);

         
        return $xn;
    }

    




    public function enc($xi)
    {
        if (ParagonIE_Sodium_Core_Util::strlen($xi) !== 16) {
            throw new SodiumException('Input must be an AES block in size');
        }
         
        $z = $this->state[1]
            ^ $this->state[4]
            ^ $this->state[5]
            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
        $this->update($xi);
        return $xi ^ $z;
    }

    




    public function finalize($ad_len_bits, $msg_len_bits)
    {
        $encoded = ParagonIE_Sodium_Core_Util::store64_le($ad_len_bits) .
            ParagonIE_Sodium_Core_Util::store64_le($msg_len_bits);
        $t = $this->state[3] ^ $encoded;

        for ($i = 0; $i < 7; ++$i) {
            $this->update($t);
        }

        return ($this->state[0] ^ $this->state[1] ^ $this->state[2]) .
            ($this->state[3] ^ $this->state[4] ^ $this->state[5]);
    }

    



    public function update($m)
    {
        







        list($s_0, $s_1) = ParagonIE_Sodium_Core_AES::doubleRound(
            $this->state[5],$this->state[0] ^ $m,
            $this->state[0], $this->state[1]
        );

        list($s_2, $s_3) = ParagonIE_Sodium_Core_AES::doubleRound(
            $this->state[1], $this->state[2],
            $this->state[2], $this->state[3]
        );
        list($s_4, $s_5) = ParagonIE_Sodium_Core_AES::doubleRound(
            $this->state[3], $this->state[4],
            $this->state[4], $this->state[5]
        );

        







        $this->state[0] = $s_0;
        $this->state[1] = $s_1;
        $this->state[2] = $s_2;
        $this->state[3] = $s_3;
        $this->state[4] = $s_4;
        $this->state[5] = $s_5;
        return $this;
    }
}
