Итак, мы засылаем супер-секретных агентов Алису и Боба во вражескую страну под прикрытием. В процессе миссии им предстоит связаться и работать вместе, обмениваться информацией, обычные шпионские дела. Конечно, все это нужно делать с соблюдением всех возможных правил и техник безопасности. Ведь в последнюю очередь мы хотим их раскрытия: под угрозой находятся как непосредственно миссия и сами агенты, так и вся национальная безопасность. Поэтому в наших интересах давать шпионам минимум необходимой информации. В частности, чем меньше они знают друг о друге и техниках связи, тем лучше.  Но как тогда им опознать своего товарища по штабу?  TL;DR — изобретаем механизм аутентификации пользователей с помощью стеганографии для воображаемого трехсимвольного агентства несуществующей страны. Прикрытие есть прикрытие, поэтому ни Алиса, ни Боб не должны вызывать никакими своими действиями подозрения. Правильное планирование подразумевает паранойю о постоянной слежке за ними на всех возможных уровнях. В данном посте не будет рассматриваться задача непосредственного обмена информацией (она заслуживает своей отдельной серии), но лишь способ удостовериться, что она передается тем, кем надо, тому, кому надо. Скорее всего, оба шпиона будут иметь историю в формате обычных граждан, притом никак между собой не связанных. Поэтому сразу приходится наложить вето на использование классических криптографических средств и защищенных каналов — каждый агент контрразведки знает, что честным людям, не имеющим тесной связи, скрывать нечего. Конечно, такая задача не нова, она счастливо существовала и решалась задолго до появления этих ваших интернетов. И мало того, что решалась, так некоторые решения укрепились в культуре и до сих пор встречаются в книгах, фильмах и играх. Посмотрим хотя бы на такую сцену: два человека в длинных пальто сходятся в публичном месте и обмениваются очень странными фразами. Если инициирующая фраза и ответ верны, то аутентификация прошла успешно, а люди обмениваются папками с пометкой "Top Secret" и расходятся в неизвестных направлениях. Сразу очевиден недостаток такой схемы — фразы надо хранить в секрете и часто менять, что не очень-то и просто на вражеской территории. При этом для того, чтобы не быть произнесенными случайно и не привести к случаю с КДПВ, они делаются довольно выделяющимися и случайными, а значит, могут выдать агентов, их произносящих.   Нас, в век цифровых технологий, такой метод не устраивает. Особенно, если вспомнить, что почти все каналы связи подконтрольны кому-то и прослушиваются как из хороших, так и из плохих побуждений. И в чем бы нас они не уверяли, жизни людей ну не стоит доверять политике приватности какого-нибудь фейсбука.  Ежу понятно, что возможность скрыть одно в другом в такой ситуации выглядит как никогда привлекательно. И в самом деле, даже описанный метод является ее подвидом — кодовые фразы можно рассматривать как контейнеры со всего одним битом информации внутри. То же млекопитающее отряда насекомоядных понимает, что речь при этом идет не о простом перекидывании шпионами друг другу стегоконтейнеров. Подобный обмен будет вызывать чуть ли не больше подозрений, чем какое-нибудь распространенное PGP-шифрование, поэтому нас не интересует. В отличие от криптограмм, стегоконтейнеры обладают очевидным преимуществом — контекстом применения. Любой текст, изображение, аудиофайл и пр. помимо очевидного содержимого несет также возможность его естественного обсуждения и может быть отправлен не просто так с бухты-барахты, а в процессе не вызывающего подозрения диалога. Вооружившись лишь такими идеями, мы уже можем составить простой протокол стеганографической аутентификации на основе общего ключа: Такой протокол обладает очевидными недостатками — Алиса и Боб должны обладать как общим ключом, так и функциями встраивания и извлечения. Их компрометация может привести к возможности детального анализа метода аутентификации противника и поставит под угрозу как других пользователей, так и сам штаб. Надо что-то исправлять. Если читатель учился в школе после появления компьютерных классов, то он должен помнить обучение основам алгоритмизации с помощью исполнителя черепашки, муравья и подобных. Их идея заключалась в демонстрации возможностей оптимизации большого числа ручных одиночных действий с помощью создания простых программ. Для решения нашей же задачи нужно пойти в обратную сторону.  Раз мы можем упрощать запись конечного алгоритма от последовательности шагов к их процедурному описанию по заданным параметрам, то можем провести и обратный процесс. Если представить контейнер массивом из каких-то его составляющих, то встраивание сообщения по ключу можно записать как упорядоченную последовательность операций над элементами контейнера по определенным индексам с различными константными параметрами. Здесь начинается недоматематика, поэтому пугливых прошу просто пролистать сложно выглядящие параграфы до раздела эксплуатации или даже чуть подальше. Ничего страшного не произойдет, я обещаю. Для встраивания же данных нам нужна последовательность формы: (f1, S1, i, D1), (f2, S2, j, D2)..., где: Для извлечения не нужно хранить части данных (К.О.), поэтому достаточно троек: (g1, S1, I1), (g2, S2, I2)... с аналогичными значениями, только gi: (State, Element) -> (State, D). Все это можно изобразить симметричной диаграммой ниже. Если по каким-то причинам понятности достичь мне не удалось, то не страшно, просто читай дальше.  Видно, что функция встраивания имеет большее число степеней свободы. В отличие от своей сестры, она модифицирует контейнер, при этом делает это на основе двух независимых элементов — встраиваемых данных и элемента. Благодаря, или, точнее, из-за этого возможно два глобальных подхода к реализации алгоритма стеганографии такой системой: Если для алгоритма не требуется состояние, то все вышесказанное остается в силе, просто без одной буквы и блока на диаграмме. Без него даже проще, на самом деле. Теперь, если знать заранее, какие контейнеры с какими сообщениями и ключами будут использоваться, можно вместо полного раскрытия частей алгоритма генерировать и давать агентам на пользование лишь подобные последовательности и интерпретатор для них. Ну окей, не просто давать, конечно, но об этом далее. Даже исполнитель черепашка может нарисовать квадрат сотнями разных способов, просто меняя порядок операций и добавляя новые. А значит, никто не мешает и нам сделать то же самое с описанными последовательностями для фиксированных входных данных.  То есть, мы можем взять последовательность встраивания, добавить новые операции, перемешать это все, да так, что результат останется тем же. Разве что, при наличии состояния, нужно будет его отслеживать и добавлять отдельно необходимые изменения в последовательность. Именно поэтому без него проще, да. Так или иначе, после подобного замешивания и зашумления даже сам встраивающий уже не сможет понять, что же он такого, собственно, встраивает: любая последовательность из N операций будет представлять N! потенциально встраиваемых сообщений — по одному на каждую перестановку встраиваемых частей. При этом само N находится под большим вопросом. Поэтому можно звать такие последовательности открытыми — они не дают никакой информации ни о встраиваемом сообщении, ни об используемом алгоритме и ключе. При извлечении информации же нам очень важен как порядок (для восстановления того самого верного сообщения из всех возможных), так и количество извлекаемых частей, поэтому извлекающие последовательности остаются неизменяемыми с момента появления на свет. Так как они неявно содержат информацию об используемом ключе, генераторе и алгоритме, их, как животных из красной книги, нужно хранить и беречь. И держать в секрете. При чем тут асимметричность? Дело в том, что теперь каждой извлекающей последовательности сопоставляется бесконечное число встраивающих. А восстановление одной по другой — в общем случае задача неразрешимая. Забываем про любую около-математику и возвращаемся к исходной задаче — как же нам заслать Алису с Бобом на вражескую территорию, чтобы:  Ну с первым пунктом все понятно, мы просто им не даем никакой явной информации друг о друге, никаких общих ключей. Для второго нужно вспомнить описание протокола повыше. Теперь мы можем исключить из использования непосредственно алгоритмы Embed и Extract, представляющие потенциальную гостайну и все такое. И, с учетом сказанного, для третьего можно составить следующий двухэтапный протокол. Выработка аутентификационной информации до начала миссии со штабом в роли доверенной стороны Трента: Таким образом, у Алисы оказывается лишь одна последовательность на все случаи жизни и контекст будущего контакта, а Боб становится счастливым обладателем набора одноразовых последовательностей и хэшей сообщений, ими встраиваемых. Протокол аутентификации уже во время миссии выглядит так: Внимательный читатель заметит, что до проведения протокола что Алиса, что Боб обладают просто набором информации, которая сама по себе ничего не значит ни для них, ни для потенциального противника, и лишь во время проведения "играет красками". Каждый открытый набор Боба используется только раз, что контролируется предпоследним шагом Алисы. При встрече ранее использованного M (а значит, и невидимой ею Em) другим лицом она понимает, что один из ее "соратников" — фальшивка. Повторное использование одним и тем же лицом говорит ей, что оно не в курсе тонкостей проведения протокола и уж точно не является тем, с кем ей нужно было вступить в контакт. Что ж, лучше поздно, чем никогда.  Окей, вот так вот это все выглядит слишком сложным и непонятным. Кто-нибудь до сюда добрался? Давайте лучше продемонстрирую на практике, потому что даже самим шпионам детали протокола знать для использования не надо, что уж про бедных читателей говорить. Только сначала немного о том, как оно все было реализовано. Итак, осталось лишь написать все необходимое для проведения протокола. Ну не руками ведь все делать (хотя и так можно). И сегодня жертвой моего кода станет… крутит колесо фортуны… Джава? Ну и ладно, заодно все в STL'е будет, ничего искать не придется. Начнем с необходимого API. Для работы необходимо лишь определить класс массива элементов контейнера с возможностью получать и изменять элементы по индексу: Дальнейшее использование сводится к созданию обертки стеганографического автомата над нужным контейнером и подачи на его вход функций встраивания и извлечения: Аналогичным образом используются классы с суффиксом Stateless, в случае, если для реализации алгоритма не требуется поддерживать внутреннее состояние. Генераторы последовательностей могут работать как угодно и не обладают общим API. Частью данных может являться в общем случае вообще что угодно, от одиноких битов до наскальной живописи в отдельной кодировке. В качестве примера реализации, с использованием созданных интерфейсов я реализовал простой алгоритм из семейства LSB для растровых изображений со сжатием без потерь. Их элементами являются пиксели, не имеющие соседов по младшему биту всех компонент RGB. Функция встраивания работает с одинокими битами исходных данных и просто изменяет младший бит значения одной из компоненты (на какую укажет индекс). Довольно просто, но отлично подходит для реализации протокола, так как изменение любого элемента одинаково незаметно по их выбору, так что индексы изменяемых элементов генерировать с помощью случайного генератора. В случае джавы, с помощью SecureRandom, но при желании он легко меняется на свой источник энтропии. Тем не менее, это очень простой метод, не советую его использовать настоящим шпионам. Так как текст имеет свойство искажаться в зависимости от симулируемой личности агента (некоторые не ставят заглавные, другие любят ставить смайлики и пр, третьи вообще неграмотны), то для расчета хэша я предлагаю использовать sha256, но только от печатных слов, переведенных в нижний регистр: Программный комплекс состоит из двух частей — одна для генерации последовательностей и прочих хэшей для Трента, другая для проведения встраивания и проверки полученных сообщений на соответствие. Работа с обеими происходит из командной строки посредством ее аргументов и потоков ввода-вывода, других интерфейсов не завезли (страх и ужас). Все-таки быть что работником штаба, что шпионом — означает иметь какую-никакую квалификацию. Ну а если нет — все равно покажу на примере.  Для начала, Тренту в штабе надо выработать аутентификационную информацию. В частности, продумать заранее ситуацию, в которой агенты будут работать.  Например, пусть Боб будет фрилансером, занимающимся графикой, а Алиса — его заказчиком. Аутентификация же будет происходить под видом заказа создания графики / дизайна / чего-нибудь еще. Сообщаем эту полезную информацию обоим и возвращаемся к самому протоколу. Подготовим заранее подходящее встраиваемое сообщение M.txt, минимизируя число знаков в нем: "отлично мне подходит куда перечислять деньги". Генерируем Em и Ex с помощью утилиты для Трента: Тут $(stat -c%s "M.txt") возвращает размер сообщения в байтах, а 4096 — ограничение для диапазона генерируемых индексов (чтобы позволить использовать контейнеры поменьше). Аналогично, $(cat M.txt) используется для передачи самого сообщения в параметр командной строки. В принципе, можно обойтись и без баша, используя свой ручной труд, но кому как удобнее. Ex.txt передается Алисе, Em.txt и hash.bin — Бобу. Теперь представим, что агенты успешно внедрились и хотят связаться друг с другом — переходим к выполнению протокола. Боб размещает свое резюме или предложение работы на какой-нибудь бирже, а Алиса начинает общение: Боб ищет изображение зонтика, может быть даже рисует сам, если душа творческая, немного сжимает / накладывает ватермарку (или что там нынче фрилансеры делают) и выполняет: Подождав некоторое время, симулируя работу, если в реальности ее не делал, он отправляет Алисе полученный контейнер, естественно, придерживаясь контекста:  Та, в свою очередь, извлекает хранимое внутри сообщение: Превращает набор слов в нормальное предложение и отправляет его Бобу:  Тот проверяет верность сообщения: И продолжает непринужденное общение, если все окей. Весь диалог со стороны наблюдателя выглядит как-то так:  Понятное дело, что ничего подозрительного контр-разведка во всем этом перехваченным не найдет. На самом деле, даже методы стегоанализа в таком случае далеко не всегда будут применяться — ну заказал кто-то за 5 баксов картинку зонта, нашли, чем удивить интернет. Вычислительные ресурсы и люди не бесконечны, чтобы каждую такую ситуацию проверять. Аутентификация же прошла успешно, занавес. -> GitHub Безработная информационная безопасность