final private static function streamDecrypt(
        ReadOnlyFile $input,
        MutableFile $output,
        EncryptionKey $encKey,
        string $nonce,
        string $mac,
        Config $config,
        array &$chunk_macs
    ): bool {
        $start = $input->getPos();
        /** @var int $cipher_end */
        $cipher_end = $input->getSize() - (int) $config->MAC_SIZE;
        // Begin the streaming decryption
        $input->reset($start);

        while ($input->remainingBytes() > (int) $config->MAC_SIZE) {
            /**
             * Would a full BUFFER read put it past the end of the
             * ciphertext? If so, only return a portion of the file.
             */
            if (($input->getPos() + (int) $config->BUFFER) > $cipher_end) {
                $read = $input->readBytes(
                    $cipher_end - $input->getPos()
                );
            } else {
                // @codeCoverageIgnoreStart
                $read = $input->readBytes((int) $config->BUFFER);
                // @codeCoverageIgnoreEnd
            }

            // Version 2+ uses a keyed BLAKE2b hash instead of HMAC
            \sodium_crypto_generichash_update($mac, $read);
            /** @var string $mac */
            $calcMAC = Util::safeStrcpy($mac);
            $calc = \sodium_crypto_generichash_final($calcMAC, (int) $config->MAC_SIZE);

            if (empty($chunk_macs)) {
                // @codeCoverageIgnoreStart
                // Someone attempted to add a chunk at the end.
                throw new InvalidMessage(
                    'Invalid message authentication code'
                );
                // @codeCoverageIgnoreEnd
            } else {
                /** @var string $chunkMAC */
                $chunkMAC = \array_shift($chunk_macs);
                if (!\hash_equals($chunkMAC, $calc)) {
                    // This chunk was altered after the original MAC was verified
                    // @codeCoverageIgnoreStart
                    throw new InvalidMessage(
                        'Invalid message authentication code'
                    );
                    // @codeCoverageIgnoreEnd
                }
            }

            // This is where the decryption actually occurs:
            $decrypted = \sodium_crypto_stream_xor(
                $read,
                (string) $nonce,
                $encKey->getRawKeyMaterial()
            );
            $output->writeBytes($decrypted);
            \sodium_increment($nonce);
        }
        if (\is_string($nonce)) {
            \sodium_memzero($nonce);
        }
        return true;
    }
final private static function streamDecrypt(
        ReadOnlyFile $input,
        MutableFile $output,
        EncryptionKey $encKey,
        string $nonce,
        string $mac,
        Config $config,
        array &$chunk_macs
    ): bool {
        $start = $input->getPos();
        /** @var int $cipher_end */
        $cipher_end = $input->getSize() - (int) $config->MAC_SIZE;
        // Begin the streaming decryption
        $input->reset($start);

        while ($input->remainingBytes() > (int) $config->MAC_SIZE) {
            /**
             * Would a full BUFFER read put it past the end of the
             * ciphertext? If so, only return a portion of the file.
             */
            if (($input->getPos() + (int) $config->BUFFER) > $cipher_end) {
                $read = $input->readBytes(
                    $cipher_end - $input->getPos()
                );
            } else {
                // @codeCoverageIgnoreStart
                $read = $input->readBytes((int) $config->BUFFER);
                // @codeCoverageIgnoreEnd
            }

            // Version 2+ uses a keyed BLAKE2b hash instead of HMAC
            \sodium_crypto_generichash_update($mac, $read);
            /** @var string $mac */
            $calcMAC = Util::safeStrcpy($mac);
            $calc = \sodium_crypto_generichash_final($calcMAC, (int) $config->MAC_SIZE);

            if (empty($chunk_macs)) {
                // @codeCoverageIgnoreStart
                // Someone attempted to add a chunk at the end.
                throw new InvalidMessage(
                    'Invalid message authentication code'
                );
                // @codeCoverageIgnoreEnd
            } else {
                /** @var string $chunkMAC */
                $chunkMAC = \array_shift($chunk_macs);
                if (!\hash_equals($chunkMAC, $calc)) {
                    // This chunk was altered after the original MAC was verified
                    // @codeCoverageIgnoreStart
                    throw new InvalidMessage(
                        'Invalid message authentication code'
                    );
                    // @codeCoverageIgnoreEnd
                }
            }

            // This is where the decryption actually occurs:
            $decrypted = \sodium_crypto_stream_xor(
                $read,
                (string) $nonce,
                $encKey->getRawKeyMaterial()
            );
            $output->writeBytes($decrypted);
            \sodium_increment($nonce);
        }
        if (\is_string($nonce)) {
            \sodium_memzero($nonce);
        }
        return true;
    }
final private static function streamDecrypt ( ReadOnlyFile $ input , MutableFile $ output , EncryptionKey $ encKey , string $ nonce , string $ mac , Config $ config , array & $ chunk_macs ) : bool { $ start = $ input -> getPos ( ) ; /** @var int $cipher_end */ $ cipher_end = $ input -> getSize ( ) - ( int ) $ config -> MAC_SIZE ; // Begin the streaming decryption $ input -> reset ( $ start ) ; while ( $ input -> remainingBytes ( ) > ( int ) $ config -> MAC_SIZE ) { /**
             * Would a full BUFFER read put it past the end of the
             * ciphertext? If so, only return a portion of the file.
             */ if ( ( $ input -> getPos ( ) + ( int ) $ config -> BUFFER ) > $ cipher_end ) { $ read = $ input -> readBytes ( $ cipher_end - $ input -> getPos ( ) ) ; } else { // @codeCoverageIgnoreStart $ read = $ input -> readBytes ( ( int ) $ config -> BUFFER ) ; // @codeCoverageIgnoreEnd } // Version 2+ uses a keyed BLAKE2b hash instead of HMAC \ sodium_crypto_generichash_update ( $ mac , $ read ) ; /** @var string $mac */ $ calcMAC = Util :: safeStrcpy ( $ mac ) ; $ calc = \ sodium_crypto_generichash_final ( $ calcMAC , ( int ) $ config -> MAC_SIZE ) ; if ( empty ( $ chunk_macs ) ) { // @codeCoverageIgnoreStart // Someone attempted to add a chunk at the end. throw new InvalidMessage ( 'Invalid message authentication code' ) ; // @codeCoverageIgnoreEnd } else { /** @var string $chunkMAC */ $ chunkMAC = \ array_shift ( $ chunk_macs ) ; if ( ! \ hash_equals ( $ chunkMAC , $ calc ) ) { // This chunk was altered after the original MAC was verified // @codeCoverageIgnoreStart throw new InvalidMessage ( 'Invalid message authentication code' ) ; // @codeCoverageIgnoreEnd } } // This is where the decryption actually occurs: $ decrypted = \ sodium_crypto_stream_xor ( $ read , ( string ) $ nonce , $ encKey -> getRawKeyMaterial ( ) ) ; $ output -> writeBytes ( $ decrypted ) ; \ sodium_increment ( $ nonce ) ; } if ( \ is_string ( $ nonce ) ) { \ sodium_memzero ( $ nonce ) ; } return true ; }
Stream decryption - Do not call directly

@param ReadOnlyFile $input
@param MutableFile $output
@param EncryptionKey $encKey
@param string $nonce
@param string $mac (hash context for BLAKE2b)
@param Config $config
@param array &$chunk_macs

@return bool

@throws FileError
@throws CannotPerformOperation
@throws FileAccessDenied
@throws FileModified
@throws InvalidMessage
@throws \TypeError
@throws \SodiumException
Stream decryption - Do not call directly