Hash copy: примеры (PHP)

Изучаем hash_copy: копирование контекста хеширования в PHP
Раздел: Хеширование и шифрование
hash_copy(HashContext $context): HashContext
Описание функции hash_copy

Функция hash_copy создает копию контекста хеширования. Она используется для сохранения промежуточного состояния процесса хеширования данных.

Основное применение – инкрементальное хеширование больших объемов информации, когда необходимо вычислить хеш от нескольких частей данных, объединенных одним алгоритмом, без сохранения всех данных в памяти.

Синтаксис и аргументы
hash_copy(HashContext $context): HashContext
  • $context – обязательный параметр, объект типа HashContext, возвращаемый функцией hash_init(). Представляет собой инициализированный контекст хеширования с определенным алгоритмом.
  • Возвращаемое значение – новый объект HashContext, который является копией исходного контекста. Ошибки выбрасываются как исключения.
Краткие примеры использования
Пример 1: Базовое копирование контекста
$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
Пример 2: Использование с флагами
$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... (сокращено)
Похожие функции в PHP
  • hash_init() – создает новый контекст хеширования. Используется вместе с hash_update() и hash_final() для инкрементального хеширования. hash_copy работает с объектом, созданным этой функцией.
  • hash() – вычисляет хеш от строки за один вызов. Предпочтительнее для простого хеширования готовых данных, когда не требуется поэтапная обработка.
  • hash_hkdf() – генерирует производный ключ. Используется для других целей (расширение ключей), а не для копирования состояния.
  • hash_pbkdf2() – функция для генерации ключа на основе пароля. Не является альтернативой для инкрементального хеширования.

Функцию hash_copy выбирают, когда необходим контроль над процессом хеширования на разных этапах, например, при верификации данных по частям или создании нескольких хешей с общей начальной частью.

Аналоги в других языках
Python (hashlib)
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.

JavaScript (Web Crypto API)

Прямого аналога в стандартном API нет. Хеширование обычно выполняется за один раз. Для инкрементального хеширования можно использовать SubtleCrypto.digest() с предварительной конкатенацией данных или сторонние библиотеки.

MySQL

Функции хеширования (MD5(), SHA2()) вычисляют значение сразу от аргумента. Инкрементальное хеширование не поддерживается.

Go
package main
import (
    "crypto/sha256"
    "fmt"
)
func main() {
    h := sha256.New()
    h.Write([]byte("data"))
    hCopy := *h // Попытка копирования структуры
    // Но в Go это не сработает корректно для hash.Hash
    // Требуется использование специальных методов или библиотек
}

В Go прямое копирование интерфейса hash.Hash обычно не приводит к полному копированию состояния.

Типичные ошибки
Ошибка 1: Копирование неинициализированного или завершенного контекста
$ctx = hash_init('sha256');
$result = hash_final($ctx);
$copy = hash_copy($ctx); // Ошибка!
Fatal error: Uncaught Error: hash_copy(): Argument #1 ($context) ...

После вызова hash_final() контекст становится недействительным.

Ошибка 2: Использование ресурса вместо объекта (для старых версий PHP)
// В PHP 7.2 и ранее hash_init возвращал ресурс
// В PHP 8 это уже объект, ошибка будет другой
$ctx = hash_init('md5');
// Некорректная попытка передать не объект
$copy = hash_copy($ctx); // В PHP 8 работает, в старых версиях - нет

В PHP 8 функция принимает объект HashContext, что делает типизацию более строгой.

Ошибка 3: Изменение данных после копирования, когда ожидается одинаковый результат
$ctx = hash_init('sha1');
hash_update($ctx, 'data');
$copy = $ctx; // Это не копия, а ссылка на тот же объект!
hash_update($ctx, 'more');
echo hash_final($ctx) === hash_final($copy) ? 'равны' : 'не равны';
равны

Для создания настоящей копии необходимо использовать hash_copy(), а не простое присваивание.

Изменения в версиях PHP
  • PHP 5.3.0 – функция hash_copy() была добавлена.
  • PHP 7.2.0 – тип возвращаемого значения и параметра изменен с ресурса (resource) на объект HashContext.
  • PHP 8.0.0 – функция теперь выбрасывает исключение Exception в случае ошибки, вместо возврата false. Тип аргумента строго указан как HashContext.
  • Поддержка новых алгоритмов хеширования (например, sha3-серия) автоматически становится доступной для hash_copy().
Расширенные примеры
Пример 1: Построчное хеширование большого файла с ветвлением
Пример php
// Имитация чтения большого файла
$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...
Пример 2: Генерация дерева хешей (упрощенная модель)
Пример php
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...
)
Пример 3: Проверка целостности потоковых данных с промежуточными контрольными точками
Пример php
// Эмуляция приема данных по частям
$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...
Пример 4: Работа с HMAC и разными ключами на основе общего префикса данных
Пример php
// Общие начальные данные
$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...

PHP hash_copy function comments

En
Hash copy Copy hashing context