<?php 
declare(strict_types=1); 
 
if (class_exists('ParagonIE_Sodium_Core_AEGIS_State128L', 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_State128L 
{ 
    /** @var array<int, string> $state */ 
    protected array $state; 
 
    public function __construct() 
    { 
        $this->state = array_fill(0, 8, ''); 
    } 
 
    /** 
     * @internal Only use this for unit tests! 
     * @return string[] 
     */ 
    public function getState(): array 
    { 
        return array_values($this->state); 
    } 
 
    /** 
     * @param array $input 
     * @return self 
     * @throws SodiumException 
     * 
     * @internal Only for unit tests 
     */ 
    public static function initForUnitTests(array $input): self 
    { 
        if (count($input) < 8) { 
            throw new SodiumException('invalid input'); 
        } 
        $state = new self(); 
        for ($i = 0; $i < 8; ++$i) { 
            $state->state[$i] = $input[$i]; 
        } 
        return $state; 
    } 
 
    /** 
     * @param string $key 
     * @param string $nonce 
     * @return self 
     */ 
    public static function init( 
        #[SensitiveParameter] 
        string $key, 
        string $nonce 
    ): self { 
        $state = new self(); 
 
        // S0 = key ^ nonce 
        $state->state[0] = $key ^ $nonce; 
        // S1 = C1 
        $state->state[1] = SODIUM_COMPAT_AEGIS_C1; 
        // S2 = C0 
        $state->state[2] = SODIUM_COMPAT_AEGIS_C0; 
        // S3 = C1 
        $state->state[3] = SODIUM_COMPAT_AEGIS_C1; 
        // S4 = key ^ nonce 
        $state->state[4] = $key ^ $nonce; 
        // S5 = key ^ C0 
        $state->state[5] = $key ^ SODIUM_COMPAT_AEGIS_C0; 
        // S6 = key ^ C1 
        $state->state[6] = $key ^ SODIUM_COMPAT_AEGIS_C1; 
        // S7 = key ^ C0 
        $state->state[7] = $key ^ SODIUM_COMPAT_AEGIS_C0; 
 
        // Repeat(10, Update(nonce, key)) 
        for ($i = 0; $i < 10; ++$i) { 
            $state->update($nonce, $key); 
        } 
        return $state; 
    } 
 
    /** 
     * @param string $ai 
     * @return static 
     * 
     * @throws SodiumException 
     */ 
    public function absorb(string $ai): static 
    { 
        if (ParagonIE_Sodium_Core_Util::strlen($ai) !== 32) { 
            throw new SodiumException('Input must be two AES blocks in size'); 
        } 
        $t0 = ParagonIE_Sodium_Core_Util::substr($ai, 0, 16); 
        $t1 = ParagonIE_Sodium_Core_Util::substr($ai, 16, 16); 
        return $this->update($t0, $t1); 
    } 
 
 
    /** 
     * @param string $ci 
     * @return string 
     * @throws SodiumException 
     */ 
    public function dec(string $ci): string 
    { 
        if (ParagonIE_Sodium_Core_Util::strlen($ci) !== 32) { 
            throw new SodiumException('Input must be two AES blocks in size'); 
        } 
 
        // z0 = S6 ^ S1 ^ (S2 & S3) 
        $z0 = $this->state[6] 
            ^ $this->state[1] 
            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]); 
        // z1 = S2 ^ S5 ^ (S6 & S7) 
        $z1 = $this->state[2] 
            ^ $this->state[5] 
            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[6], $this->state[7]); 
 
        // t0, t1 = Split(xi, 128) 
        $t0 = ParagonIE_Sodium_Core_Util::substr($ci, 0, 16); 
        $t1 = ParagonIE_Sodium_Core_Util::substr($ci, 16, 16); 
 
        // out0 = t0 ^ z0 
        // out1 = t1 ^ z1 
        $out0 = $t0 ^ $z0; 
        $out1 = $t1 ^ $z1; 
 
        // Update(out0, out1) 
        // xi = out0 || out1 
        $this->update($out0, $out1); 
        return $out0 . $out1; 
    } 
 
    /** 
     * @param string $cn 
     * @return string 
     * 
     * @throws SodiumException 
     */ 
    public function decPartial(string $cn): string 
    { 
        $len = ParagonIE_Sodium_Core_Util::strlen($cn); 
 
        // z0 = S6 ^ S1 ^ (S2 & S3) 
        $z0 = $this->state[6] 
            ^ $this->state[1] 
            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]); 
        // z1 = S2 ^ S5 ^ (S6 & S7) 
        $z1 = $this->state[2] 
            ^ $this->state[5] 
            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[6], $this->state[7]); 
 
        // t0, t1 = Split(ZeroPad(cn, 256), 128) 
        $cn = str_pad($cn, 32, "\0"); 
        $t0 = ParagonIE_Sodium_Core_Util::substr($cn, 0, 16); 
        $t1 = ParagonIE_Sodium_Core_Util::substr($cn, 16, 16); 
        // out0 = t0 ^ z0 
        // out1 = t1 ^ z1 
        $out0 = $t0 ^ $z0; 
        $out1 = $t1 ^ $z1; 
 
        // xn = Truncate(out0 || out1, |cn|) 
        $xn = ParagonIE_Sodium_Core_Util::substr($out0 . $out1, 0, $len); 
 
        // v0, v1 = Split(ZeroPad(xn, 256), 128) 
        $padded = str_pad($xn, 32, "\0"); 
        $v0 = ParagonIE_Sodium_Core_Util::substr($padded, 0, 16); 
        $v1 = ParagonIE_Sodium_Core_Util::substr($padded, 16, 16); 
        // Update(v0, v1) 
        $this->update($v0, $v1); 
 
        // return xn 
        return $xn; 
    } 
 
    /** 
     * @param string $xi 
     * @return string 
     * @throws SodiumException 
     */ 
    public function enc( 
        #[SensitiveParameter] 
        string $xi 
    ): string { 
        if (ParagonIE_Sodium_Core_Util::strlen($xi) !== 32) { 
            throw new SodiumException('Input must be two AES blocks in size'); 
        } 
 
        // z0 = S6 ^ S1 ^ (S2 & S3) 
        $z0 = $this->state[6] 
            ^ $this->state[1] 
            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]); 
        // z1 = S2 ^ S5 ^ (S6 & S7) 
        $z1 = $this->state[2] 
            ^ $this->state[5] 
            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[6], $this->state[7]); 
 
        // t0, t1 = Split(xi, 128) 
        $t0 = ParagonIE_Sodium_Core_Util::substr($xi, 0, 16); 
        $t1 = ParagonIE_Sodium_Core_Util::substr($xi, 16, 16); 
 
        // out0 = t0 ^ z0 
        // out1 = t1 ^ z1 
        $out0 = $t0 ^ $z0; 
        $out1 = $t1 ^ $z1; 
 
        // Update(t0, t1) 
        // ci = out0 || out1 
        $this->update($t0, $t1); 
 
        // return ci 
        return $out0 . $out1; 
    } 
 
    /** 
     * @param int $ad_len_bits 
     * @param int $msg_len_bits 
     * @return string 
     */ 
    public function finalize(int $ad_len_bits, int $msg_len_bits): string 
    { 
        $encoded = ParagonIE_Sodium_Core_Util::store64_le($ad_len_bits) . 
            ParagonIE_Sodium_Core_Util::store64_le($msg_len_bits); 
        $t = $this->state[2] ^ $encoded; 
        for ($i = 0; $i < 7; ++$i) { 
            $this->update($t, $t); 
        } 
        return ($this->state[0] ^ $this->state[1] ^ $this->state[2] ^ $this->state[3]) . 
            ($this->state[4] ^ $this->state[5] ^ $this->state[6] ^ $this->state[7]); 
    } 
 
    /** 
     * @param string $m0 
     * @param string $m1 
     * @return static 
     */ 
    public function update(string $m0, string $m1): static 
    { 
        /* 
           S'0 = AESRound(S7, S0 ^ M0) 
           S'1 = AESRound(S0, S1) 
           S'2 = AESRound(S1, S2) 
           S'3 = AESRound(S2, S3) 
           S'4 = AESRound(S3, S4 ^ M1) 
           S'5 = AESRound(S4, S5) 
           S'6 = AESRound(S5, S6) 
           S'7 = AESRound(S6, S7) 
         */ 
        list($s_0, $s_1) = ParagonIE_Sodium_Core_AES::doubleRound( 
            $this->state[7], $this->state[0] ^ $m0, 
            $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] ^ $m1, 
            $this->state[4], $this->state[5] 
        ); 
        list($s_6, $s_7) = ParagonIE_Sodium_Core_AES::doubleRound( 
            $this->state[5], $this->state[6], 
            $this->state[6], $this->state[7] 
        ); 
 
        /* 
           S0  = S'0 
           S1  = S'1 
           S2  = S'2 
           S3  = S'3 
           S4  = S'4 
           S5  = S'5 
           S6  = S'6 
           S7  = S'7 
         */ 
        $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; 
        $this->state[6] = $s_6; 
        $this->state[7] = $s_7; 
        return $this; 
    } 
}
 
 |