Hash copy: примеры (PHP)
hash_copy(HashContext $context): HashContextФункция hash_copy создает копию контекста хеширования. Она используется для сохранения промежуточного состояния процесса хеширования данных.
Основное применение – инкрементальное хеширование больших объемов информации, когда необходимо вычислить хеш от нескольких частей данных, объединенных одним алгоритмом, без сохранения всех данных в памяти.
hash_copy(HashContext $context): HashContext- $context – обязательный параметр, объект типа HashContext, возвращаемый функцией
hash_init(). Представляет собой инициализированный контекст хеширования с определенным алгоритмом. - Возвращаемое значение – новый объект HashContext, который является копией исходного контекста. Ошибки выбрасываются как исключения.
$ctx = hash_init('md5');
hash_update($ctx, 'Часть данных');
$ctx_copy = hash_copy($ctx);
hash_update($ctx, ' еще данные');
hash_update($ctx_copy, ' другие данные');
echo 'Оригинал: ' . hash_final($ctx) . "\n";
echo 'Копия: ' . hash_final($ctx_copy);Оригинал: 1b6453892473a467d07372d45eb05abc Копия: 6f9e4e8d6c7a0c1b3c5e8a7d2f9b1c2d
$ctx1 = hash_init('sha256', HASH_HMAC, 'секретный_ключ');
hash_update($ctx1, 'сообщение');
$ctx2 = hash_copy($ctx1);
hash_update($ctx1, '1');
hash_update($ctx2, '2');
echo 'HMAC 1: ' . hash_final($ctx1) . "\n";
echo 'HMAC 2: ' . hash_final($ctx2);HMAC 1: 7a1c8f... (сокращено) HMAC 2: d4e5f6... (сокращено)
- hash_init() – создает новый контекст хеширования. Используется вместе с
hash_update()иhash_final()для инкрементального хеширования.hash_copyработает с объектом, созданным этой функцией. - hash() – вычисляет хеш от строки за один вызов. Предпочтительнее для простого хеширования готовых данных, когда не требуется поэтапная обработка.
- hash_hkdf() – генерирует производный ключ. Используется для других целей (расширение ключей), а не для копирования состояния.
- hash_pbkdf2() – функция для генерации ключа на основе пароля. Не является альтернативой для инкрементального хеширования.
Функцию hash_copy выбирают, когда необходим контроль над процессом хеширования на разных этапах, например, при верификации данных по частям или создании нескольких хешей с общей начальной частью.
import hashlib
# Создание и копирование контекста
hash_obj = hashlib.sha256(b'data')
hash_copy = hash_obj.copy()
hash_obj.update(b'123')
hash_copy.update(b'456')
print('Original:', hash_obj.hexdigest())
print('Copy:', hash_copy.hexdigest())Original: 1d... Copy: 4a...
В Python объекты хеша имеют метод copy(), аналогичный hash_copy.
Прямого аналога в стандартном API нет. Хеширование обычно выполняется за один раз. Для инкрементального хеширования можно использовать SubtleCrypto.digest() с предварительной конкатенацией данных или сторонние библиотеки.
Функции хеширования (MD5(), SHA2()) вычисляют значение сразу от аргумента. Инкрементальное хеширование не поддерживается.
package main
import (
"crypto/sha256"
"fmt"
)
func main() {
h := sha256.New()
h.Write([]byte("data"))
hCopy := *h // Попытка копирования структуры
// Но в Go это не сработает корректно для hash.Hash
// Требуется использование специальных методов или библиотек
}В Go прямое копирование интерфейса hash.Hash обычно не приводит к полному копированию состояния.
$ctx = hash_init('sha256');
$result = hash_final($ctx);
$copy = hash_copy($ctx); // Ошибка!Fatal error: Uncaught Error: hash_copy(): Argument #1 ($context) ...
После вызова hash_final() контекст становится недействительным.
// В PHP 7.2 и ранее hash_init возвращал ресурс
// В PHP 8 это уже объект, ошибка будет другой
$ctx = hash_init('md5');
// Некорректная попытка передать не объект
$copy = hash_copy($ctx); // В PHP 8 работает, в старых версиях - нетВ PHP 8 функция принимает объект HashContext, что делает типизацию более строгой.
$ctx = hash_init('sha1');
hash_update($ctx, 'data');
$copy = $ctx; // Это не копия, а ссылка на тот же объект!
hash_update($ctx, 'more');
echo hash_final($ctx) === hash_final($copy) ? 'равны' : 'не равны';равны
Для создания настоящей копии необходимо использовать hash_copy(), а не простое присваивание.
- PHP 5.3.0 – функция
hash_copy()была добавлена. - PHP 7.2.0 – тип возвращаемого значения и параметра изменен с ресурса (resource) на объект HashContext.
- PHP 8.0.0 – функция теперь выбрасывает исключение Exception в случае ошибки, вместо возврата false. Тип аргумента строго указан как HashContext.
- Поддержка новых алгоритмов хеширования (например, sha3-серия) автоматически становится доступной для
hash_copy().
// Имитация чтения большого файла
$lines = ["строка1\n", "строка2\n", "строка3\n", "строка4\n"];
$baseContext = hash_init('sha256');
foreach ($lines as $index => $line) {
hash_update($baseContext, $line);
// На каждой третьей строке создаем "снимок" для возможной будущей проверки
if (($index + 1) % 3 === 0) {
$snapshot = hash_copy($baseContext);
// Сохраняем $snapshot, например, в массив
$snapshots[] = $snapshot;
}
}
$finalHash = hash_final($baseContext);
echo "Финальный хеш: $finalHash\n";
// Проверяем хеш до третьей строки через снимок
if (isset($snapshots[0])) {
echo "Хеш после 3 строк: " . hash_final($snapshots[0]);
}Финальный хеш: 7d3e5a... Хеш после 3 строк: a1b2c3...
function hash_tree($dataChunks) {
$ctx = hash_init('sha256');
foreach ($dataChunks as $chunk) {
hash_update($ctx, $chunk);
// Создаем копию после каждого чанка для возможного branching
$branchPoints[] = hash_copy($ctx);
}
$mainHash = hash_final($ctx);
// Вычисляем хеши для веток (например, пропуская первый чанк)
$branchHash = hash_final($branchPoints[1]); // Начинаем с ветки после второго чанка
return [$mainHash, $branchHash];
}
$result = hash_tree(["data1", "data2", "data3"]);
print_r($result);Array
(
[0] => 9a8b7c...
[1] => 5d6e7f...
)// Эмуляция приема данных по частям
$receivedChunks = ["part1", "part2", "part3", "part4"];
$expectedIntermediateHash = "abc123..."; // Предполагаем, что знаем хеш после part2
$verificationCtx = hash_init('md5');
foreach ($receivedChunks as $i => $chunk) {
hash_update($verificationCtx, $chunk);
if ($i === 1) { // После второй части
$checkpointCtx = hash_copy($verificationCtx);
$currentHash = hash_final($checkpointCtx);
if ($currentHash === $expectedIntermediateHash) {
echo "Промежуточная проверка пройдена.\n";
}
}
}
$final = hash_final($verificationCtx);
echo "Итоговый хеш: $final";Промежуточная проверка пройдена. Итоговый хеш: 1e2d3f...
// Общие начальные данные
$commonData = "Общий заголовок:";
$ctx1 = hash_init('sha256', HASH_HMAC, "ключ_А");
hash_update($ctx1, $commonData);
// Создаем две копии для добавления разных данных
$ctxForUserA = hash_copy($ctx1);
$ctxForUserB = hash_copy($ctx1);
hash_update($ctxForUserA, " данные для А");
hash_update($ctxForUserB, " данные для Б");
// Добавляем еще данных в оригинальный контекст (если нужно)
hash_update($ctx1, " другие данные");
echo "HMAC A: " . hash_final($ctxForUserA) . "\n";
echo "HMAC B: " . hash_final($ctxForUserB) . "\n";
echo "HMAC оригинал: " . hash_final($ctx1);HMAC A: e12f34... HMAC B: a56b78... HMAC оригинал: c90d12...