private static final function streamVerify(ReadOnlyFile $input, $mac, Config $config) : array
{
$start = $input->getPos();
// Grab the stored MAC:
$cipher_end = $input->getSize() - $config->MAC_SIZE;
$input->reset($cipher_end);
$stored_mac = $input->readBytes($config->MAC_SIZE);
$input->reset($start);
$chunkMACs = [];
$break = false;
while (!$break && $input->getPos() < $cipher_end) {
/**
* 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() + $config->BUFFER >= $cipher_end) {
$break = true;
$read = $input->readBytes($cipher_end - $input->getPos());
} else {
$read = $input->readBytes($config->BUFFER);
}
/**
* We're updating our HMAC and nothing else
*/
\Sodium\crypto_generichash_update($mac, $read);
// Copy the hash state then store the MAC of this chunk
$chunkMAC = Util::safeStrcpy($mac);
$chunkMACs[] = \Sodium\crypto_generichash_final($chunkMAC, $config->MAC_SIZE);
}
/**
* We should now have enough data to generate an identical MAC
*/
$finalHMAC = \Sodium\crypto_generichash_final($mac, $config->MAC_SIZE);
/**
* Use hash_equals() to be timing-invariant
*/
if (!\hash_equals($finalHMAC, $stored_mac)) {
throw new InvalidMessage('Invalid message authentication code');
}
$input->reset($start);
return $chunkMACs;
}