Время прочтения: 3 мин.

Для подтверждения достоверности аудиторских доказательств, представленных в виде изображений в формате «со сжатием без потерь» (lossless compression) png, bmp, tiff, помимо криптографических методов возможно применять алгоритмы стеганографии – способы передачи или хранения информации, которые позволяют скрыть определенное сообщение в медиа файле, сохраняя при этом втайне сам факт такой передачи (хранения) этой информации (метки-сообщения).

Сохраненная таким образом в файле изображения информация служит своего рода водяным знаком (stego watermarking) или цифровым отпечатком (digital fingerprint). Файл, не содержащий такой метки, не может считаться подлинным.

В этой статье я продемонстрирую, как с помощью языка программирования Python (ПК с ОС Windows) можно разместить необходимое нам, визуально незаметное текстовое сообщение в файле picture.bmp, используя метод Least Significant Bit.

Начнем с установки библиотеки OpenCV

pip install requests bs4

Импортируем необходимые модули

import cv2
import numpy as np

Создаем функцию toBin, которая выполнит преобразование любого типа данных в двоичные данные.

def toBin(data):
# Преобразуем данные в двоичный формат
    if isinstance(data, str):
        return ''.join([ format(ord(i), "08b") for i in data ])
    elif isinstance(data, bytes) or isinstance(data, np.ndarray):
        return [ format(i, "08b") for i in data ]
    elif isinstance(data, int) or isinstance(data, np.uint8):
        return format(data, "08b")
    else:
        raise TypeError("Тип не поддерживается.")

Функция code выполняет кодирование необходимой метки-сообщения (hiddeninf). Например, такой: «Attention! For internal use only!», в файл picture.bmp.

def code(imgName, hiddeninf):
    # Читаем изображение
    img = cv2.imread(imgName)
    # Максимум байт для кодирования
    nBytes = img.shape[0] * img.shape[1] * 3 // 8
    print("[!] Количество байт для кодирования:", nBytes)
    if len(hiddeninf) > nBytes:
        raise ValueError("[!] Недостаточно байт, требуется большее изображение или меньше данных кодирования.")
    print("...кодируем...")
    # Добавляем критерии остановки
    hiddeninf += "====="
    dataIndex = 0
    # Преобразовываем данные в двоичный формат
    binSecretData = toBin(hiddeninf)
    # Размер данных, которые необходимо скрыть
    dataLen = len(binSecretData)
    for row in img:
        for pixel in row:
            # Преобразовываем RGB в двоичный формат
            r, g, b = toBin(pixel)
            # Если есть еще данные для хранения, тогда изменить младший бит данных
            if dataIndex < dataLen:
                # Red-пиксель: младший значимый бит
                pixel[0] = int(r[:-1] + binSecretData[dataIndex], 2)
                dataIndex += 1
            if dataIndex < dataLen:
                # Green-пиксель: младший значимый бит
                pixel[1] = int(g[:-1] + binSecretData[dataIndex], 2)
                dataIndex += 1
            if dataIndex < dataLen:
                # Blue-пиксель: младший значимый бит
                pixel[2] = int(b[:-1] + binSecretData[dataIndex], 2)
                dataIndex += 1
            # Выйти из цикла, если данные закодированы
            if dataIndex >= dataLen:
                break
    return img

Следующая функция- decode выполняет декодирование изображения picture.bmp.

def decode(imgName):
    print("...декодируем...")
    # Читаем изображение
    img = cv2.imread(imgName)
    binData = ""
    for row in img:
        for pixel in row:
            r, g, b = toBin(pixel)
            binData += r[-1]
            binData += g[-1]
            binData += b[-1]
    # Разбивка по 8 бит
    allBytes = [ binData[i: i+8] for i in range(0, len(binData), 8) ]
    # Преобразование в символы
    decodedData = ""
    for byte in allBytes:
        decodedData += chr(int(byte, 2))
        if decodedData[-5:] == "=====":
            break
    return decodedData[:-5]

В итоге, применив созданные функции,

if __name__ == "__main__":
    inImg = "C:\\Users\\User\\picture.bmp"
    outImg = "C:\\Users\\ User\\coded_picture.bmp"
    hiddeninf = "Attention! For internal use only!"
    # Кодируем данные в изображение
    encodedImg = code(imgName=inImg, hiddeninf=hiddeninf)
    # Сохраняем изображение с закодированными данными
    cv2.imwrite(outImg, encodedImg)
    # Декодируем данные из изображения
    decodedData = decode(outImg)
    print("[!] Декодированные данные:", decodedData)

получаем следующий результат:

[!] Количество байт для кодирования: 40313
...кодируем...
...декодируем...
[!] Декодированные данные: Attention! For internal use only!

Внешне, после обработки, изображение-контейнер не будет отличаться от оригинала и только при его декодировании можно прочесть сохраненную в нем информацию (метку-сообщение).

Стеганография также применяется для защиты авторского (исключительного) права, скрытой передачи информации в файле-контейнере, отслеживании незаконной передачи (слива) информации недобросовестными сотрудниками сторонним лицам.