Моя статья 2016 года, изначально написанная для блога LightHouse Software. Приведенный код актуален и сегодня.

Думаю, нет необходимости лишний раз говорить о том, насколько в наше время важна защита личных данных и тайна переписки. Конечно, к нашим услугам имеются криптографические алгоритмы, но одного только шифрования порой бывает мало – иногда нужно не просто передать секретное сообщение, но и скрыть сам факт передачи. И здесь приходят на помощь алгоритмы стеганографии.

В древности для скрытия информации от посторонних глаз записи делали невидимыми чернилами, которые проявлялись в пламени свечи. Компьютерным аналогом этого вида стеганографии можно назвать метод LSB (Least Significant Bit, наименьший значащий бит). Суть этого метода в том, чтобы «замаскировать» секретное сообщение под безобидную, на первый взгляд, картинку. Наименьшие значащие биты каждого пикселя изображения-оригинала заменяются на биты скрываемого изображения. Дело в том, что слабые изменения канала мало влияют на цвет пикселя в целом, и, если использовать сильно зашумленную фотографию – например, пейзаж с деревьями – то на глаз никакой разницы заметно не будет.

Естественно, скрываемое изображение сохранится с сильными потерями: обычно его кодируют в 2 бита на канал – следовательно, при использовании модели RGB получается палитра из 4х4х4=64 цветов. Но и этого более чем достаточно, чтобы зашифровать, например, текст или чертеж, где не требуется большое количество цветовых оттенков.

Заметим также, что метод LSB применим только для lossless-форматов (BMP, PNG, TGA). Алгоритмы сжатия с потерями (такие, как JPEG) неизбежно модифицируют информацию в каналах, поэтому закодировать в них секретное сообщение не получится. Для JPEG, кстати, существуют собственные разновидности стеганографии, работающие в частотной области.

При помощи библиотеки dlib создание стеганографического файла PNG выглядит весьма тривиально – мы просто проходим циклом по всем байтам RGB-изображения и, с помощью побитовых сдвигов и операции «ИЛИ», записываем в два младших бита нашу секретную информацию:

import dlib.image;

// Входные изображения должны быть одинакового размера
auto input = loadPNG("input.png");
auto secret = loadPNG("secret.png");

auto stego = image(input.width, input.height, 3);

foreach(i, b; input.data)
{
    ubyte compressed = secret.data[i] / 85;
    stego.data[i] = ((b >> 2) << 2) | compressed;
}

stego.savePNG("stego.png");

Перекодирование секретного байта в 2-битный формат делается путем деления на 85 – максимальное значение для 2-битного канала равно 3, следовательно, 255 / 3 = 85.

Обратная операция – извлечение секретного сообщения из картинки – и того проще. При помощи операции «И» мы извлекаем два младших бита по маске 00000011 и переводим обратно в 8-битное представление путем умножения на 85:

output.data[i] = (stego.data[i] & 0b00000011) * 85;

Written by Gecko

Разработчик компьютерной графики

Оставить комментарий

Ваш адрес email не будет опубликован.