Как посчитать имитовставку mac функциями openssl
Продолжая традицию, начатую в статьях 1 и 2 нашего блога, предлагаем обсудить еще одну из атак на протокол TLS. В сегодняшней статье будет рассмотрено, как предпринятые в TLS 1.1 и TLS 1.2 контрмеры, направленные на устранение уязвимости протокола к атакам, основанным на методах Барда-Дэя и Воденея, сами привели к возникновению новых уязвимостей и к появлению новых типов атак.
Для ликвидации уязвимости к атакам, аналогичным атаке Воденея, был принят ряд контрмер, связанных с порядком вычисления имитовставки при некорректном заполнении. В пункте 6.2.3.2 RFC 5246 (описание протокола TLS версии 1.2) присутствует следующая рекомендация: указано, что лучшим методом защиты от временных атак, аналогичных атаке Воденея, является вычисление значения имитовставки даже в случае некорректного заполнения, например, вычисление значения имитовставки по всему полученному после расшифрования фрагменту в предположении отсутствия заполнения.
«For instance, if the pad appears to be incorrect, the implementation might assume a zero-length pad and then compute the MAC. This leaves a small timing channel, since MAC performance depends to some extent on the size of the data fragment, but it is not believed to be large enough to be exploitable, due to the large block size of existing MACs and the small size of the timing signal.»
Таким образом, указано, что предлагаемые меры оставляют временной канал утечки информации, связанный с наличием разницы во времени вычисления имитовставки в случае сообщения с корректным заполнением (время вычисления самого сообщения) и модифицированного сообщения с некорректным заполнением (время вычисления сообщения и соответствующих заполнению байтов). Также сделан вывод, что канал невозможно использовать по причине большого размера блока современных алгоритмов выработки имитовставки. Буквально сказано так: «предполагается, что временной канал недостаточно большой для его использования».
Не странно ли видеть слова «предполагается, что это безопасно» в спецификации криптографического протокола? Так вот, это предположение оказывается неверным.
Атака Аль-Фардана и Патерсона или Число 13 приносит удачу
При наличии низкоинформативного побочного канала возможны два пути его эксплуатации. Первый путь заключается в нахождении относительно реалистичных условий, при которых возможно стабильное долговременное использование данного канала, обеспечивающее достаточный для анализа объём получаемых данных.
Второй путь заключается в построении дополнительных технических и математических механизмов для расширения данного побочного канала.
Успехов на первом пути добились Аль-Фардан (AlFardan) и Патерсон (Paterson) из Лондонского Университета (City University London). Метод Аль-Фардана–Патерсона предполагает модель нарушителя, в которой нарушитель имеет возможность наблюдать и подменять сообщения в канале в точке, близкой к атакуемому клиенту или серверу. Неформально определенное требование «близости» происходит из необходимости работы с крайне слабым побочным каналом по времени.
Опишем построение данного канала утечки и использование этого канала для дешифрования блока Сi (здесь и далее используются обозначения из этой статьи) передаваемого по каналу связи шифртекста С = (С1|С2|С3|. |Cm) = Ek(M1|M2|M3|. |Mm). Будем считать, что для шифрования используется AES (важен не сам алгоритм шифрования, а его длина блока) с длиной блока равной 128 бит, а для выработки кода аутентификации используется HMAC-SHA-1 с длиной блока 160 бит.
Для некоторого случайно выбранного значения Ω сформируем шифртекст C att ( Ω ) = (HDR|IV|C1|C2|Ci‑1⊕ Ω |Ci) , где HDR – заголовок, C0 – произвольный блок. После расшифрования на стороне получателя последний блок P4 будет равен Dk(Ci) ⊕ Ci‑1 ⊕ Ω = Mi. Далее возможны следующие три случая: блок Mi ⊕ Ω в конце содержит корректное заполнение длины 1, длины 2 (или более, с вероятностью в 256 раз меньшей), либо некорректное заполнение.
С учётом указанной выше рекомендации RFC 5246, в случае некорректного заполнения код аутентификации сообщения вычисляется по всему тексту, т.е. до конца блока Mi. По свойствам протокола TLS, в этом случае он вычисляется на 57 или более байт, если заголовок имеет длину 13 байтов (отсюда название атаки «Lucky Thirteen»), а в случае корректного заполнения длины 1 – на 56 байт. В случае же, когда присутствует корректное заполнение длины 2 и более, происходит расчёт кода аутентификации сообщения на 55 или менее байт.
С учетом структуры HMAC-SHA-1, в случае длины аутентифицируемых данных 55 или менее требуется не более 4 вычислений функции сжатия SHA-1, тогда как при длине 56 или более требуется 5 или более вычислений. После этих вычислений происходит возврат ошибки (вероятность появления коллизий при проверке HMAC-SHA-1 пренебрежимо мала), которую наблюдает нарушитель.
Измерение времени до возврата ошибки позволяет использовать побочный канал по времени, связанный с количеством вычислений функции сжатия. В случае быстрого возврата ошибки (4 вычисления функции сжатия) нарушителю известно, что в последних 2 байтах (или более, но вероятность этого меньше в 256 раз) Mi ⊕ Ω содержится корректное заполнение. Зная структуру заполнения и значение Ω, нарушитель однозначно определяет последние два байта Mi. Зная последние два байта Mi, далее возможно тем же способом, модифицируя Ω , восстановить третий с конца байт Mi, и так далее.
Звучит интересно, а что на практике?
Допустим что сервер, клиент и атакующий находятся в одной сети VLAN с пропускной способностью 100 Mbps. Атакующий начинает перехватывать пакеты от клиента к серверу, модифицировать их и отправлять на сервер и собирает статистику по времени получения сообщений TLS alert от сервера. Для повышения скорости атаки сразу после получения ответа от сервера атакующий отсылает клиенту и серверу RST пакет, который приводит к мгновенному закрытию соединения. Для подбора каждого байта в худшем случае нужно L*256 попыток, где L – это количество попыток с одинаковым Ω . Несколько попыток с одинаковым Ω нужно для уменьшения вероятности неправильной трактовки результата. Эксперименты показали что вероятность правильного подбора байта при росте L от 1 до 128 изменяется от 0,756 до 1. Уже при L=8 вероятность удачи равна 0.914. Это говорит о том, что даже при одной попытке на каждое Ω мы имеем достаточно большую вероятность правильно отличить ошибки заполнения от ошибки имитозащиты. На момент публикации статьи практически все известные реализации TLS (OpenSSL, GnuTLS, NSS, Java BC, PolarSSL, yaSSL) были уязвимы к данному варианту временной атаки.
Наша атака или А если нет разницы, зачем заполнять меньше?
Годом ранее на конференции РусКрипто’2012 специалистами ООО «КРИПТО-ПРО» была представлена другая временная атака, направленная на расширение побочного канала.
Заметим, что в пункте 6.2.3.2 RFC 5246 указано, что заполнение может иметь любую длину, не превосходящую 255 байтов, обеспечивающую должное выравнивание. Таким образом, при размере блока алгоритма выработки имитовставки в 16 байтов сообщение длиной, например, 16 байтов, дополненное 240 байтами вида 0xf0, будет считаться дополненным корректно, и при проверке корректности сообщения будет вычисляться имитовставка на 1 блок. В случае же контролируемой модификации конечных блоков данного открытого текста (производимой аналогично методу Воденея) заполнение будет приниматься как некорректное, и имитовставка будет вычисляться на 16 блоков, обеспечивая существенную разницу во времени вычисления, создающую потенциальный канал утечки информации. По сравнению с атакой « Lucky Thirteen » э то позволяет ослабить требование по « близости» нарушителя к атакуемому клиенту или серверу. Наша атака работает в модели нарушителя, аналогичной BEAST , т.е. помимо навязывания шифртекста нарушитель имеет возможность навязывать открытый текст для зашифрования.
Идея атаки состоит в следующем. На расшифрование подается сообщение, после расшифрования которого в конце оказывается несколько блоков, интерпретируемых либо как часть корректного длинного заполнения, либо как часть сообщения с некорректным заполнением. В случае корректного заполнения имитовставка рассчитывается только на предшествующую этим блокам часть сообщения, иначе – на все сообщение. В случае большого числа этих блоков разница во времени вычисления имитовставки окажется значительной.
Благодаря данному временному каналу восстанавливается последний байт блока, предшествующего добавляемой части. После определения данного байта, аналогичным образом восстанавливается предшествующий ему, и так далее.
Описанная в нашей статье атака не может быть осуществлена напрямую из браузера сценарием JavaScript, т.к. этот уровень не управляет дополнением открытого текста для выравнивания границ блоков – это происходит на уровне реализации протокола TLS. Тем не менее, несложно модифицировать данную атаку таким образом, что она будет осуществима с помощью сценария JavaScript. Для этого понадобится, чтобы сценарий нарушителя дополнял отправляемое в канал сообщение в конце до границы блока аналогичным образом, но при этом отбрасывал последний байт. Тогда реализация TLS добавит заполнение в 1 байт вида 0x00, и у атакующего остаётся возможность подбирать открытый текст побайтово с конца в условиях существенной разницы по времени вычисления имитовставки при корректном и некорректном заполнении.
Ранее мы уже писали почему наша реализация TLS и другие, использующие поточные алгоритмы шифрования, неуязвимы к подобным атакам.
Судя по всему, тема атак на протокол TLS неисчерпаема, а значит, нам будет о чём писать в нашем блоге. Не отключайтесь!
Беляев Анатолий
Смирнов Павел
Смышляев Станислав
SSH с высоты птичьего полёта, или разгребаем кучи ключей
Решил я недавно разобраться в подробностях работы SSH. Использовал его для удалённого запуска команд давно, но, будучи не слишком опытным в системном администрировании, очень размыто представлял, зачем админы просят им отправить какой-то ключ, что с этим ключом происходит при подключении, зачем при запуске ssh периодически орёт на меня какими-то предупреждениями, и прочие прелести. К своему удивлению, не смог найти ресурсов с описанием протокола, после которых у меня не осталось бы только больше вопросов. Поэтому, после прочтения спецификаций и разборок с OpenSSH, хочу разложить всё по полочкам здесь.
Статья рассчитана на тех, кто поверхностно знаком с SSH, возможно, использовали на практике, но не осознали его сакральных смыслов и глубоких тайн. Попытаюсь описать основные аспекты безопасности протокола: какие ключи и алгоритмы используются, в какой момент и зачем. Статья призвана дать базу в работе с самим протоколом SSH, зная которую можно разбираться в его более продвинутых возможностях. Также будут замечания, как некоторые компоненты протокола претворяются в жизнь в OpenSSH на линуксе (конкретной программе, наверное, самой популярной, реализующей протокол SSH).
Что нужно знать
Единственное требование: знать об (а)симметричном шифровании и цифровой подписи. Что такое криптографический ключ, чем открытый отличается от закрытого, зачем они нужны и что умеют — на эти и связанные вопросы здесь ответа нет. Кому надо, тот загуглит про (а)симметричное шифрование, об этом есть множество доступных ресурсов.
Терминология
- Пользователь — системный пользователь, то есть который создаётся на линуксе через adduser или на винде в настройках.
- Клиент — программа, реализующая клиентскую часть протокола SSH: отправляющая запросы к SSH-серверу, пишущая вам в консоль гневные тирады из-за смены ключей, и прочее. В дистрибутивах линукса часто предустановлен клиент OpenSSH, который запускается командой ssh .
- Сервер — соответственно программа, реализующая серверную часть SSH. Не менее часто в дистрибутивах линукса предустановлен и сервер OpenSSH, выполненный в виде демона sshd . О запуске и настройке этого демона речи не пойдёт, как это делается, читателю предлагается найти на других ресурсах.
- Стороны — клиент и сервер.
- Вы — вы, то есть человек, использующий клиент.
- Админ(истратор) — человек, ответственный за сервер. Возможно, совпадает с «вами». Также это может быть некоторая автоматическая система, например, если вы используете облачные сервисы. В этом случае взаимодействие с сервером происходит через панель управления облаком.
Что такое SSH?
SSH — это сетевой протокол для защищённого управления удалёнными устройствами по незащищённому сетевому соединению. Самый примечательный сценарий использования: удалённый вход в систему и выполнение команд.
Это вольный перевод первого абзаца статьи про SSH на английской википедии. Думаю, каждый, кто открывал хоть одну ссылку про SSH в гугле, знает об этом.
Чуть менее тривиально (написано во втором абзаце) то, что на самом деле SSH состоит из трёх более или менее независимых протоколов: протокол транспортного уровня, протокол аутентификации, и протокол соединения. Возможно, называть их прямо отдельными протоколами — слишком громкие слова, и можно было бы считать, что это просто три стадии работы протокола. Но так их обзывают в спецификации, а чем я хуже непонятно. Кстати, спецификация у каждого из них своя: RFC 4253, RFC 4252 и RFC 4254 соответственно, за подробностями можно и нужно обращаться к этим ссылкам.
Обычно на практике разделение на три протокола нечасто упоминается, но, чтобы понять, когда и зачем какой ключ используется, это довольно важно. Поэтому далее каждый из этих протоколов разберём отдельно.
Акт 1. Протокол транспортного уровня
Сцена 1. Установка защищённого соединения
Первый этап в работе SSH — наладить защищённый канал связи. Внимание, спойлер: позднее, например если используется аутентификация по паролю, мы будем отправлять по сети пароль голым текстом (а как иначе сервер сможет сравнить пароль с хешем, который хранит у себя?), либо не пароль так ещё кучу всякой конфиденциальной информации. Поэтому нужно осуществить некоторые танцы с бубном, чтобы кто попало эти данные не прочитал. Это, по-моему, самый сложный из трёх протоколов, так как содержит множество хитрых фикусов и рассуждений о безопасности.
Итак, первое, что делают стороны после того, как между устройствами установлено сетевое соединение (а SSH обычно работает поверх TCP, так что обычно после того, как налажено соединение TCP) — отправляют друг другу версию своего ПО и самого протокола SSH, которую хотят использовать (на момент написания статьи актуальна версия 2.0). Далее они обмениваются списками поддерживаемых алгоритмов шифрования (и не только).
Разработчики SSH предусмотрели, что, с одной стороны, алгоритмы шифрования, как и всё в жизни, со временем выходят из моды (точнее в них находят уязвимости), а с другой, не все разработчики клиентов/серверов SSH способны поддерживать все существующие алгоритмы. Поэтому и нужен шаг, на котором стороны договариваются об используемых алгоритмах. Сколько же алгоритмов и для каких конкретных целей нам понадобится? Не один и даже не два.
Первый алгоритм — алгоритм сжатия. Самый скучный, им просто сжимается каждое отправленное сообщение ради экономии трафика.
Во-первых, алгоритмов сжатия может быть выбрано два: один для сообщений от клиента к серверу, второй от сервера к клиенту. Конкретно для сжатия возможность выбрать два алгоритма не особо важна, всё равно почти всегда используется gzip в обе стороны. Но такая же возможность есть для некоторых других алгоритмов, для которых она может быть более актуальной (далее указано, для каких).
Во-вторых, на самом деле стороны договариваются одновременно обо всех алгоритмах, а «первым» я его назвал в целях красоты повествования. Вообще всё, что описано далее про протокол транспортного уровня, происходит всего за 3-4 обмена сообщениями между клиентом и сервером (то бишь всего за 6-8 отправленных сообщений). Так что по длине текста статьи не стоит делать вывод, что все проверки и обмены ключами и алгоритмами занимают большое количество шагов, это не так. В разработке протокола старались минимизировать количество необходимых сетевых взаимодействий.
В SSH слова «соединение защищено», помимо прочего, значат, что все отправляемые сообщения подвергаются симметричному шифрованию. То есть и у сервера, и у клиента есть (обычно) одинаковый ключ симметричного шифрования, который может и зашифровать, и расшифровать любое сообщение. Симметричные шифры бывают разные, поэтому второй алгоритм, о котором договариваются стороны: алгоритм симметричного шифрования (encryption algorithm).
Как и со сжатием, алгоритмов симметричного шифрования, может быть выбрано два: для сообщений от сервера к клиенту один, а от клиента к серверу другой. Спецификация рекомендует использовать одинаковый в обе стороны, и чаще всего так и происходит, поэтому я не буду зацикливаться. Но вообще говоря надо иметь это в виду: алгоритмов симметричного шифрования (а, следовательно, и ключей симметричного шифрования) две штуки на соединение.
Вопрос: как так сделать, чтобы клиент и сервер, изначально общаясь по незащищённому соединению, смогли договориться о ключе симметричного шифрования, и при этом никто кроме клиента и сервера не узнал этот ключ? Ответ: использовать хитроумный алгоритм обмена ключами (англ. key exchange algorithm, он же kex algorithm). Это третий алгоритм, о котором договариваются стороны.
Конкретные алгоритмы обмена ключами — это вариации на тему алгоритма Диффи-Хеллмана. На английской википедии есть очень наглядная картинка (приведена ниже), где в роли ключей выступают цвета. Сначала стороны договариваются о некотором открытом ключе (обычно клиент его генерирует и отправляет серверу по незащищённому соединению, на картинке общим ключом является жёлтый цвет). Затем каждая сторона создаёт свой закрытый ключ (на картинке цвет заката и морской волны в моей интерпретации ). Он каким-то образом объединяется с открытым ключом (в примере с цветами — смешивается, а вообще, то, как ключи объединяются, и как в принципе генерируются, зависит от конкретного выбранного алгоритма обмена ключами), и результат отправляется другой стороне.
Затем каждая сторона объединяет уже полученный чужой результат со своим закрытым ключом, и в итоге, благодаря магии математики, оказывается, что у обеих сторон вышло одно и то же — общий секретный ключ. Далее я не буду выделять его как один из используемых ключей, потому что это всего лишь промежуточное значение, которое используется для генерации ключей, делающих более осмысленные вещи. Тут вопрос терминологии: в английском этот секретный ключ даже ключом не называется, там это просто «shared secret».
Это одно и то же можно использовать, чтобы сгенерировать ключ симметричного шифрования. Он считается как хеш секретного ключа, конкатенированного с некоторыми другими данными, также известными и клиенту, и серверу. Сама функция хеширования привязана к выбранному алгоритму обмена ключами, так что у обеих сторон она одинаковая.

Однако только симметричного шифрования недостаточно для защищённого соединения. Нужен ещё уже четвёртый алгоритм имитовставки (mac algorithm), которому нужен ключ имитовставки (integrity key). Он настолько же важный и базовый, как алгоритм симметричного шифрования, и введение имитовставки, так же, как и шифрование, обычно происходит под капотом у программ-реализаций, а рядовой разработчик о них не задумывается. Но если, говоря о шифровании, я могу рассчитывать, что большинство знакомы с его принципами, то имитовставка менее общеизвестна. Поэтому позволю себе вынести подробности о ней в спойлер для любопытного читателя.
Почему же шифра недостаточно? Шифр защищает от возможности прочитать содержимое сообщения, но не защищает от того, чтобы изменить его. Да, злоумышленник не знает ключ шифрования, так что скорее всего не сможет подменить сообщение на что-либо осмысленное, то есть, например, не сможет заставить сервер выполнить конкретную команду (и то не факт, если очень умный, может и сможет). Но он точно может намусорить: поменять какой-нибудь байт, и на сервер придёт мусорное сообщение, а сервер подумает, что клиент это и имел в виду. Последствия у такого тоже могут быть самые разные, в лучшем случае просто команда клиента не выполнится, и клиенту вернётся сообщение с ошибкой.
Но SSH же работает поверх TCP, который гарантирует целостность данных? Он включает контрольную сумму, которая не позволит просто так изменить сообщение? Не совсем. TCP защищает от случайных изменений пакета. Злоумышленник же, поменяв содержимое сообщения, может запросто пересчитать контрольную сумму, и подменить и её: ведь шифруются только данные, на уровне протокола SSH, а данные всех протоколов, поверх которых SSH работает, не шифруются.
Поэтому для предотвращения таких вмешательств в ход истории существует имитовставка (англ. MAC — message authentication code). Это набор байтов, который генерируется из содержимого сообщения и ключа имитовставки (integrity key), и дописывается к концу сообщения. Используя этот ключ и содержимое сообщения, каждая сторона может проверить, что оно дошло без изменений. Ключ хранится в секрете и (обычно) одинаковый у сервера и клиента, всё как с симметричным шифрованием. Таким образом, если злоумышленник подменит какие-то байты, сервер заметит подвих и не станет обрабатывать изменённое сообщение.
Сам ключ имитовставки генерируется аналогично ключу симметричного шифрования — как хеш секретного ключа, полученного из алгоритма обмена ключами, и некоторой информации, известной обеим сторонам. Только эта дополнительная информация разная для генерации ключа имитовставки и симметричного шифрования, поэтому и ключи получаются разные. От каких именно данных считается хеш для всех ключей, написано в RFC.
Алгоритмов имитовставки (а, следовательно, и ключей имитовставки) также может быть два на соединение: один от клиента к серверу, другой от сервера к клиенту.
Этих трёх алгоритмов достаточно, чтобы наладить защищённое соединение: стороны перед отправлением шифруют каждое сообщение, при получении дешифруют, а имитовставка гарантирует, что сообщение дошло в неизменном виде.
Но это не конец. На этапе обмена ключами происходит ещё одно важное действие: клиент, так сказать, проверяет сервер на вшивость. Привычно думать, что в аутентификации нуждается клиент, то есть клиент должен подтвердить свою личность, чтобы злоумышленник не проник внутрь сервера, и не натворил делов. О предотвращении делов речь пойдёт позже. Но в SSH аутентификация сервера не менее важна, ведь клиент (опять спойлер) будет отправлять ему, например, свои пароли. А все описанные ранее шаги не гарантируют, что сервер не подставной: с таким же успехом клиент мог произвести обмен ключами со злоумышленником.
Чтобы бороться с недобросовестностью сервера, используется очередной алгоритм, о котором стороны также договариваются перед обменом ключами — алгоритм цифровой подписи сервера. Это не официальное название, это я так обозвал в попытках отразить суть, в спецификации зовётся «server host key algorithm».
Сцена 2. Идентификация сервера
Пока что все описанные действия — базовые меры безопасности, которые происходят за кулисами, и с которыми редко приходится иметь дело, если вообще приходится. Теперь же речь пойдёт о вещах более приземлённых, и знание о которых важно при работе с протоколом.
У сервера изначально есть пара ключей: закрытый и открытый, они называются ключами сервера (host keys). Только на самом деле такая пара не одна: у сервера есть по паре ключей на каждый алгоритм цифровой подписи, который он поддерживает (ведь разные алгоритмы требуют разного вида ключей, вообще говоря). У сервера OpenSSH на линуксе по умолчанию ключи сервера хранятся в папке /etc/ssh в файлах с названиями ssh_host_*_key (закрытый ключ) и ssh_host_*_key.pub (соответствующий открытый ключ), вместо звёздочки пишется название алгоритма, например rsa или ecdsa . Эти ключи перед запуском SSH-сервера кладёт туда администратор: либо генерирует с нуля (например, с помощью утилиты ssh-keygen -A ), либо устанавливает из каких-то других соображений, мало ли в кармане завалялись.
Во время обмена ключами, сервер подписывает некоторую строку своим закрытым ключом (на этот момент они с клиентом уже договорились, какой алгоритм цифровой подписи использовать, поэтому ключ выбираются соответствующий этому алгоритму), и отправляет результат клиенту впридачу с открытым ключом. Эта строка составляется из некоторого набора данных, которые обеим сторонам на этот момент уже известны, вроде названия используемого протокола, версии ПО и т.п. (конкретно из каких описано в RFC), поэтому клиент может составить точно такую же строку и с помощью открытого ключа проверить достоверность подписи (возможность составить такую же строку нужна, потому что для проверки электронной подписи надо знать содержимое подписанного сообщения. Можно было бы отправлять подписанную строку клиенту вместе с ключом и подписью, но, вероятно, так решили не делать ради экономии трафика). Если подпись верна, значит сервер правда владеет закрытым ключом, соответствующим заявленному открытому.
Однако это ещё ничего не значит. С таким же успехом сервер злоумышленника мог бы сгенерировать свою пару открытый/закрытый ключ, отправить клиенту открытый, и подписать строку закрытым. Да, клиент убедится, что сервер действительно знает закрытый ключ, который соответствует отправленному открытому, но это не значит, что сервер не подставной.
Как убедиться, что сервер настоящий — это вопрос менее тривиальный, чем применить цифровую подпись. Есть разные варианты, одни более безопасные, другие менее. Чаще и проще всего это делается так: клиент хранит у себя небольшую базу данных (обычно просто в виде файла в определённом формате), которые сопоставляют каждому серверу его открытый ключ. Например, клиент OpenSSH на линуксе смотрит в файл ~/.ssh/known_hosts .
Ситуация: мне нужно ssh-нуться к машинке по адресу example.com . Для этого (будучи ответственным и волнующимся о безопасности человеком) я пойду к администратору, и узнаю у него публичный ключ сервера, запущенного на этой машинке (соответствующий алгоритму, который буду использовать для проверки сервера). Затем добавлю в known_hosts запись о том, что публичный ключ сервера example.com такой-то (формат файла подскажет гугл). Затем при каждом подключении к example.com клиент будет проверять, что отправленный сервером открытый ключ совпадает с тем, что лежит в known_hosts . Если совпадает, клиент проверит, что сервер правда владеет соответствующим закрытым ключом как описано выше. Если владеет — значит сервер точно не подставной. Небольшой нюанс: так как в known_hosts ключ сопоставляется домену, то если я хочу подключаться к этой машинке не только по домену, но и по IP адресу, этому IP адресу тоже нужно отдельной строчкой сопоставить тот же ключ.
Однако вряд ли, если вы ранее пользовались SSH, при каждом подключении к новому серверу ходили просить админа отправить ключ. Обычно при первом подключении к новому серверу клиенты SSH показывают сообщение такого характера:
The authenticity of host '. ' can't be established. ED25519 key fingerprint is SHA256:noP2S4gaQmKTIO8cHHg4ju3QptLSo6MEgCw2AiLwOJM. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])?
Таким образом клиент говорит, что не знает, можно ли доверять этому серверу, и оставляет это на ваше усмотрение. Как можно действовать:
- Набрать no и отменить подключение.
- Набрать yes : тогда клиент автоматически добавит полученный открытый ключ сервера в known_hosts , сопоставив его домену, к которому пытались подключиться (обычно добавляется и сопоставление к IP адресу, соответствующему на данный момент этому домену). Перед тем, как бездумно набирать yes , можно обратить внимание, что выше выведен хеш открытого ключа. Его можно сравнить с ожидаемым хешем ключа (например, опять же, спросить у админа, какой должен быть хеш, или, если подключаетесь к машине в облаке, в панели управления облака он может быть указан).
- Также можно ввести отпечаток (fingerprint) — это, грубо говоря, тоже хеш публичного ключа, который можно спросить у админа или скопировать из авторитетного источника, тогда клиент сам проверит, что отпечаток ключа, отправленного сервером, совпадает со введённым вами, и, если это так, добавит его в known_hosts .
Стоит отметить, что так как ключ сервера используется клиентом для идентификации, если он в какой-то момент изменится (например, если на сервере переустановили SSH или операционку, либо может перегенерировали ключи, из-за того, что закрытый оказался слит, а, может, просто в целях профилактики), то сервер не получится идентифицировать. С точки зрения клиента смена ключа неотличима от попытки злоумышленника подсунуть липовый ключ. Поэтому в таких случаях клиент при подключении на вас наорёт, например, как орёт OpenSSH:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the RSA key sent by the remote host is 6e:45:f9:a8:af:38:3d:a1:a5:c7:76:1d:02:f8:77:00. Please contact your system administrator. Add correct host key in /home/hostname /.ssh/known_hosts to get rid of this message.
Собственно русским по белому написано: возможно, вас пытаются злоумыслить, а, возможно, просто ключ поменялся. Узнать это можно только обратившись к администратору. Есть способы, как организовать регулярную смену ключей, но я недостаточно квалифицирован, чтобы их описывать. Любопытный читатель загуглит «ssh key rotation».
Хранить публичный ключ вкупе с доменом — не единственный способ идентификации сервера, ещё можно, например, использовать сертификаты: тогда сервер перед обменом ключами отправит свой сертификат, который клиент сможет проверить на достоверность. Но о них, возможно, в другой раз.
Итого
После всех проделанных действий мы имеем: возможность защищённым образом передавать пакеты по незащищённой сети, и уверенность в том, что сервер не подставной. Для этого у нас есть
- Ключ сервера: закрытый хранится только на сервере, открытый стороны используют в начале соединения для проверки личности сервера, и после этого больше не используют.
- Ключ симметричного шифрования и ключ имитовставки: есть у обеих сторон, хранятся только в памяти и генерируются заново при каждом подключении (и, вообще говоря, могут меняться в течение соединения. Для этого существует процедура повторного обмена ключами — key re-exchange — но это детали). Нужны для работы защищённого соединения. Разработчик обычно с ними дела не имеет, работа с ними происходит под капотом.
Обращу внимание, что пока что речь ни о каком ключе клиента не шла. Тот самый id_rsa.pub , который вечно нужно генерировать и кому-то отправлять, на текущий момент ещё не использовался.
Акт 2. Протокол аутентификации
До этого сервер распинался перед клиентом, как бы подтвердить свою личность, пора бы и клиенту что-нибудь подтвердить. Всю описанную выше процедуру вполне мог провернуть злоумышленник, пытающийся получить доступ к серверу, поэтому теперь сервер должен проверить клиента.
Простейший способ аутентифицировать клиента: не аутентифицировать клиента. Да, можно настроить сервер для некоторых пользователей так, что как только соединение установлено, считается, что клиент молодец, и сразу может от имени этих пользователей выполнять команды/делать что там SSH ещё умеет делать. Под каким пользователем нужно подключиться, вы выбираете сами при подключении (пишете ssh myuser@example.com ), если для него авторизация выключена, то и ни паролей, ни ключей не понадобится. Естественно, это дико небезопасно, но в некоторых случаях можно оправдать такую схему.
Но мы не будем её оправдывать. У нас же теперь есть защищённое соединение, по которому можно передавать что угодно без страха, что кто-то левый прочитает. Поэтому чуть менее простейший способ аутентификации клиента: по паролю. Клиент просто отправляет имя пользователя, под которым хочет зайти, и пароль. Сервер где-то у себя хранит хеш пароля для каждого пользователя, под которым можно зайти (на линуксе обычно пользователи ssh совпадают с системными пользователями, соответственно пароль совпадает с паролем системного пользователя). При попытке входа пароль сравнивается с хешем, вот и сказочке конец. Возможность входа по паролю не обязательна, сервер SSH может её не поддерживать.
А вот обязательна возможность входа ещё менее простейшим, но более безопасным способом — по ключу. Здесь уже вы сами создаёте пару ключей клиента (например, с помощью утилиты ssh-keygen , по умолчанию закрытый ключ она записывает в файл ~/.ssh/id_rsa , а открытый — в ~/.ssh/id_rsa.pub ), сообщаете администратору открытый ключ (при использовании ssh-keygen это id_rsa.pub ), тот добавляет его на сервер. Если запущен сервер OpenSSH на линуксе, то у каждого пользователя на сервере в домашней папке есть файл ~/.ssh/auhorized_keys : администратор добавляет ключ клиента в authorized_keys того пользователя, под которым клиент сможет входить. Можно добавить один ключ в authorized_keys нескольких пользователей, тогда вы сможете входить под всеми ними. Под каким конкретно вы пытаетесь войти — опять же, указываете при подключении, например ssh myuser@example.com .
Дальше всё как было при идентификации сервера: клиент составляет строку (из данных, которые сервер тоже знает, конкретная строка описана в спецификации), подписывает закрытым ключом и отправляет подпись серверу вместе с открытым ключом, а также именем пользователя, под которым хочет войти (допустим, myuser ). Сервер проверяет, что открытый ключ ему известен (OpenSSH на линуксе смотрит, что он указан в файле /home/myuser/.ssh/authorized_keys ), составляет такую же строку, и проверяет достоверность подписи. Если всё совпало — клиент идентифицирован, и можно принимать от него команды/файлы/что угодно.
Важно! Ни в коем случае нельзя никому сообщать свой закрытый ключ (по умолчанию ~/.ssh/id_rsa без расширения .pub ). Это всё равно, что рассказать свой пароль: сервер не может отличить вас от злоумышленника, имеющего ваш закрытый ключ.
Акт 3. Протокол соединения
На этом этапе, защищённое соединение установлено, клиент подтвердил личность сервера, а сервер — клиента. Протокол соединения работает уже на прикладном уровне: если SSH используется для запуска команд в терминале, он описывает, как передавать сами команды и настройки терминала, если для передачи файлов — процесс передачи файлов. Это наименее интересная для меня часть SSH, так что в подробности вдаваться не буду. Просто определяется какой-то набор сообщений: как запустить команду на удалённом устройстве, как сообщить серверу, что на клиенте изменились размеры окна терминала, и т.д. и т.п.
Резюме
При установке соединения в SSH используются следующие ключи:
- Ключ сервера — концептуально это ключ цифровой подписи, нужен для проверки личности сервера клиентом. Не используется для шифрования.
- Ключ клиента — концептуально это ключ цифровой подписи, нужен для проверки личности клиента сервером. Не используется для шифрования. Вообще говоря, необходим только если используется аутентификация по ключу. Если аутентификации нет, или она по паролю, или как-то ещё (SSH подразумевает возможную реализацию иных методов), этот ключ вообще не нужен.
- Ключ синхронного шифрования и ключ имитовставки — меняются при каждом подключении, могут меняться в течение сессии, используются для защиты соединения.
Если вы используете OpenSSH на линуксе, то вот список основных файлов, которые используются. На клиенте:
- ~/.ssh/id_rsa — закрытый ключ клиента, который никому нельзя сообщать.
- ~/.ssh/id_rsa.pub — открытый ключ клиента, который администратор добавляет на сервер, чтобы вы получили доступ.
- ~/.ssh/known_hosts — список, сопоставляющий доменам/IP адресам серверов их открытые ключи. Используется чтобы удостовериться, что сервер не подставной.
- ~/.ssh/config , /etc/ssh/ssh_config — в статье не упоминался, но это настройки клиента SSH, подробнее, что можно настроить, расскажет man ssh_config .
- /etc/ssh/ssh_host_*_key — закрытые ключи сервера, по ключу на каждый поддерживаемый алгоритм цифровой подписи.
- /etc/ssh/ssh_host_*_key.pub — открытые ключи сервера.
- /home/[user]/.ssh/authorized_keys — список открытых ключей клиентов, которым разрешено входить под пользователем [user] .
- /etc/ssh/sshd_config — настройки сервера, подробнее в man sshd_config .
Естественно, здесь опущены разные мелочи, не расписаны принципы ротации ключей, использования сертификатов, и прочее прочее. Но, надеюсь, статья будет полезной для начинающих рыбок в море сетевых протоколов.
- ssh
- ключи
- ключи шифрования
- криптография
- протоколы
- сетевые протоколы
- сетевые технологии*
4 Лекция. Контроль целостности данных. Хеш-функции. Имитовставка. ЭЦП
Целостности данных — при котором отсутствует любое ее изменение либо изменение осуществляется только преднамеренно субъектами, имеющими на него право.
Методы контроля целостности данных:
-
Полная копия данных
Контрольная сумма
Имитовставка
Полная копия данных.
Создаются полные копии данных и потом сверяются.
- простота реализации
- полный контроль данных (до бита)
- большой объем
- копии можно подменить
- копиями можно воспользоваться (например: если данные — пароль)

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

Рис. Контроль целостности с помощью контрольной суммы
Примеры контрольных сумм: CRC8, CRC16, CRC32
исходный текст: Контроль целостности данных
crc32 (длина 32 бита)
- контроль целостности файлов
- контроль передаваемых данных по каналам связи
- контроль целостности при считывании данных (например: c HDD)
Хеш.
Хеш (хэш, криптографический хеш) — значение, рассчитанное по входным данным с помощью криптографического алгоритма.
- малый размер
- стандартный размер
- нельзя подобрать исходные данные к значению за приемлемое время (например: получить пароль)
- низкая скорость вычисления (сопоставима с шифрованием)
- можно подменить
- для одного значения существует множество исходных данных

Рис. Основная задача хеш функций
Вычисляют хеш шифрованием данных блочным алгоритмом в режимах CBC, но со стандартным (известным) ключом. Хешем является последний шифрованный блок.
ГОСТ Р 34.11-94
Входное сообщение M разделяется на блоки mn,mn ? 1,mn ? 2. m1 по 256 бит. В случае если размер последнего блока mn меньше 256 бит, то к нему приписываются слева нули для достижения заданной длины блока.
Каждый блок сообщения подаётся на шаговую функцию для вычисления промежуточного значения хеш-функции Hout =f( Hin , mi ) где Hout , Hin , mi — блоки длины 256 бит.
Рис. Вычисление хеш по ГОСТ Р 34.11-94 (сравните с CBC)
h — значение хеш-функции сообщения M
Len(M) — длина сообщения
Ключи для f-функции генерятся стандартным образом, что бы все пользователи могли вычислить одинаковые хеш для одних и тех же файлов.
исходный текст: Контроль целостности данных
md2 (длина 128 бит)
md4 (длина 128 бит)
md5 (длина 128 бит)
sha1 (длина 160 бит)
sha224 (длина 224 бит)
sha256 (длина 256 бит)
sha384 (длина 384 бит)
sha512 (длина 512 бит)
ГОСТ Р 34.11-94 (длина 256 бит)
- контроль целостности файлов
- контроль передаваемых данных по каналам связи
- контроль целостности при считывании данных (например: c HDD)
- хеши паролей
- аутентификация (CRAM-MD5, DIGEST-MD5 и т.д.)
Имитовставка (MAC, message authentication code — код аутентичности сообщения)
Имитовставка — значение, рассчитанное по входным данным с помощью криптографического алгоритма с использованием секретного элемента (ключа), известного только отправителю и получателю.
- малый размер
- стандартный размер
- нельзя подобрать исходные данные к значению за приемлемое время (например: получить пароль)
- нельзя подменить без секретного элемента (ключа)
- низкая скорость вычисления (сопоставима с шифрованием)
- для одного значения существует множество исходных данных
- секретный ключ известен как минимум двоим
Вычисляют имитовставку шифрованием данных блочным алгоритмом в режимах CBC. Имитовставкой является последний шифрованный блок.
Рис. Вычисление имитовставки
Имитовставка по ГОСТ 28147-89
Длина имитовставки от 1 до 32 бит.
Открытый текст TO разбивается на блоки длиной 64 бита. Последний блок в случае необходимости дополняется нулями.

Первые 32 бита получившегося блока составляют имитовставку. Спецификация шифра предусматривает использование в качестве имитовставки и меньшее количество бит по желанию, но не большее.

Рис. Проблема имитовставки
Получатель должен знать ключ, и этот ключ позволяет ему генерировать сообщения с тем же значением имитовставки, что и у присланного сообщения, таким образом, имитовставка на основе симметричного шифра не дает знания — отправитель или получатель сформировал эту имитовставку.
Отсюда следует, что имитовставка на основе симметричного шифра не может заменять собой электронную подпись!
- контроль целостности файлов
- контроль передаваемых данных по каналам связи
- аутентификации источника данных (не во всех случаях)
Обычные хэш-алгоритмы использовать для вычисления имитовставки нельзя (MD5 и т.д.) т.к. отсутствует секретный ключ. Поэтому создан HMAC .
HMAC (H ash-based Message Authentication Code ) — механизм включения секретного ключа в существующие хэш-алгоритмы.
ЭЦП.
Электронная цифровая подпись — зашифрованное значение вычисленного хеша по входным данным.
- малый размер
- стандартный размер
- нельзя подобрать исходные данные к значению за приемлемое время (например: получить пароль)
- нельзя подменить без секретного элемента (ключа)
- секретный ключ известен одному
- низкая скорость вычисления (сопоставима с шифрованием)
- для одного значения существует множество исходных данных

Рис. Создание и проверка ЭЦП
- вычисляется хеш
- шифруется хеш
- контроль целостности файлов
- контроль передаваемых данных по каналам связи
- аутентификации источника данных (кто создал подпись)
Как посчитать имитовставку mac функциями openssl
Модуль pgcrypto предоставляет криптографические функции для Postgres Pro .
Данный модуль считается « доверенным » , то есть его могут устанавливать обычные пользователи, имеющие право CREATE в текущей базе данных.
F.34.1. Стандартные функции хеширования
F.34.1.1. digest()
digest(data text, type text) returns bytea digest(data bytea, type text) returns bytea
Вычисляет двоичный хеш данных ( data ). Параметр type выбирает используемый алгоритм. Поддерживаются стандартные алгоритмы: md5 , sha1 , sha224 , sha256 , sha384 и sha512 . Если модуль pgcrypto собирался с OpenSSL , становятся доступны и другие алгоритмы, как описано в Таблице F.24.
Если вы хотите получить дайджест в виде шестнадцатеричной строки, примените encode() к результату. Например:
CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$ SELECT encode(digest($1, 'sha1'), 'hex') $$ LANGUAGE SQL STRICT IMMUTABLE;
F.34.1.2. hmac()
hmac(data text, key text, type text) returns bytea hmac(data bytea, key bytea, type text) returns bytea
Вычисляет имитовставку на основе хеша для данных data с ключом key . Параметр type имеет то же значение, что и для digest() .
Эта функция похожа на digest() , но вычислить хеш с ней можно, только зная ключ. Это защищает от сценария подмены данных и хеша вместе с ними.
Если размер ключа больше размера блока хеша, он сначала хешируется, а затем используется в качестве ключа хеширования данных.
F.34.2. Функции хеширования пароля
Функции crypt() и gen_salt() разработаны специально для хеширования паролей. Функция crypt() выполняет хеширование, а gen_salt() подготавливает параметры алгоритма для неё.
Алгоритмы в crypt() отличаются от обычных алгоритмов хеширования MD5 и SHA1 в следующих аспектах:
Они медленные. Так как объём данных невелик, это единственный способ усложнить перебор паролей.
Они используют случайное значение, называемое солью, чтобы у пользователей с одинаковыми паролями зашифрованные пароли оказывались разными. Это также обеспечивает дополнительную защиту от получения обратного алгоритма.
Они включают в результат тип алгоритма, что допускает сосуществование паролей, хешированных разными алгоритмами.
В Таблице F.21 перечислены алгоритмы, поддерживаемые функцией crypt() .
Таблица F.21. Алгоритмы, которые поддерживает crypt()
| Алгоритм | Макс. длина пароля | Адаптивный? | Размер соли (бит) | Размер результата | Описание |
|---|---|---|---|---|---|
| bf | 72 | да | 128 | 60 | На базе Blowfish, вариация 2a |
| md5 | без ограничений | нет | 48 | 34 | crypt на базе MD5 |
| xdes | 8 | да | 24 | 20 | Расширенный DES |
| des | 8 | нет | 12 | 13 | Изначальный crypt из UNIX |
F.34.2.1. crypt()
crypt(password text, salt text) returns text
Вычисляет хеш пароля ( password ) в стиле crypt(3). Для сохранения нового пароля необходимо вызвать gen_salt() , чтобы сгенерировать новое значение соли ( salt ). Для проверки пароля нужно передать сохранённое значение хеша в параметре salt и проверить, соответствует ли результат сохранённому значению.
Пример установки нового пароля:
UPDATE . SET pswhash = crypt('new password', gen_salt('md5'));
Пример проверки пароля:
SELECT (pswhash = crypt('entered password', pswhash)) AS pswmatch FROM . ;
Этот запрос возвращает true , если введённый пароль правильный.
F.34.2.2. gen_salt()
gen_salt(type text [, iter_count integer ]) returns text
Вычисляет новое случайное значение соли для функции crypt() . Строка соли также говорит crypt() , какой алгоритм использовать.
Параметр type задаёт алгоритм хеширования. Принимаются следующие варианты: des , xdes , md5 и bf .
Параметр iter_count позволяет пользователю указать счётчик итераций для алгоритма, который его принимает. Чем больше это число, тем больше времени уйдёт на вычисление хеша пароля, а значит, тем больше времени понадобится, чтобы взломать его. Хотя со слишком большим значением время вычисления хеша может вырасти до нескольких лет — это вряд ли практично. Когда параметр iter_count опускается, применяется количество итераций по умолчанию. Множество допустимых значений для iter_count зависит от алгоритма, как показано в Таблице F.22.
Таблица F.22. Счётчики итераций для crypt()
| Алгоритм | По умолчанию | Мин. | Макс. |
|---|---|---|---|
| xdes | 725 | 1 | 16777215 |
| bf | 6 | 4 | 31 |
Для xdes есть дополнительное ограничение: счётчик итераций должен быть нечётным.
При выборе подходящего числа итераций учтите, что оригинальный алгоритм DES crypt был рассчитан так, чтобы выдавать 4 хеша в секунду на компьютерах того времени. Если за секунду будет вычисляться меньше 4 хешей, скорее всего, возникнут определённые неудобства при пользовании. С другой стороны, скорость больше, чем 100 хешей в секунду, вероятно, будет слишком высокой.
В Таблице F.23 дана сводка относительной скорости различных алгоритмов хеширования. В таблице показано, сколько времени уйдёт на перебор всех комбинацией символов в восьмисимвольном пароле, в предположении, что пароль содержит только буквы в нижнем регистре, либо буквы в верхнем и нижнем регистре, а также цифры. В строках crypt-bf числа после косой черты показывают значение параметра iter_count функции gen_salt .
Таблица F.23. Скорости алгоритмов хеширования
| Алгоритм | Хешей/сек. | Для [a-z] | Для [A-Za-z0-9] | Длительность относительно md5 |
|---|---|---|---|---|
| crypt-bf/8 | 1792 | 4 года | 3927 лет | 100k |
| crypt-bf/7 | 3648 | 2 года | 1929 лет | 50k |
| crypt-bf/6 | 7168 | 1 год | 982 лет | 25k |
| crypt-bf/5 | 13504 | 188 дней | 521 лет | 12.5k |
| crypt-md5 | 171584 | 15 дней | 41 год | 1k |
| crypt-des | 23221568 | 157.5 минут | 108 дней | 7 |
| sha1 | 37774272 | 90 минут | 68 дней | 4 |
| md5 (хеш) | 150085504 | 22.5 минут | 17 дней | 1 |
Для расчётов использовался процессор Intel Mobile Core i3.
Показатели алгоритмов crypt-des и crypt-md5 взяты из вывода теста программы John the Ripper v1.6.38.
Показатели md5 получены программой mdcrack 1.2.
Показатели sha1 получены программой lcrack-20031130-beta.
Заметьте, что вариант « перепробовать все комбинации » не вполне реалистичен. Обычно перебор паролей производится с применением словарей, которые содержат и обычные слова, и их различные видоизменения. Поэтому даже похожие на слова пароли обычно можно подобрать быстрее, чем за указанное время, тогда как 6-символьный несловесный пароль может избежать взлома. А может и не избежать.
F.34.3. Функции шифрования на базе PGP
Функции, описанные здесь, реализуют часть стандарта OpenPGP (RFC 4880), относящуюся к шифрованию. Они поддерживают шифрование как с симметричным, так и с закрытым ключом.
Зашифрованное PGP сообщение состоит из 2 частей или пакетов:
Пакет, содержащий сеансовый ключ — либо симметричный, либо открытый (в зашифрованном виде).
При шифровании с симметричным ключом (то есть, паролем):
Заданный пароль хешируется по алгоритму String2Key (S2K). Этот алгоритм подобен алгоритмам crypt() — специально замедлен и добавляет случайную соль — но на выход выдаёт двоичный ключ полной длины.
Если требуется отдельный сеансовый ключ, генерируется новый случайный ключ. В противном случае в качестве сеансового будет использоваться непосредственно ключ S2K.
При шифровании с открытым ключом:
Генерируется новый случайный сеансовый ключ.
В любом случае данные, которые должны быть зашифрованы, обрабатываются так:
Необязательная подготовка данных: сжатие, перекодировка в UTF-8 и/или преобразование концов строк.
Перед данными добавляется блок случайных байт. Это равносильно использованию случайного вектора инициализации.
В конце добавляется хеш SHA1 случайного префикса и данных.
F.34.3.1. pgp_sym_encrypt()
pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea
Шифрует данные ( data ) симметричным ключом PGP psw . В options могут передаваться криптографические параметры, описанные ниже.
F.34.3.2. pgp_sym_decrypt()
pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea
Расшифровывает сообщение, зашифрованное симметричным ключом PGP.
Расшифровывать данные bytea функцией pgp_sym_decrypt запрещено. Это ограничение введено, чтобы не допустить вывода некорректных символьных данных. Расшифровывать изначально текстовые данные с помощью pgp_sym_decrypt_bytea можно без ограничений.
Аргумент options может содержать криптографические параметры, описанные ниже.
F.34.3.3. pgp_pub_encrypt()
pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea
Зашифровывает данные ( data ) открытым ключом PGP ( key ). Если передать этой функции закрытый ключ, она выдаст ошибку.
Аргумент options может содержать криптографические параметры, описанные ниже.
F.34.3.4. pgp_pub_decrypt()
pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns text pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) returns bytea
Расшифровывает сообщение, зашифрованное открытым ключом. В key должен передаваться закрытый ключ, соответствующий открытому ключу, применяющемуся при шифровании. Если секретный ключ защищён паролем, этот пароль нужно передать в параметре psw . Если пароля нет, но необходимо передать криптографические параметры, вы должны передать пустой пароль.
Расшифровывать данные bytea функцией pgp_pub_decrypt запрещено. Это ограничение введено, чтобы не допустить вывода недопустимых символьных данных. Расшифровывать изначально текстовые данные с помощью pgp_pub_decrypt_bytea можно без ограничений.
Аргумент options может содержать криптографические параметры, описанные ниже.
F.34.3.5. pgp_key_id()
pgp_key_id(bytea) returns text
pgp_key_id извлекает идентификатор ключа из открытого или закрытого ключа PGP. Она также может выдать идентификатор ключа, которым были зашифрованы данные, если ей передаётся зашифрованное сообщение.
Она может выдать два специальных идентификатора ключа:
Сообщение зашифровано симметричным ключом.
Заметьте, что разные ключи могут иметь одинаковый идентификатор. Это редкое, но не невероятное явление. В таком случае клиентское приложение должно пытаться расшифровать данные с каждым ключом, пока не найдёт подходящий — примерно так же, как и с ANYKEY .
F.34.3.6. armor() , dearmor()
armor(data bytea [ , keys text[], values text[] ]) returns text dearmor(data text) returns bytea
Эти функции переводят двоичные данные в/из формата PGP «ASCII Armor», по сути представляющий собой кодировку Base64 с контрольными суммами и дополнительным форматированием.
Если задаются массивы keys и values , для каждой пары ключ/значения в формат Armor добавляется заголовок Armor. Оба массива должны быть одномерными и иметь одинаковую длину. Задаваемые ключи и значения могут содержать только символы ASCII.
F.34.3.7. pgp_armor_headers
pgp_armor_headers(data text, key out text, value out text) returns setof record
Функция pgp_armor_headers() извлекает заголовки Armor из параметра data . Она возвращает набор строк с двумя столбцами, key и value. Если в ключах или значениях оказываются символы не ASCII, они воспринимаются как UTF-8.
F.34.3.8. Параметры функций PGP
Имена параметров подобны принятым в GnuPG. Значение параметра должно задаваться после знака равно; друг от друга параметры отделяются запятыми. Например:
pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')
Все эти параметры, кроме convert-crlf , применяются только к функциям шифрования. Функции расшифровывания получают параметры из данных PGP.
Вероятно, самые интересные параметры — это compress-algo и unicode-mode . Остальные должны иметь достаточно адекватные значения по умолчанию.
F.34.3.8.1. cipher-algo
Выбирает алгоритм шифрования.
Значения: bf, aes128, aes192, aes256 (только OpenSSL: 3des , cast5 )
По умолчанию: aes128
Применим к: pgp_sym_encrypt, pgp_pub_encrypt
F.34.3.8.2. compress-algo
Выбирает алгоритм сжатия. Принимается, только если Postgres Pro собран с zlib.
Значения:
0 — без сжатия
1 — сжатие ZIP
2 — сжатие ZLIB (= ZIP плюс метаданные и CRC блоков)
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt
F.34.3.8.3. compress-level
Определяет уровень сжатия. Чем больше уровень, тем меньшего объёма результат, но длительнее процесс. Значение 0 отключает сжатие.
Значения: 0, 1-9
По умолчанию: 6
Применим к: pgp_sym_encrypt, pgp_pub_encrypt
F.34.3.8.4. convert-crlf
Определяет, преобразовывать ли \n в \r\n при шифровании и \r\n в \n при дешифровании. В RFC 4880 требуется, чтобы текстовые данные хранились с переводами строк в виде \r\n . Воспользуйтесь этим параметром, чтобы поведение полностью соответствовало RFC.
Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt
F.34.3.8.5. disable-mdc
Не защищать данные хешем SHA-1. Единственная разумная причина использовать этот параметр — добиться совместимости с древними программами PGP, вышедшими до того, как в RFC 4880 была предусмотрена защита пакетов с SHA-1. Все последние реализации с gnupg.org и pgp.com прекрасно поддерживают это.
Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt
F.34.3.8.6. sess-key
Использовать отдельный сеансовый ключ. Для шифрования с открытым ключом всегда используется отдельный сеансовый ключ; этот параметр предназначен для шифрования с симметричным ключом, которое по умолчанию использует непосредственно ключ S2K.
Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt
F.34.3.8.7. s2k-mode
Режим алгоритма S2K.
Значения:
0 — Без соли. Опасно!
1 — С солью, но с фиксированным числом итераций.
3 — С переменным числом итераций.
По умолчанию: 3
Применим к: pgp_sym_encrypt
F.34.3.8.8. s2k-count
Число итераций для алгоритма S2K. Оно должно быть не меньше 1024 и не больше 65011712.
По умолчанию: случайное значение между 65536 и 253952
Применим к: pgp_sym_encrypt, только с s2k-mode=3
F.34.3.8.9. s2k-digest-algo
Выбирает алгоритм хеширования, который будет использоваться для вычисления S2K.
Значения: md5, sha1
По умолчанию: sha1
Применим к: pgp_sym_encrypt
F.34.3.8.10. s2k-cipher-algo
Выбирает шифр, который будет использоваться для шифрования отдельного сеансового ключа.
Значения: bf, aes, aes128, aes192, aes256
По умолчанию: используется cipher-algo
Применим к: pgp_sym_encrypt
F.34.3.8.11. unicode-mode
Определяет, преобразовывать ли текстовые данные из внутренней кодировки базы данных в UTF-8 и обратно. Если кодировка базы уже UTF-8, перекодировка не производится, но сообщение помечается как UTF-8. Без данного параметра этого не происходит.
Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt
F.34.3.9. Формирование ключей PGP с применением GnuPG
Формирование нового ключа:
gpg --gen-key
Предпочитаемый тип ключей: « DSA and Elgamal » .
Для шифрования RSA вы должны создать главный ключ либо DSA, либо RSA только для подписания, а затем добавить подключ для шифрования, выполнив команду gpg —edit-key .
Просмотр списка ключей:
gpg --list-secret-keys
Экспорт открытого ключа в формате «ASCII Armor»:
gpg -a --export KEYID > public.key
Экспорт закрытого ключа в формате «ASCII Armor»:
gpg -a --export-secret-keys KEYID > secret.key
Прежде чем передавать эти ключи функциям PGP, вы должны применить функцию dearmor() к этим ключам. Либо, если вы можете обработать двоичные данные, уберите -a из команды.
Дополнительную информацию вы можете получить в руководстве man gpg , The GNU Privacy Handbook (Руководство GNU по обеспечению конфиденциальности) и другой документации на сайте https://www.gnupg.org/.
F.34.3.10. Ограничения кода PGP
Не поддерживается подписывание. Это также означает, что принадлежность подключа шифрования главному ключу не проверяется.
Не поддерживается использование ключа шифрования в качестве главного ключа. Так как подобная практика обычно не приветствуется, это не должно быть проблемой.
F.34.4. Низкоуровневые функции шифрования
Эти функции выполняют только шифрование данных; они не предоставляют расширенные возможности шифрования PGP. Таким образом, с ними связаны следующие проблемы:
Они используют ключ пользователя непосредственно в качестве ключа шифрования.
Они не обеспечивают проверку целостности, которая должна выявлять модификацию зашифрованных данных.
Они рассчитаны на то, что пользователи будут управлять всеми параметрами шифрования самостоятельно, даже вектором инициализации.
Поэтому с появлением поддержки шифрования PGP использовать низкоуровневые функции шифрования не рекомендуется.
encrypt(data bytea, key bytea, type text) returns bytea decrypt(data bytea, key bytea, type text) returns bytea encrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
Эти функции зашифровывают/расшифровывают данные, применяя метод шифрования, заданный параметром type . Строка type имеет следующий формат:
алгоритм[-режим] [/pad:дозаполнение]
где допустимый алгоритм :
cbc — следующий блок зависит от предыдущего (по умолчанию)
и допустимое дозаполнение :
pkcs — данные могут быть любой длины (по умолчанию)
Так что, например, эти вызовы равнозначны:
encrypt(data, 'fooz', 'bf') encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')
Для функций encrypt_iv и decrypt_iv параметр iv задаёт начальное значение для режима CBC; для ECB он игнорируется. Оно обрезается или дополняется нулями, если его размер не равен ровно размеру блока. В функциях без этого параметра оно по умолчанию заполняется нулями.
F.34.5. Функции получения случайных данных
gen_random_bytes(count integer) returns bytea
Возвращает криптографически стойкие случайные байты в количестве count . За один вызов можно получить максимум 1024 байт. Это ограничение предотвращает исчерпание пула энтропии.
gen_random_uuid() returns uuid
Возвращает UUID версии 4 (случайный). (Эта функция в данном модуле считается устаревшей, она вызывает одноимённую функцию ядра.)

