/**
* Recalculate and verify the HMAC of the input file
*
* @param resource $input
* @param resource $mac (hash context)
* @param Config $config
*
* @return Hashes of various chunks
* @throws FileAlert\AccessDenied
*/
private static final function streamVerify(ReadOnlyFile $input, $mac, Config $config)
{
$start = $input->getPos();
$cipher_end = $input->getSize() - $config->MAC_SIZE;
$input->reset($cipher_end);
$stored_mac = $input->readBytes($config->MAC_SIZE);
$input->reset($start);
$chunk_macs = [];
$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
*/
\hash_update($mac, $read);
/**
* Store a MAC of each chunk
*/
$chunkMAC = \hash_copy($mac);
if ($chunkMAC === false) {
throw new CryptoException\CannotPerformOperation('An unknown error has occurred');
}
$chunk_macs[] = \hash_final($chunkMAC, true);
}
/**
* We should now have enough data to generate an identical HMAC
*/
$finalHMAC = \hash_final($mac, true);
/**
* Use hash_equals() to be timing-invariant
*/
if (!\hash_equals($finalHMAC, $stored_mac)) {
throw new CryptoException\InvalidMessage('Invalid message authentication code');
}
$input->reset($start);
return $chunk_macs;
}